On Linux syscalls numbers and arguments are define by POSIX (I think), and their call mechanism is define by ABI.
That's kind of true and kind of not true. POSIX is a family of standards. As far as the API goes, it mandates a C API, but doesn't mandate how they're implemented. It definitely doesn't mandate syscall numbers!
On Linux, some POSIX calls are system calls (e.g. open, read), some are libraries built on top of OS calls (e.g. pthreads), and some bits of POSIX aren't implemented at all (e.g. STREAMS).
I'm not sure how it works nowadays on modern Linux'es, but you might run into issues when dynamically loading shared libraries (so files, that's like dll file on Windows).
Unlike Windows, dynamic shared objects are not built into the operating system. Conceptually, the way that you use one on a modern Linux is to memory map the file into your address space, then trawl through the ELF headers to understand what's in it.
You could implement all that yourself, however, the way it's actually done in Linux is that every program is considered as something like bytecode which is "interpreted" by a special interpreter called ld-linux.so
. Other ELF platforms have something similar; on FreeBSD it's called ld.so
, and on OS X it's called dyld.
There's a good description of how all this stuff works in John Levine's book, Linkers and Loaders
. Levine put a copy of the manuscript free online
, but some of the diagrams are missing. Another option is to look at the dynamic loader from the Flux OSKit
, called rtld. It's a bit old, but it'll give you a good idea of what a minimal dynamic loader looks like in an ELF/POSIXy environment.
I would suggest to link to libc.
I would too. It may not be as Handmade as you'd like, but the simple fact is that Unix is a C virtual machine, and so doesn't really work well without libc.