you don't link all of libc
On OpenBSD, there is a rule that you link with libc to interface with the kernel, because that’s where the syscall stubs live. This causes a great deal of consternation for partisans of other languages, because they don’t want to link “all of libc”. But when does anything link all of libc?
quick proof
$ ls -l /bin/ls /usr/lib/libc.a
-r-xr-xr-x 1 root bin 297448 Mar 20 2024 /bin/ls
-r--r--r-- 1 root bin 8347256 Mar 20 2024 /usr/lib/libc.a
ls is statically linked, yet clearly smaller than libc.a. It is not linked with all of libc. QED.
If you are statically linking with libc just to get open and mmap and related symbols, and somehow all of libc shows up in your program, that’s a deficiency of your dogshit linker.
ar
What’s in libc.a anyway? It’s an ar archive, which is conceptually not so different from tar I guess. It’s a big pile of object files collected together. Like tar, it has options to print (and extract) files.
$ ar p /usr/lib/libc.a malloc.o > malloc.o
$ ar p /usr/lib/libc.a open.o > open.o
$ ls -l *o
-rw-r--r-- 1 tedu wheel 201624 Feb 11 15:32 malloc.o
-rw-r--r-- 1 tedu wheel 2360 Feb 11 15:32 open.o
There’s more code in malloc than open, so that looks about right. You can now objdump or even hexdump these files to confirm that they are the same files one gets by compiling.
Heck, let’s just go through it start to finish.
wx$ cc -c funtime.c
wx$ ar qc libfun.a funtime.o
wx$ ar p libfun.a funtime.o > new.o
wx$ strings new.o
Are we not entertained
wx$ cat funtime.c
char mesg[] = "Are we not entertained";
linking
There’s an index of symbols to files in the archive, generated by ranlib. When you link with an archive, conceptually those object files are extracted and then linked as if added on the command line. There’s not much difference between cc ... -lfun
and cc ... funtime.o
except the former is much simpler in the case where there are many object files involved. A good linker is a bit more sophisticated than running cat to append the archive to the executable, but not terribly so.
The fact that the linker works at the level of object files explains why there are so many source files in libc (and other archives of a certain vintage). Object files do get linked in their entirety, and so splitting libc into many object files allows more granular linking. If it were compiled as a giant splat.c file, then you would end up linking with all of it.
bits
The rules are different for dynamic linking, but all the cool kids only do static linking. You do get all of libc, but it’s already there anyway.
I keep saying all of libc like a nervous tic because the people who hate linking with libc keep saying all of libc. I don’t know why.
The rule that you link with libc on OpenBSD is more like a guideline. It’s not magic, it’s just code. You can write not C code that does the same. There’s no blockchain of all libcs embedded in the kernel to enforce the rule.