So only part of that gets "bloated" is Win32 API itself (which is spread across multiple DLLs and don't actually bloat RAM usage). Most of the time even those functions and structures are carefully designed to have some future-proofness but it is usual to see APIs like CreateFile, CreateFile2, CreateFile3. Internally the earlier versions are upgraded to call the latest version. So not so much bloating there either.
When the C runtime and the OS system calls are combined into the single binary like POSIX, it creates the ABI hell we're in with the modern Unix-likes. Either the OSes have to regularly break the C ABI compatibility for the updates or we have to live with terrible implementations.
GNU libc and Linux combo is particularly bad. On GNU/Linux (or any other current libc replacements), the dynamic loading is also provided by the C library. This makes "forever" binary file compatibility particularly tricky to achieve. Glibc broke certain games / Steam by removing some parts of their ELF implementation: https://sourceware.org/bugzilla/show_bug.cgi?id=32653 . They backed due to huge backlash from the community.
If "the year of Linux desktop" would ever happen, they need to either do an Android and change the definition of what a software package is, or split Glibc into 3 parts: syscalls, dynamic loader and the actual C library.
PS: There is actually a catch to your " C runtime is optional." argument. Microsoft still intentionally holds back the ability of compiling native ABI Windows programs without Visual Studio.
The structured exception handlers (equivalent of Windows for SIGILL, SIGBUS etc.. not for SIGINT or SIGTERM though) are populated by the object files from the C runtime libraries (called VCRuntime/VCStartup). So it is actually not possible to have official Windows binaries without MSVC or any other C runtime like Mingw-64 that provides those symbols. It looks like some developers in Microsoft wanted to open-source VCRuntime / VCStartup but it was ~vetoed~ not fully approved by some people: https://github.com/microsoft/STL/issues/4560#issuecomment-23... , https://www.reddit.com/r/cpp/comments/1l8mqlv/is_msvc_ever_g...
What is left of the C standard library, if you remove syscall wrappers?
> ABI hell
Is that really the case? From my understanding the problem is more, that Linux isn't an OS, so you can't rely on any *.so being there.
> What is left of the C standard library, if you remove syscall wrappers?
Still quite a bit actually. Stuff like malloc, realloc, free, fopen, FILE, getaddrinfo, getlogin, math functions like cos, sin tan, stdatomic implementations, some string functions are all defined in C library. They are not direct system calls unlike: open, read, write, ioctl, setsockopt, capget, capset ....
> > ABI hell
> Is that really the case? From my understanding the problem is more, that Linux isn't an OS, so you can't rely on any *.so being there.
That's why I used more specific term GNU/Linux at the start. There is no guarantee of any .so file can be successfully loaded even if it is there. Glibc can break anything. With the Steam bug I linked this is exactly what happened. Shared object files were there, Glibc stopped supporting a certain ELF file field.
There is only and only one guarantee with Linux-based systems: syscalls (and other similar ways to talk with kernel like ioctl struct memory layouts etc) always keep working.
There is so much invisible dependence on Glibc behavior. Glibc also controls how the DNS works for the programs for example. That also needs to be split into a different library. Same for managing user info like `getlogin`. Moreover all this functionality is actually implemented as dynamic library plugins in Glibc (NSSwitch) that rely on ld.so that's also shipped by Glibc. It is literally a Medusa head of snakes that bite multiple tails. It is extremely hard to test ABI breakages like this.
Wrapper around sbrk, mmap, etc. whatever the modern variant is.
> fopen, FILE
Wrapper around open, write, read, close.
> stdatomic implementations
You can argue, these are wrappers around thread syscalls.
> math functions like cos, sin tan, some string functions are all defined in C library
True for these, but they are so small, they could just be inlined directly, on their own they wouldn't necessarily deserve a library.
> That's why I used more specific term GNU/Linux at the start.
While GNU/Linux does describe a complete OS, it doesn't describe any specific OS. Every Distro does it's own thing, so I think these is what you actually need to call an OS. But everything is built so that the user can take the control over the architecture and which components the OS consists of, so every installation can be a snowflake, and then it is technically its own OS.
I personally consider libc and the compiler (which both make a C implementation) to be part of the OS. I think this is both grounded in theory and in practice. Only in some weird middle ground between theory and practice you can consider them to not be.
I don't think that's correct. While `malloc` uses `brk` syscall to allocate large memory areas, it uses non-trivial algorithms and data-structures to further divide that areas into smaller chunks which actually returned. Using syscall for every `malloc`/`free` is quite an overhead.
> fopen, FILE
> Wrapper around open, write, read, close.
They're not just wrappers. They implement internal buffering, some transformations (for example see "binary" mode, "text" mode.
> stdatomic implementations
> You can argue, these are wrappers around thread syscalls.
No, they're wrappers around compiler intrinsics which emit specific assembly instructions. At least for any sane architecture.
> I personally consider libc and the compiler (which both make a C implementation) to be part of the OS. I think this is both grounded in theory and in practice. Only in some weird middle ground between theory and practice you can consider them to not be.
C is used a lot in embedded projects. I even think that's the majority of C code nowadays. These projects usually don't use libc (as there's no operating system, so concept of file or process just doesn't make sense). So it's very important to separate C compiler and libc and C compiler must be able to emit code with zero dependencies.
> C is used a lot in embedded projects.
Sure that's a freestanding implementation, which primary distinction is that it doesn't rely on the libc. The notion of the libc being part of the OS in the wider sense, still holds water here, since here no OS corresponds to no libc.
It would be better if you specified which part was removed: support for executable code on stack. This is used in 99% cases by malware so it is better to break 1% of broken programs and have other 99% run safer.
The dynamic loader used to be its own library, FWIW. It got merged into the main one recently.
Are there any other solutions that don't depend on glibc?