See also Linux's nolibc headers, which allows one to write C software that completely bypass libc, but instead directly operate through syscalls.
https://github.com/torvalds/linux/tree/master/tools/include/...
A sample use-case? I was developing an Erlang-like actor platform that should operate under Linux as well as a bare-metal microkernel, and all I needed is a light layer over syscalls instead of pulling the entire glibc. Also it provides a simple implementation for standard C functions (memcpy, printf) so I don't have to write them myself.
> What would be a use-case?
Maybe bootstapping a new language with no dependencies.
Where was this when I needed it!
As one of my class projects, I built a Linux compatibility layer for the toy OS we had built, by adding a proper ELF loader and emulating syscalls. I really struggled to get glibc or even musl working, and so I ended up hand-coding some `-nostdlib` programs instead of being able to use coreutils. If nolibc really works as a minimal libc, would have been incredibly cool to be able to run coreutils on my OS!
Do I understand correctly that nolibc is just another implementation of the C standard library in terms of Linux syscalls? Comparably to, say, musl libc?
nolibc seems kinda neglected, or like a minimal subset of what's actually useful. There's no pread/pwrite etc, only read/write, forcing you to use lseek and ruining concurrent use.
Well... last time I had a look at the assembly code of syscall entry on x86_64, I was scared away... this piece of "assembly" does require some very niche C compiler options to be compatible (stack alignment and more if I recall properly).
Linux "C" code hard dependency on gcc/clang/("ultra complex compilers") is getting worse by the day. It should (very easy to say, I know) have stayed very simple and plain C99+ with smart macro definitions to be replaced with pure assembly or the missing bits for modern hardware programming (atomics/memory barriers, explicit unaligned access, etc), but those abominations like _generic (or static assert,__thread,etc) are toxic additions to the C standard (there are too many toxic additions and not enough removal/simplification/hardening in ISO C, yes, we will have to define a "C profile" which breaks backward compatibility with hardening and simplifications).
I don't say all extensions are bad, but I think they need more "smart and pertinent pick and choose" (and I know this is a tough call), just because they "cost". For instance, for a kernel, we know it must have fine grained control of ELF object sections... or we would get much more source files (one per pertinent section) or "many more source configuration macros" (....but there I start to wonder if it was not actually the "right" way instead of requiring a C compiler to support such extension, it moves everything to the linker script... which is "required" anyway for a kernel).
Linus T. is not omnipotent and can do only that much and a lot of "official" linux devs are putting really nasty SDK dependency requirements in everyday/everywhere kernels.
That said, on my side, many of my user apps are now directly using linux syscalls... but are written in RISC-V assembly interpreted on x86_64 (I have a super lean interpreter/syscall translater written in x86_64 assembly and a super lean executable format wrapped in ELF executable format), or very plain and simple C99+ (legacy or because I want some apps to be a bit more 'platform crossy'... for now).
> See also Linux's nolibc headers
Kind of an understatement. The existence of an official interface obsoletes 3rd party projects like the one posted.
Another use-case is when you are writing threaded code that uses the clone() syscall instead of pthreads, usually for something with high performance, unusual clone flags, or a very small stack.
Most libc functions, including the syscall wrappers and all pthreads functions, aren't safe to call in threads created by raw clone(). Anything that reads or writes errno, for example, is not safe.
I've had to do this a couple of times. One a long time ago was an audio mixing real-time thread for a video game, which had to keep the audio device fed with low-latency frames for sound effects. In those days, pthreads wasn't good enough. For talking to the audio device, we had to use the Linux syscall wrapper macros, which have been replaced by nolibc now. More recently, a thread pool for high-performance storage I/O, which ran slightly faster than io_uring, and ran well on older kernels and ones with io_uring disabled for security.