That means that you can have your application linked with library A version X, and load another library that is linked with library A version Y, and so long as certain practices are followed, everything works because you're not getting cross-contamination of symbols.
Meanwhile on Linux the defaults are quite different, and I can't load OpenGL ICD driver that depends on GLIBC_2.38 symbol on application that was loaded with glibc-2.37. Moreso, a lot of APIs will use malloc() and free() instead of language-independent allocator, unless the allocation comes from kernel. And no, you can't mix and match those.
What would you use instead of malloc and free?
Similarly other OSes used to have memory allocation services that weren't linked in any way to a language runtime - VMS has several calls, all language independent, ranging from low-level equivalents of mmap() to malloc-alternative.
And yes, a big chunk of Windows portability is that various APIs use those language-independent calls internally, and so can developer in order to avoid creating issues - and documentation promotes those language independent methods.
At no point you get into situation with Windows API that you call free() on memory allocated by malloc() from a different libc.
In comparison, the available language-independent APIs without using any special libraries on Linux, are to directly call mmap() and sbrk() through inline assembly (beware the glibc wrappers!).
And with how applications are linked by default on linux, whoever loads glibc first sets the stage for every other module in the program, which makes for "fun" errors when you get a library that happens to be compiled with newer version (or older). And even if you force windows-style linking (good luck, lots of infrastructure needed), you end up dealing with modules passing each other malloc()ed blocks and possibly trying to call free() from different allocator on it.
Anyway, for all practical purposes, glibc has no stable ABI at all.
Basically, I have my vulkan driver requiring fstat from 2.33 but the "pressure-vessel" (sniper version), has a glibc 2.31, then it does parse partially my global elf loading configuration and the configuration of some packages to import that driver and all its dependencies... including my glibc elf loader... ooof! That level of convolution will have a very high price all over the board.
I have arguments often with one of "pressure-vessel" devs because of some shortcuts they took which do break my distro (which is really basic and very vanilla, but not "ubuntu"). It took weeks to get the fixes in (I guess all of them are in... until the glibc devs manage to do something which will wreak havock on "pressure-vessel"). Oh... that makes me think I need to warn them about that super new and recent ELF relocation type they will have to parse and detect in host drivers...
On my side, I am investigating some excrutiatingly simple modern file format which should help a lot fighting those issues, there will be technical trade-offs obviously (but would run-ish on "old" kernels anyway with elf "capsules" and an orthogonal runtime). Like json is for xml, but for elf/pe.
It seems only the linux ABI can be trusted... til Linus T. is able to hold the line.
The ABI is not stable. Google has to do extra work monitoring for ABI breakages to make sure that pushing out an update of an LTS branch of the kernel does not break people's drivers.
https://source.android.com/docs/core/architecture/kernel/sta...
However Windows also has a lower level system API / ABI i.e. Win32 that's always stable all the way to Win 95. WinSXS sometimes helps for certain legacy apps too. This allows apps using different C libraries to work together. Win32 contains everything about the OS interface. Creating windows, drawing things, basic text, allocating pages from the OS, querying users, getting info about files is all stable. Win32 is the lower level of the C library. There is also a better separation of the functions in different DLLs. Win32 has multiple DLLs that has different jobs and they are mostly orthogonal. Kernel32 contains core almost system call stuff. Shell32 contains interactions with the OS shell i.e. creating windows, message boxes, triggering different UIs.
The surrounding libraries like DirectX / DirectPlay / DirectWrite that are also used by the games are also part of the system libs starting from 7. They are also stable.
On Linux world there is no separation of the system level libraries and normal user libraries. Glibc is just another library that one depends on. It contains the entire lower level interface to kernel, all the network stuff and POSIX user access stuff. It also contains a component that has no job in a C library: the dynamic executable loader. Unlike Windows on the Unix systems the C library is at the lowest level and Glibc being the default libc for Linux and making itself the default provider of the dynamic executable loader makes writing stable ABI almost impossible. Since other libraries depend on Glibc and executables depend on Glibc's dynamic loader everything is affected by the domino effect.
The elf loader should be extracted from the glibc... but there is price to pay, and it is where those guys could do their manic ABI breaking again.
Some very low level interfaces will have to be defined between the external elf loader and the posix/c runtime. For the moment those interfaces are private, just look at how intimate they are about threading and TLS.