On solaris, freebsd, and so on, the operating system ships the kernel and libc versions together, libc makes syscalls, and everyone else makes C ABI calls to libc.
The libc ABI is in fact the only interface the kernel, on solaris bsd etc, gives you to do something like "open a file" or "make a network call".
On those operating systems, the kernel syscalls may change between versions, so if you statically link libc, or if you make syscalls directly, your program may break when the OS version is upgraded. Every program is meant to use the libc ABI, which they won't break, and then internally the OS may refactor libc+the kernel syscall interface together without worrying about breaking anything.
macOS is similar to this.
Linux is unusual in that the libc and kernel implementation are maintained separately, and both keep their own stability promises, so on linux it is viable to make a pure go implementation which makes syscalls directly. Which is exactly what go has done, and why this static toolchain is possible.
Most programming languages don't bother with the linux syscall ABI because they'll have to use libc for other unixes as well (like solaris etc), so they might as well do it for linux too.
Example of small nuance that can ruin your day/week: the size of syscall(2) arguments is fixed, but for some syscalls the arguments must be smaller. Hence you have to pack and aline the values properly in the registers. This example is from the syscall(2) manpage.
I've recently been trying to fix some of them, but so far getting traction on their mailing list is a bit slow.
I have a pretty complex C++ command line tool which works just fine with MUSL and results in a distro-agnostic executable (https://github.com/floooh/sokol-tools). What potential problems should I be aware of?
For C++ we were faced with some issues, so the process we ended up with is:
- build musl, install it in some location
- inject a few GCC libs and linux headers required for C runtime to have the above location be a proper sysroot for clang to use
- build LLVM libc++ and a few libs (e.g libunwind) as static libs against that sysroot using clang, and inject them into the sysroot
- build whatever C++ final product we want against the sysroot using clang, statically linking libc++ in
- for a dynamic lib, remove " "needed" dynamic reference to libc.so in ELF. also, hide all symbols from libc++ and load with bind local so that when loaded the shared lib prefers its internal symbols (which would make it crash if it jumps to another libc++) and does not pollute the symbol namespace with its internal ones (which would make another lib crash if it jumps to the internal libc++)
- for an executable binary instead of a lib, dynamic references may instead need to be altered so that it works for both
It all hinges on musl being a subset of glibc, which is not entirely true either (see the musl website for differences in behaviour, which may or may not matter depending on the piece of software)
See:
https://wiki.musl-libc.org/functional-differences-from-glibc...
https://github.com/DataDog/libddwaf/blob/c6a90d39d93f04ebb5e...
AFAIK you can link libc++ against musl.
Not all OSes have libc per se, only those of UNIX/POSIX origin.
Secondly, on most UNIX environments, libc isn't the ISO C standard library, rather the full OS public API, static linking to it is possible (GNU libc being the exception due to how it is designed), however it limits what the binary would be able to call across OS versions if at all, e.g. the actual implementation semantics change.
Apple does not ship a statically linkable libSystem (I don’t think they ever have), and openbsd is trying to move away from it to tie up origin verification.
Also take into consideration all the other surviving commercial UNIXes, POSIX embedded OSes and POSIX environments for mainframes and micro-computers.
IIRC they couldn't even do it natively on MacOS due to system restrictions, unsure if that's still the case.
There is no "native" way to do this unless you consider libc (or libsystem) "native". There is no kernel interface to do dnd lookups just standard userspace tooling.
A different change [2] had a similar effect of using the system facilities for certificate validation in 1.18.
In general, using the custom stuff “works” (until it doesn’t!) but results in janky programs that don’t behave with respect to split tunnel DNS and so forth - very common with cross compiled programs until recently.