upvote
Modularizing (and versioning each part independently) libc would go a long way. There is also a need to separate the stuff needed for system integration with what is necessary for users of the C language to actually do stuff.
reply
> Modularizing

Can't be done. The libc is legacy, it can't be changed without breaking everything. It's also mandatory on every operating system other than Linux.

A change in paradigm is necessary. Freestanding C, not hosted C. This completely gets rid of the libc and is a surprisingly clean language. Linux only, because it's the only kernel with a stable binary interface. Every other OS forces a C runtime.

I once worked on a liblinux project that embodied this... Stopped because Linux itself has a nolibc thing in the kernel tree and I didn't want to compete with it. Now I'm working on the Rust version.

> what is necessary for users of the C language to actually do stuff

Surprisingly little. I wrote an entire lisp interpreter in freestanding C with Linux system calls. It managed to survive for a rather long time without any memory allocation at all.

The system layer is refreshingly tiny. It consists of a memory allocator and extremely basic functions like memmove and strlen. I successfully got rid of total nonsense like thread local errno, locales, implicit buffering, cached global state, possibly more. All that stuff is gone! Exactly one global survived: the stack canary generated by GCC and clang. Every other symbol in the ELF is controlled by me.

Wasn't able to get rid of the NUL terminator. Linux itself needs it. To get rid of that little billion dollar mistake requires an entirely new kernel with zero UNIX/POSIX influence. I had to make my peace with that one. All my buffers maintain an extra NUL byte at the end.

reply
> A change in paradigm is necessary. Freestanding C, not hosted C. This completely gets rid of the libc and is a surprisingly clean language. Linux only, because it's the only kernel with a stable binary interface. Every other OS forces a C runtime.

Great choice for small programs, but what if I want hardware accelerated 3d?

reply
Modern 3D APIs are built around passing around a few buffers per frame and long command queues. Should be doable with IPC.
reply
Yeah, that's the annoying part. Been wondering about this for years, and graphics support was among the first issues raised on the lone lisp GitHub repository. At this point I've even started exploring the mesa codebase, made some patches but didn't submit them yet due to the AI stigma.

With Linux system calls alone it should be possible to set up kernel mode setting without depending on any toolkit at all. This should be enough to get a framebuffer for software rendering.

For hardware acceleration though, one must give this graphics context to an OpenGL ES implementation. That's where it gets ugly. There is no way to divorce that from the libc short of literally rewriting it.

Maybe Vulkan will enable it? I can't say for sure at my current knowledge level.

reply
Vulkan is designed to only load core statically and the whole extension spaghetti dynamically, using driver entry points.

https://vulkan.lunarg.com/doc/view/latest/mac/LoaderInterfac...

reply
exactly
reply
>A change in paradigm is necessary. Freestanding C, not hosted C. This completely gets rid of the libc and is a surprisingly clean language. Linux only, because it's the only kernel with a stable binary interface. Every other OS forces a C runtime.

I'm sure those OSes make efforts to make said runtime binary compatible between executables.

reply
Yes, but the whole idea is to delete the runtime binary and talk to the kernel directly.

That's when you run into the Darth Vader of binary interfaces.

> I have altered the ABI. Pray I do not alter it further. -- De Raadt

reply
> Can't be done. The libc is legacy, it can't be changed without breaking everything. It's also mandatory on every operating system other than Linux.

Windows explicitly does not want you to link the system libc. You are expected to bring your own, and doing so means your process has multiple libc's loaded into its address space.

And if you choose to build a binary that doesn't need a libc, you won't be bringing one.

reply
> Windows explicitly does not want you to link the system libc

This is categorically false; UCRT[1] is a thing. The 'U' stands for universal. Unlike Linux, Windows allows developers to choose their ABI boundary and also ship that boundary if they desire, or use the 'system' one and ask older platforms to install redistributables or Windows update packages. There's the old and new C runtimes in MSVCRT.DLL and UCRTBASE.DLL, the C++ runtime in VCRUNTIME140.DLL, Win32 in KERNEL32.DLL, USER32.DLL and more, and then the stable-ish kernel interfaces in NTDLL.DLL, in order of 'closeness to the kernel'.

And also, 'libc' is a UNIXism; on Windows the term is CRT, for 'C runtime'.

[1]: https://learn.microsoft.com/en-gb/cpp/porting/upgrade-your-c...

reply
> Unlike Linux, Windows allows developers to choose their ABI boundary

Linux has a stable system call ABI. I can trash the entire user space and boot Linux with init=/my/program if I want.

It's not that Linux doesn't allow developers to choose, it's that Linux doesn't actually control the userspace ABIs. Userspace is whatever we want it to be, and people settled on "GNU/Linux".

reply
That was introduced in Windows 10, Windows history is a long one.

Usually Windows developers will use Win32 directly, ZeroMemory() instead of memset(), and so on.

reply
Indeed. I was just about to point out the fact that the CRT only provides the C standard library; Windows applications can run perfectly fine without one by linking only to the Windows API DLLs.
reply
By "system libc" I'm only referring to msvcrt.dll, found in the Windows directory. Quoting Raymond Chen:

> At some point, the decision was made to just give up and declare it an operating system DLL, to be used only by operating system components.

https://devblogs.microsoft.com/oldnewthing/20140411-00/?p=12...

reply
I think the grander point is that there is no real 'system CRT' on Windows; as I mentioned, there are multiple entry points each at an appropriate level of abstraction available to both platform and application developers (not that there is a real difference between the two, since platform developers may also write applications like Office). Many Windows platform libraries (WIL, for instance) themselves use UCRT instead of MSVCRT now. The latter exists, but it is by no means and has not ever been by any means the single entry point to the Windows platform, unlike glibc on most desktop Linux distributions.
reply
> You are expected to bring your own, and doing so means your process has multiple libc's loaded into its address space.

That only massively compounds the problem.

> And if you choose to build a binary that doesn't need a libc, you won't be bringing one.

NT system calls are not stable. You still need to link against ntdll.dll at the very least, like a forced Linux vDSO.

reply
> That only massively compounds the problem.

The Windows ecosystem, that manages to deliver built binaries easily & widely, regardless of whether the author has a 1 year old OS or a 15 year old OS, suggests that it's not as big a problem as you believe.

reply
It's not a problem in the same way that things like snap or flatpak aren't problems. It works but it bloats things up considerably and makes you wonder where it all went so wrong. I mean, dozens of slightly incompatible runtimes inside a single process?
reply
Those incompatible runtimes are separated by a linker that doesn't resolve all symbols globally, but rather scoped to the shared object they're expected from. They all coexist happily, and if you're so inclined you could resolve the same symbol from each, if you had reason to do so.
reply
We have a similar no dependency philosophy

SQLite already does this with its VFS layer. You hand it the OS functions it needs instead of it grabbing them, etc.

You could look at going straight to syscalls, mesa would be a rewrite nightmare

I went zero-dependency on a production CMS. Rust, no framework, no external crates beyond argon2 for password hashing. Running live on client sites. The hard part wasn't building it, it was accepting that everything you reach for is pulling glibc or similar assumptions back in through the side door. SQLite's VFS model is the right pattern. More things should work that way.

reply