Prima di tutto le istruzioni #include
non hanno nulla a che fare con linking
. Ricordare qualsiasi cosa con un #
in primo piano in C
è pensato per il preprocessore, non per il compilatore o il linker.
Ma detto che la funzione deve essere collegata non è vero?
Facciamo i passaggi in passaggi separati.
$ 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
Bene, come si vede gint's lint (analizzatore statico) è in azione!
Qualunque cosa si procederà ad ignorarlo ...
$ 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));
Questa volta solo un avvertimento. Ora abbiamo un file oggetto nella directory corrente. Andiamo a controllarla ...
$ nm st.o
U isdigit
0000000000000000 T main
U printf
Come si può vedere sia printf
e isdigit
è elencato come indefinito. Quindi il codice deve venire da qualche parte, vero?
procediamo a collegarlo ...
$ gcc st.o
$ nm a.out | grep 'printf\|isdigit'
U [email protected]@GLIBC_2.2.5
U [email protected]@GLIBC_2.2.5
Bene come potete vedere la situazione è leggermente migliorata. Come isdigit
e printf
non sono solitari indifesi come se fossero nello st.o
. È possibile vedere che entrambe le funzioni sono fornite da GLIBC_2.2.5
. Ma dov'è questo GLIBC
?
Bene esaminiamo l'eseguibile finale un po 'più ...
$ 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 ... non v'è che libc
. Quindi, sebbene tu non abbia dato alcuna istruzione, il linker è collegato a 3 librerie per impostazione predefinita, una di queste è la libc
che contiene sia printf
e isdigit
.
Si può vedere il comportamento predefinito del linker da:
$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}}
Quali sono le altre due biblioteche?
Bene ricordare quando si scavate nella a.out
, sia printf
e isdigit
erano ancora indicato come U
che significa sconosciuto. In altre parole, non c'erano gli indirizzi memory
associati a questi simboli.
In realtà è qui che si trova la magia. Queste librerie sono state effettivamente caricate durante il runtime, non durante il tempo di collegamento come nei sistemi precedenti.
Come è implementato? Bene ha un gergo associato, qualcosa come collegamento pigro. Quello che fa, è quando il processo chiama una funzione, se non c'è alcun indirizzo di memoria (sezione TESTO), genera un Trap
(Qualcosa come un'eccezione nel gergo della lingua di alto livello, quando il controllo viene passato al motore della lingua). Il kernel intercetta tale Trap
e lo consegna al caricatore dinamico che carica la libreria e restituisce l'indirizzo di memoria associato al processo del chiamante.
Ci sono molti motivi teorici, perché fare cose pigramente è meglio che farlo in anticipo. Immagino sia un argomento completamente nuovo, di cui parleremo in un altro momento.
Potrebbe essere che ctype.h ridefinisce isdigit come macro? –
@WouterHuysentruit no, non sto nascondendo nulla. Inoltre, se hai appena copiato il programma e incollato in ideone sembra compilare e linkare bene su GCC 5.1, ma si blocca. – user1537366