How do you compile static binaries on macOS?
You don’t. Thanks for reading.
In other news: on Linux you can easily compile static binaries because, as you might be already aware, most software on Linux is open source.
That’s not the case on macOS because there are some system libraries which are, obviously, closed source.
This means that you can’t create a “true” static binary.
But sometimes you don’t need (or want!) a completely portable static binary, because those system libraries are present in every single macOS installation and Apple, I have to say, has done an excellent job in keeping them compatible both forward and backwards.
For example MDFourier on Intel architecture is being built on macOS Mavericks 10.9 and it still works with the newest macOS Sonoma 14.6: that’s an 11 year gap!
This means that, yes, we can’t compile “real” static binaries, but we can statically link all the other libraries which are not macOS System Libraries, thus making our binary (almost) perfectly portable.
Unfortunately there’s not a clean way to do this properly and it’s mostly a hack for a simple reason: Dynamic Libraries, and the way the linker behaves.
Dynamic Libraries (easily recognizable because they come with the .dylib extension) are shared libraries much like DLLs on Windows systems and they can’t be statically linked to binaries.
macOS will *always* link to those, no matter what and, unfortunately, there’s no way to tell the compiler to prefer the static version of a certain library.
So how do we do this?
Yes, it’s unfortunate, but the only way to do this is to remove (or rename) the Dynamic Libraries.
Everytime you install a library it will be installed in both Dynamic Library (.dylib) and Static Library (.a) format.
If we take, for example, libogg installed by Homebrew, you’ll see that there is a libogg.dylib and a libogg.a
Since we’re building a static binary, we want to use libogg.a and the only way to force the linker to do this is to either delete or rename libogg.dylib to something like libogg.dylib.bak (I’m oldschool).
When the binary has been compiled, the linker will look for the Dynamic Library and, unable to find it, will opt for the static one, which is what we want.
Be always careful when compiling static binaries though.
This is still a hack so you must always check what libraries have been dynamically linked.
To do this, you can use the command line tool “otool” to check which libraries have been dynamically linked into your binary.
Taking a statically linked MDFourier binary, what we want to see is:
MechBook:MDFourier donluca$ otool -L mdfourier
mdfourier:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
As you can see, only two System Libraries are dynamically linked which are part of every macOS system, so we’re good to go.
Sometimes you might end up in a messy situation where there are some dependencies among static libraries which are not properly resolved and the linker will end up giving you a “Symbols not found” error (or something similar).
The only way to solve this is to use again “otool” and see what are that dynamic library’s dependencies and work your way up.
Here’s an example with libvorbis.dylib:
MechBook:lib donluca$ otool -L libvorbis.dylib
libvorbis.dylib:
/usr/local/opt/libvorbis/lib/libvorbis.0.dylib (compatibility version 5.0.0, current version 5.9.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.50.4)
/usr/local/opt/libogg/lib/libogg.0.dylib (compatibility version 9.0.0, current version 9.4.0)
As you can see, libvorbis depends on libogg so you always have to keep in mind that.
That’s pretty much it.
It’s unfortunate we have to resort to such trickery, but there seems to be no way out of this and I’ve been fighting with those kind of issues for a very long time.
If you’re unsure on how to set up the Makefile for making those “semi-static” binaries on macOS, please refer to the MDFourier Makefile as an example.
Thanks for reading!
1 thought on “Compiling static binaries on macOS”