First of all #include
statements don't have anything to do with linking
. Remember anything with a #
in-front in C
is meant for the preprocessor, not the compiler or the linker.
But that said the function has to be linked isn't it?
Let's do the steps in separate steps.
$ gcc -c -Werror --std=c99 st.c
st.c: In function ‘main’:
st.c:5:22: error: implicit declaration of function ‘isdigit’ [-Werror=implicit-function-declaration]
printf("%d %d\n",isdigit(48),isdigit(48.4));
^
cc1: all warnings being treated as errors
Well as you see gcc's lint(static analyzer) is in action!
Whatever we will proceed to ignore it...
$ gcc -c --std=c99 st.c
st.c: In function ‘main’:
st.c:5:22: warning: implicit declaration of function ‘isdigit’ [-Wimplicit-function-declaration]
printf("%d %d\n",isdigit(48),isdigit(48.4));
This time only an warning. Now we have a object file at the current directory. Let's inspect it...
$ nm st.o
U isdigit
0000000000000000 T main
U printf
As you can see both printf
and isdigit
is listed as undefined. So the code has to come from somewhere isn't it?
let's proceed to link it ...
$ gcc st.o
$ nm a.out | grep 'printf\|isdigit'
U isdigit@@GLIBC_2.2.5
U printf@@GLIBC_2.2.5
Well as you can see situation is mildly improved. As isdigit
and printf
are not helpless loners like they were in the st.o
. You could see both of the functions are provided by GLIBC_2.2.5
. But where is that GLIBC
?
Well let's examine the final executable a bit more...
$ ldd a.out
linux-vdso.so.1 => (0x00007ffe58d70000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb66f299000)
/lib64/ld-linux-x86-64.so.2 (0x000055b26631d000)
AHA...there is that libc
. So it turns out, though you have not given any instruction, the linker is linking with 3 libraries by default, one of them is the libc
which contains both printf
and isdigit
.
You can see the default behaviour of the linker by :
$gcc -dumpspec
*link:
%{!r:--build-id} %{!static:--eh-frame-hdr} %{!mandroid|tno-android-ld:%{m16|m32|mx32:;:-m elf_x86_64} %{m16|m32:-m elf_i386} %{mx32:-m elf32_x86_64} --hash-style=gnu --as-needed %{shared:-shared} %{!shared: %{!static: %{rdynamic:-export-dynamic} %{m16|m32:-dynamic-linker %{muclibc:/lib/ld-uClibc.so.0;:%{mbionic:/system/bin/linker;:/lib/ld-linux.so.2}}} %{m16|m32|mx32:;:-dynamic-linker %{muclibc:/lib/ld64-uClibc.so.0;:%{mbionic:/system/bin/linker64;:/lib64/ld-linux-x86-64.so.2}}} %{mx32:-dynamic-linker %{muclibc:/lib/ldx32-uClibc.so.0;:%{mbionic:/system/bin/linkerx32;:/libx32/ld-linux-x32.so.2}}}} %{static:-static}};:%{m16|m32|mx32:;:-m elf_x86_64} %{m16|m32:-m elf_i386} %{mx32:-m elf32_x86_64} --hash-style=gnu --as-needed %{shared:-shared} %{!shared: %{!static: %{rdynamic:-export-dynamic} %{m16|m32:-dynamic-linker %{muclibc:/lib/ld-uClibc.so.0;:%{mbionic:/system/bin/linker;:/lib/ld-linux.so.2}}} %{m16|m32|mx32:;:-dynamic-linker %{muclibc:/lib/ld64-uClibc.so.0;:%{mbionic:/system/bin/linker64;:/lib64/ld-linux-x86-64.so.2}}} %{mx32:-dynamic-linker %{muclibc:/lib/ldx32-uClibc.so.0;:%{mbionic:/system/bin/linkerx32;:/libx32/ld-linux-x32.so.2}}}} %{static:-static}} %{shared: -Bsymbolic}}
What are the other two libraries?
Well remember when you dug into a.out
, both printf
and isdigit
were still shown as U
that means unknown. In other words, there were no memory
address associated with these symbols.
In reality this is where the magic lies. These libraries were actually loaded during runtime, not during link time like older systems.
How it's implemented? Well it has a jargon associated with, something like lazy linking. What it does, is when the process calls a function , if there is no memory address(TEXT section), it generates a Trap
(Something like a Exception in high level language jargon, when control is handed over to the language engine). The kernel intercepts such Trap
and hands it over to the dynamic loader which loads the library and returns the associated memory address to the caller process.
There are multiple theoretical reason, why doing things lazily is better than doing it beforehand. I guess that's a whole new topic, which we will discuss at some other time.