Zunächst einmal #include
Aussagen haben nichts mit linking
zu tun. Denken Sie daran, alles mit einem in Front in C
ist für den Präprozessor, nicht der Compiler oder der Linker gemeint.
Aber das sagte die Funktion muss verknüpft werden, nicht wahr?
Lassen Sie uns die Schritte in separaten Schritten ausführen.
Nun, wie Sie sehen, gcc's Flusen (statische Analysator) ist in Aktion!
Was wird gehen wir es zu ignorieren ...
$ 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));
Diesmal nur eine Warnung. Jetzt haben wir eine Objektdatei im aktuellen Verzeichnis. Lassen Sie uns untersuchen Sie ihn ...
$ nm st.o
U isdigit
0000000000000000 T main
U printf
Wie Sie beide printf
und isdigit
als undefiniert aufgeführt wird sehen können. Also muss der Code irgendwo herkommen, nicht wahr?
lass uns fortfahren, es zu verbinden ...
$ gcc st.o
$ nm a.out | grep 'printf\|isdigit'
U [email protected]@GLIBC_2.2.5
U [email protected]@GLIBC_2.2.5
Nun, wie Sie sehen können, ist die Situation leicht verbessert. Als isdigit
und printf
sind nicht hilflose Einzelgänger wie sie in der st.o
waren. Sie können sehen, dass beide Funktionen von GLIBC_2.2.5
bereitgestellt werden. Aber wo ist das GLIBC
?
Nun lassen Sie uns das endgültige ausführbare etwas untersuchen ...
$ 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 ... es ist, dass libc
. So stellt es sich heraus, obwohl Sie keine Anweisung gegeben haben, verlinkt der Linker standardmäßig mit 3 Bibliotheken, eine davon ist die libc
, die sowohl printf
als auch isdigit
enthält.
Sie können das Standardverhalten des Linkers sehen von:
$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}}
Was die beiden anderen Bibliotheken sind?
Gut erinnern, als Sie in a.out
gegraben haben, wurden sowohl printf
als auch isdigit
immer noch als U
angezeigt, das bedeutet unbekannt. Mit anderen Worten, es gab keine memory
Adresse, die diesen Symbolen zugeordnet ist.
In Wirklichkeit ist dies die Magie. Diese Bibliotheken wurden während der Laufzeit tatsächlich geladen, nicht während der Verbindungszeit wie ältere Systeme.
Wie ist es implementiert? Nun, es hat einen Jargon zugeordnet, so etwas wie faul Verknüpfung. Was es tut, ist, wenn der Prozess eine Funktion aufruft, wenn es keine Speicheradresse (TEXT-Abschnitt) gibt, erzeugt es eine Trap
(etwas wie eine Ausnahme im Hochsprachenjargon, wenn die Kontrolle an die Sprachmaschine übergeben wird). Der Kernel fängt diese Trap
ab und übergibt sie an den dynamischen Lader, der die Bibliothek lädt und die zugeordnete Speicheradresse an den aufrufenden Prozess zurückgibt.
Es gibt mehrere theoretische Gründe, warum Dinge fauler zu machen, ist besser als vorher. Ich denke, das ist ein ganz neues Thema, das wir zu einem anderen Zeitpunkt diskutieren werden.
Könnte es sein, dass ctype.h isdigit als Makro neu definiert? –
@WouterHuysentruit nein, ich verstecke nichts. Außerdem, wenn Sie das Programm einfach kopiert und in ideone eingefügt haben, scheint es auf GCC 5.1 gut zu kompilieren und zu verlinken, aber es stürzt ab. – user1537366