I've recently switched from Apple clang 9.1.0 to 12.0.0 and I've noticed that the generated code is now somewhat bloated. Here's a little test project:
const char *s = "Hello World";
__attribute__((visibility("default"))) int addfunc(int a, int b)
{
return a + b;
}
The build line looks like this:
gcc -mmacosx-version-min=10.13 -fPIC -dynamiclib -o mylib.dylib lib.c
strip -x mylib.dylib
With clang 9.1.0 mylib.dylib
is just 8320 bytes in size. With clang 12.0.0, however, the binary is suddenly 32904 bytes, i.e. almost 4 times as big as with clang 9.1.0. The binary content is mostly zeros so when zipping it, it can be compressed to less than 1kb but still I'd prefer it if clang created some more size-optimized binaries like it did in previous versions.
That's why I'd like to ask the question: Is there a way to make clang 12.0.0 produce smaller binaries like it did in 9.1.0 or are those bigger binaries full of zeros a "feature" now and people have to live with it?
FWIW, I've also tried adding -Os
to the build line but that didn't change anything. Presumably, because there is no real code to optimize in my example. It's just a single function that adds two values. But this doesn't stop clang from bloating this into a 32kb binary...
Here is the full version information on the compilers and target CPU I used:
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 9.1.0 (clang-902.0.39.2)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple clang version 12.0.0 (clang-1200.0.32.29)
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
To answer my own question: Apparently that newer clang aligns Mach-O segments on a 16384 bytes boundary whereas the old one used 4096 bytes. It's still possible to get the old alignment by using the -segalign
linker argument, i.e.
gcc test.c -Xlinker -segalign -Xlinker 0x1000
If I compile my project like this, the binary bloat is gone.
It only works on x64, though, because M1 macOS seems to require a 16k segment alignment so executables compiled using -segalign 0x1000
are automatically killed by the OS on M1.