2014-07-06 7 views
23

Sono solo curioso qui. Ho creato un oggetto condiviso:Perché ld ha bisogno di -rpath-link quando si collega un eseguibile a un altro in modo che ne sia necessario un altro?

gcc -o liba.so -fPIC -shared liba.c 

E un altro oggetto condiviso, che collega contro il precedente:

gcc -o libb.so -fPIC -shared libb.c liba.so 

Ora, quando si crea un eseguibile che lega contro libb.so, dovrò indicare -rpath-link per ld in modo che possa trovare liba.so quando scoprendo che libb.so dipende da esso:

gcc -o test -Wl,-rpath-link,./ test.c libb.so 

altrimenti ld si lamenterà.

Perché è, che ld DEVE essere in grado di individuare liba.so quando si collega test? Perché a me non sembra che ld stia facendo molto altro che confermare l'esistenza di liba.so. Ad esempio, l'esecuzione di readelf --dynamic ./test elenca solo libb.so in base alle esigenze, quindi suppongo che il linker dinamico debba rilevare la dipendenza libb.so -> liba.so da solo e creare la propria ricerca di liba.so.

Sono su una piattaforma x86-64 GNU/Linux, e il main() - di routine in test chiama una funzione in libb.so che a sua volta chiama una funzione in liba.so.

risposta

17

Why is it, that ld MUST be able to locate liba.so when linking test ? Because to me it doesn't seem like ld is doing much else than confirming liba.so 's existence. For instance, running readelf --dynamic ./test only lists libb.so as needed, so I guess the dynamic linker must discover the libb.so -> liba.so dependency on its own, and make it's own search for liba.so .

Beh, se ho capito correttamente processo di collegamento, ld in realtà non ha bisogno di individuare anche libb.so. Potrebbe semplicemente ignorare tutti i riferimenti non risolti in test sperando che il linker dinamico li risolva durante il caricamento di libb.so in fase di runtime. Ma se ld si comportasse in questo modo, molti errori di "riferimento non definito" non verrebbero rilevati al momento del collegamento, ma verrebbero rilevati quando si tenta di caricare test in runtime. Quindi ld esegue semplicemente il controllo che tutti i simboli non trovati nello stesso test possono essere trovati nelle librerie condivise che dipendono da test. Quindi, se il programma test ha un errore "riferimento non definito" (alcune variabili o funzioni non trovate in test e nessuna in libb.so), ciò diventa ovvio al momento del collegamento, non solo in fase di runtime. Quindi tale comportamento è solo un ulteriore controllo di sanità mentale.

Ma ld va anche oltre. Quando si collegano test, ld controlla inoltre che tutti i riferimenti non risolti in libb.so si trovano nelle librerie condivise che libb.so dipende (nel nostro caso libb.so dipende liba.so, quindi richiede liba.so per essere situato in fase di collegamento). Bene, in realtà ld ha già eseguito questo controllo, quando stava collegando libb.so. Perché lo fa controllando la seconda volta ...Forse gli sviluppatori di ld hanno trovato questo doppio controllo utile per rilevare dipendenze non funzionanti quando si tenta di collegare il programma a una libreria obsoleta che potrebbe essere caricata nei tempi in cui era collegata, ma ora non può essere caricata perché le librerie dipendono on sono aggiornati (ad esempio, liba.so è stato successivamente rielaborato e parte della funzione è stata rimossa da esso).

UPD

appena fatto alcuni esperimenti. Sembra che il mio assunto "in realtà ld ha già eseguito questo controllo, quando stava collegando libb.so" è errato.

Supponiamo il liba.c ha il seguente contenuto:

int liba_func(int i) 
{ 
    return i + 1; 
} 

e libb.c ha la seguente:

int liba_func(int i); 
int liba_nonexistent_func(int i); 

int libb_func(int i) 
{ 
    return liba_func(i + 1) + liba_nonexistent_func(i + 2); 
} 

e test.c

#include <stdio.h> 

int libb_func(int i); 

int main(int argc, char *argv[]) 
{ 
    fprintf(stdout, "%d\n", libb_func(argc)); 
    return 0; 
} 

Quando si collega libb.so:

0.123.516,410617 millions
gcc -o libb.so -fPIC -shared libb.c liba.so 

linker non genera alcun messaggio di errore che liba_nonexistent_func non può essere risolto, invece genera semplicemente una libreria condivisa interrotta libb.so. Il comportamento è lo stesso di come si crea una libreria statica (libb.a) con ar che non risolve anche i simboli della libreria generata.

Ma quando si tenta di collegare test:

gcc -o test -Wl,-rpath-link=./ test.c libb.so 

si ottiene l'errore:

