Here read it from the lion's mouth:
https://wiki.musl-libc.org/design-concepts> Then, what is the exact reason a library compiled against glibc must be loaded by a specific ld-linux? I could see that this is true for C++ perhaps, or when you use very special features, but I do not see this for C.
You still have functionality like `dlopen` with C or `pthreads` with C. ld-linux.so is the thing that prepares the stack frame, or the segment pointers (FS_BASE on x86_64).
When you have your main loaded __libc_start_main_impl is called from glibc and if you disassemble it you'll see this line:
call 0x7ffff7c28790 <_dl_audit_preinit@plt>
Guess where _dl_audit_preinit lives?
(gdb) info symbol _dl_audit_preinit
_dl_audit_preinit in section .text of /lib64/ld-linux-x86-64.so.2
Moreover glibc has "magic" sections like .init_first. Only ld-linux.so that is compiled from glibc source knows how to handle that. https://elixir.bootlin.com/glibc/glibc-2.44.9000/source/csu/...
> I often compiled programs against one version of glibc and run it against a different version, so I know there is not a tight coupling. So please be specific in explaining in what scenarios this would break.
It didn't break, since glibc hasn't broken backwards compatibility of ld-linux.so and glibc combination lately. Last time they broke it was in 2024 for a really specific subset: https://sourceware.org/pipermail/libc-alpha/2024-December/16...
A breakage hasn't been observed doesn't mean that there is no tight-coupling between ld-linux.so and glibc that is compiled from glibc source.
If you want a really specific scenario:
- Write a very simple C file that contains a global (volatile if you want to stop the compiler optimizations) thread_local variable with C23 syntax
- Compile a simple .so file on a GNU distro targetting glibc ABI with GCC (pass -std=c23)
- start up a Musl distro (e.g. Alpine Docker image), copy that .so file in
- Write a C program that dlopens the GNU .so file
- Try accessing the thread_local variable you defined
- Watch the world burn