Risposte:
(Vedi la cronologia di questa risposta per ottenere il testo più elaborato, ma ora penso che sia più facile per il lettore vedere righe di comando reali).
File comuni condivisi da tutti i comandi seguenti
$ cat a.cpp
extern int a;
int main() {
return a;
}
$ cat b.cpp
extern int b;
int a = b;
$ cat d.cpp
int b;
$ g++ -c b.cpp -o b.o
$ ar cr libb.a b.o
$ g++ -c d.cpp -o d.o
$ ar cr libd.a d.o
$ g++ -L. -ld -lb a.cpp # wrong order
$ g++ -L. -lb -ld a.cpp # wrong order
$ g++ a.cpp -L. -ld -lb # wrong order
$ g++ a.cpp -L. -lb -ld # right order
Il linker cerca da sinistra a destra e nota i simboli non risolti mentre procede. Se una libreria risolve il simbolo, ci vogliono i file oggetto di quella libreria per risolvere il simbolo (bo in libb.a in questo caso).
Le dipendenze delle librerie statiche l'una contro l'altra funzionano allo stesso modo: la libreria che necessita di simboli deve essere prima la libreria che risolve il simbolo.
Se una libreria statica dipende da un'altra libreria, ma l'altra libreria dipende nuovamente dalla libreria precedente, esiste un ciclo. È possibile risolvere questo problema racchiudendo le librerie ciclicamente dipendenti da -(
e -)
, come -( -la -lb -)
(potrebbe essere necessario sfuggire alle parentesi, come -\(
e -\)
). Il linker quindi cerca le librerie allegate più volte per garantire la risoluzione delle dipendenze cicliche. In alternativa, è possibile specificare le librerie più volte, in modo da ciascuno è prima di uno con l'altro: -la -lb -la
.
$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -L. -ld -o libb.so # specifies its dependency!
$ g++ -L. -lb a.cpp # wrong order (works on some distributions)
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong order
$ g++ -Wl,--as-needed a.cpp -L. -lb # right order
È lo stesso qui: le librerie devono seguire i file oggetto del programma. La differenza qui rispetto alle librerie statiche è che non è necessario preoccuparsi delle dipendenze delle librerie l'una contro l'altra, poiché le librerie dinamiche risolvono autonomamente le loro dipendenze .
Apparentemente alcune distribuzioni recenti utilizzano per default il --as-needed
flag linker, il che impone che i file oggetto del programma vengano prima delle librerie dinamiche. Se quel flag viene passato, il linker non si collegherà alle librerie che non sono effettivamente necessarie all'eseguibile (e lo rileva da sinistra a destra). La mia recente distribuzione archlinux non usa questo flag di default, quindi non ha dato un errore per non aver seguito l'ordine corretto.
Non è corretto omettere la dipendenza di b.so
contro d.so
quando si crea il primo. Ti verrà richiesto di specificare la libreria al momento del collegamento a
, ma in a
realtà non ha bisogno dell'intero b
stesso, quindi non è necessario preoccuparsi b
delle proprie dipendenze.
Ecco un esempio delle implicazioni se ti manca specificare le dipendenze per libb.so
$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -o libb.so # wrong (but links)
$ g++ -L. -lb a.cpp # wrong, as above
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong, as above
$ g++ a.cpp -L. -lb # wrong, missing libd.so
$ g++ a.cpp -L. -ld -lb # wrong order (works on some distributions)
$ g++ -Wl,--as-needed a.cpp -L. -ld -lb # wrong order (like static libs)
$ g++ -Wl,--as-needed a.cpp -L. -lb -ld # "right"
Se ora guardi a quali dipendenze ha il binario, noti che anche il binario stesso dipende libd
, non solo libb
come dovrebbe. Il binario dovrà essere ricollegato se in libb
seguito dipende da un'altra libreria, se lo fai in questo modo. E se qualcun altro carica libb
usando dlopen
in fase di runtime (pensa a caricare i plugin in modo dinamico), anche la chiamata fallirà. Quindi "right"
dovrebbe essere anche un wrong
.
lorder
+ tsort
. Ma a volte non c'è ordine, se hai riferimenti ciclici. Quindi devi solo scorrere l'elenco delle librerie fino a quando tutto non viene risolto.
Il linker GNU ld è un cosiddetto linker intelligente. Terrà traccia delle funzioni utilizzate dalle precedenti librerie statiche, eliminando definitivamente quelle funzioni che non sono utilizzate dalle sue tabelle di ricerca. Il risultato è che se si collega una libreria statica troppo presto, le funzioni in quella libreria non saranno più disponibili per le librerie statiche successivamente sulla linea di collegamento.
Il tipico linker UNIX funziona da sinistra a destra, quindi metti tutte le tue librerie dipendenti a sinistra e quelle che soddisfano quelle dipendenze sulla destra della linea di collegamento. È possibile che alcune librerie dipendano da altre, mentre allo stesso tempo altre librerie dipendono da esse. Questo è dove diventa complicato. Quando si tratta di riferimenti circolari, correggi il tuo codice!
Ecco un esempio per chiarire come funzionano le cose con GCC quando sono coinvolte le librerie statiche . Quindi supponiamo di avere il seguente scenario:
myprog.o
- contenente la main()
funzione, dipende dalibmysqlclient
libmysqlclient
- statico, per il bene dell'esempio (preferiresti la libreria condivisa, ovviamente, poiché libmysqlclient
è enorme); in /usr/local/lib
; e dipende dalle cose dalibz
libz
(dinamico)Come colleghiamo questo? (Nota: esempi dalla compilazione su Cygwin usando gcc 4.3.4)
gcc -L/usr/local/lib -lmysqlclient myprog.o
# undefined reference to `_mysql_init'
# myprog depends on libmysqlclient
# so myprog has to come earlier on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# we have to link with libz, too
gcc myprog.o -lz -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# libz is needed by libmysqlclient
# so it has to appear *after* it on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient -lz
# this works
Se aggiungi -Wl,--start-group
ai flag del linker non importa in quale ordine si trovano o se ci sono dipendenze circolari.
Su Qt questo significa aggiungere:
QMAKE_LFLAGS += -Wl,--start-group
Risparmia un sacco di tempo nei guai e non sembra rallentare molto il collegamento (che comunque richiede molto meno tempo della compilazione).
È possibile utilizzare l'opzione -Xlinker.
g++ -o foobar -Xlinker -start-group -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a -Xlinker -end-group
è QUASI uguale a
g++ -o foobar -Xlinker -start-group -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a -Xlinker -end-group
Attento!
Un suggerimento rapido che mi ha fatto scattare: se stai invocando il linker come "gcc" o "g ++", quindi usando "--start-group" e "--end-group" non passerai queste opzioni al linker - né segnalerà un errore. Fallirà il collegamento con simboli indefiniti se l'ordine della libreria è sbagliato.
Devi scriverli come "-Wl, - start-group" ecc. Per dire a GCC di passare l'argomento al linker.
L'ordine dei collegamenti è certamente importante, almeno su alcune piattaforme. Ho visto arresti anomali per le applicazioni collegate con le librerie nell'ordine sbagliato (dove errato significa che A è collegato prima di B ma B dipende da A).
L'ho visto molto, alcuni dei nostri moduli si collegano a oltre 100 librerie del nostro codice più sistema e librerie di terze parti.
A seconda dei diversi linker HP / Intel / GCC / SUN / SGI / IBM / etc è possibile ottenere funzioni / variabili non risolte ecc., Su alcune piattaforme è necessario elencare le librerie due volte.
Per la maggior parte usiamo una gerarchia strutturata di librerie, core, piattaforma, diversi livelli di astrazione, ma per alcuni sistemi devi ancora giocare con l'ordine nel comando link.
Una volta che hai trovato una soluzione, documentala in modo che lo sviluppatore successivo non debba risolverla di nuovo.
Il mio vecchio professore diceva " alta coesione e basso accoppiamento ", è ancora vero oggi.
gcc
cambiato a un comportamento più rigoroso-(relativamente) recente.