libb.so: undefined reference to `liba_nonexistent_func' 
collect2: ld returned 1 exit status 

Rilevamento errore del genere non sarebbe possibile se ld non ha la scansione in modo ricorsivo tutte le condiviso librerie. Sembra quindi che la risposta alla domanda sia la stessa di cui ho detto sopra: ld, -rpath-link per assicurarsi che l'eseguibile collegato possa essere caricato in seguito da un caricamento dinamico. Solo un controllo di sanità.

UPD2

Avrebbe senso per verificare la presenza di riferimenti non risolti il ​​più presto possibile (durante il collegamento libb.so), ma ld per alcuni motivi non lo fa. Probabilmente è per consentire di creare dipendenze cicliche per le librerie condivise.

liba.c può avere la seguente implementazione:

int libb_func(int i); 

int liba_func(int i) 
{ 
    int (*func_ptr)(int) = libb_func; 
    return i + (int)func_ptr; 
} 

Così liba.so utilizza libb.so e libb.so utilizza liba.so (meglio non fare mai una cosa del genere).Questo compila con successo e funziona:

$ gcc -o liba.so -fPIC -shared liba.c 
$ gcc -o libb.so -fPIC -shared libb.c liba.so 
$ gcc -o test test.c -Wl,-rpath=./ libb.so 
$ ./test 
-1217026998 

Anche se readelf dice che non ha bisogno di liba.solibb.so:

$ readelf -d liba.so | grep NEEDED 
0x00000001 (NEEDED)      Shared library: [libc.so.6] 
$ readelf -d libb.so | grep NEEDED 
0x00000001 (NEEDED)      Shared library: [liba.so] 
0x00000001 (NEEDED)      Shared library: [libc.so.6] 

Se ld controllato per i simboli non risolti durante il collegamento di una libreria condivisa, la il collegamento di liba.so non sarebbe possibile.

Nota che ho usato chiave -rpath invece di -rpath-link. La differenza è che -rpath-link viene utilizzato in fase di collegamento solo per verificare che tutti i simboli del eseguibile finale possono essere risolti, mentre -rpath ingloba in realtà il percorso specificato come parametro nel ELF:

$ readelf -d test | grep RPATH 
0x0000000f (RPATH)      Library rpath: [./] 

Quindi è ora possibile eseguire test se le librerie condivise (liba.so e libb.so) si trovano nella directory di lavoro corrente (./). Se hai appena utilizzato -rpath-link, non ci sarebbe nessuna voce in test ELF e dovresti aggiungere il percorso alle librerie condivise al fileo alla variabile di ambiente LD_LIBRARY_PATH.

UPD3

In realtà è possibile verificare la presenza di simboli non risolti durante il collegamento libreria condivisa, --no-undefined opzione deve essere usata per fare quello:

$ gcc -Wl,--no-undefined -o libb.so -fPIC -shared libb.c liba.so 
/tmp/cc1D6uiS.o: In function `libb_func': 
libb.c:(.text+0x2d): undefined reference to `liba_nonexistent_func' 
collect2: ld returned 1 exit status 

Inoltre ho trovato un buon articolo che chiarisce molti aspetti del collegamento di librerie condivise che dipendono da altre librerie condivise: Better understanding Linux secondary dependencies solving with examples.

+1

Imparato molto. Grazie. Il collegamento non funziona più però. –

+2

@SurajeetBharati il ​​link dovrebbe essere corretto una volta che la mia modifica è approvata (in pratica, sostituire la barra finale con '.html') – Edward

+1

Funziona ora. :) –

0

non sono in realtà dicendo ld (durante il collegamento libb contro liba) doveliba è - solo che si tratta di una dipendenza. Un rapido ldd libb.so ti mostrerà che non riesce a trovare liba.

Dal momento che presumibilmente queste librerie non si trovano nel percorso di ricerca del linker, si otterrà un errore del linker quando si collega l'eseguibile. Tieni presente che quando si collega la liba stessa, la funzione in libb è ancora non risolta, ma il comportamento predefinito di ld non è quello di preoccuparsi dei simboli non risolti nei DSO finché non si collega l'eseguibile finale.

6

sistema di voi, attraverso ld.so.conf, ld.so.conf.d, e l'ambiente di sistema, LD_LIBRARY_PATH, ecc .., fornisce le percorsi di ricerca delle biblioteche a livello di sistema, che sono integrate da librerie installate attraverso pkg-config informazioni e simili quando si genera nei confronti di serie librerie. Quando una biblioteca risiede in un percorso di ricerca definito, i percorsi di ricerca della libreria standard vengono seguiti automaticamente consentendo di trovare tutte le librerie richieste.

Non esiste uno standard percorso di ricerca libreria per librerie condivise personalizzate create dall'utente. Specificare il percorso di ricerca per le librerie tramite la designazione -L/path/to/lib durante la compilazione e il collegamento. Per le librerie in posizioni non standard, il percorso di ricerca della libreria può essere inserito facoltativamente nell'intestazione del file eseguibile (intestazione ELF) in fase di compilazione in modo che il file eseguibile possa trovare le librerie necessarie.

rpath fornisce un modo di incorporare il percorso di ricerca della libreria di runtime personalizzato nell'intestazione ELF in modo che sia possibile trovare anche le librerie personalizzate senza dover specificare il percorso di ricerca ogni volta che viene utilizzato. Questo vale per le librerie che dipendono anche dalle librerie.Come hai trovato, non solo l'ordine che specifichi le librerie sulla riga di comando è importante, devi anche fornire il percorso di ricerca della libreria run-time o rpath, informazioni per ogni libreria dipendente a cui stai collegando, in modo che l'intestazione contiene la posizione di tutte le librerie necessarie per l'esecuzione.

Addemdum da Commenti

My question is primarily why ld must "automatically try to locate the shared library" (liba.so) and "include it in the link".

Questo è semplicemente il modo ld opere. Da man ld "L'opzione -rpath viene anche utilizzata quando si localizzano oggetti condivisi necessari per gli oggetti condivisi esplicitamente inclusi nel collegamento ... Se -rpath non viene utilizzato quando si collega un eseguibile ELF, il contenuto della variabile di ambiente" LD_RUN_PATH "sarà usato se è definito." Nel tuo caso non è disponibile nel LD_RUN_PATH quindi avrà bisogno di un modo per localizzare liba durante la compilazione del tuo eseguibile, con rpath (descritto sopra) o fornendo un percorso di ricerca esplicito ad esso.

Secondarily what "include it in the link" really means. To me it seems that it just means: "confirm it's existence" (liba.so's), since libb.so's ELF headers are not modified (they already had a NEEDED tag against liba.so), and the exec's headers only declare libb.so as NEEDED. Why does ld care about finding liba.so, can it not just leave the task to the run-time linker?

No, ritorno alla semantica di ld. Per produrre un "buon collegamento", , ld deve essere in grado di individuare tutte le librerie dipendenti tutte. ld non può assicurare un buon collegamento altrimenti. Il linker di runtime deve trovare e caricare, non solo per trovare le librerie condivise necessarie per un programma. ld non può garantire che ciò accada a meno che lo ld stesso non possa trovare tutte le librerie condivise necessarie al momento in cui il programma è collegato.

+1

Sì, ma l'opzione -rpath-link non inserisce alcun tag RPATH in nessuno dei file oggetto. La documentazione dice: Quando si utilizza ELF o SunOS, una libreria condivisa potrebbe richiedere un altro. Ciò si verifica quando un collegamento ld -shared include una libreria condivisa come uno dei file di input. Quando il linker incontra una tale dipendenza quando esegue un collegamento non condiviso, non modificabile, cercherà automaticamente di individuare la libreria condivisa richiesta e includerla nel collegamento, se non è inclusa esplicitamente. In tal caso, l'opzione -rpath-link specifica il primo set di directory da cercare –

+1

E suppongo che il collegamento di un eseguibile sia un "collegamento non condiviso, non modificabile". La mia domanda è principalmente perché ld deve "cercare automaticamente di localizzare la libreria condivisa" (liba.so) e "includerla nel link". Secondariamente ciò che significa "includerlo nel link" significa davvero. A me sembra che ciò significhi semplicemente: "conferma la sua esistenza" (liba.so's), poiché le intestazioni ELF di libb.so non sono modificate (avevano già un tag NEEDED contro liba.so) e le intestazioni di exec dichiarano libb. così come NECESSARIO. Perché mi interessa trovare la liba.so, non può semplicemente lasciare l'attività al linker di runtime? –

0

Immagino che tu debba sapere quando usare l'opzione -rpath e l'opzione -rpath-link. Prima cito quello man ld specificato:

  1. The difference between -rpath and -rpath-link is that directories specified by -rpath options are included in the executable and used at runtime, whereas the -rpath-link option is only effective at link time. Searching -rpath in this way is only supported by native linkers and cross linkers which have been configured with the --with-sysroot option.

È necessario distinguere tra link-tempo e tempo di esecuzione. In base alla risposta di anton_rh accettata, il controllo di simboli non definiti non è abilitato durante la compilazione e il collegamento di librerie condivise o librerie statiche, ma ABILITATO durante la compilazione e il collegamento di file eseguibili. (Tuttavia, si noti che esistono alcuni file che sono libreria condivisa e file eseguibili, ad esempio, ld.so. Digitare man ld.so per esplorarlo e non so se il controllo dei simboli non definiti sia abilitato durante la compilazione di questi file di tipi "duali").

Quindi -rpath-link viene utilizzato durante il controllo del collegamento e -rpath viene utilizzato per il tempo di collegamento e il runtime poiché rpath è incorporato nelle intestazioni ELF. Ma dovresti fare attenzione che l'opzione -rpath-link sostituirà l'opzione -rpath durante il tempo di collegamento se entrambi sono specificati.

Ma ancora, perché l'opzione -rpath-option e -rpath? Penso che siano usati per eliminare "il collegamento eccessivo".Vedi questo Better understanding Linux secondary dependencies solving with examples., usa semplicemente ctrl + F per navigare ai contenuti relativi a "overlinking". Dovresti concentrarti sul motivo per cui "il collegamento" è negativo e, a causa del metodo adottato per evitare il "collegamento", l'esistenza delle opzioni ld-rpath-link e -rpath è ragionevole: omettiamo deliberatamente alcune librerie nei comandi per la compilazione e il collegamento per evitare " overlinking "e, a causa dell'omissione, ld o o -rpath per individuare queste librerie omesse.

Problemi correlati