Linux, in un certo senso, è una serie di librerie statiche e dinamiche che dipendono l'una dall'altra. Per i nuovi utenti di sistemi basati su Linux, l'intera gestione delle librerie può essere un mistero. Ma con l'esperienza, l'enorme quantità di codice condiviso integrato nel sistema operativo può essere un vantaggio quando si scrivono nuove applicazioni.
Per aiutarti a entrare in contatto con questo argomento, ho preparato un piccolo esempio applicativo che mostra i metodi più comuni che funzionano su comuni distribuzioni Linux (questi non sono stati testati su altri sistemi). Per seguire questo tutorial pratico utilizzando l'applicazione di esempio, apri un prompt dei comandi e digita:
$ git clone https://github.com/hANSIc99/library_sample
$ cd library_sample/
$ make
cc -c main.c -Wall -Werror
cc -c libmy_static_a.c -o libmy_static_a.o -Wall -Werror
cc -c libmy_static_b.c -o libmy_static_b.o -Wall -Werror
ar -rsv libmy_static.a libmy_static_a.o libmy_static_b.o
ar: creating libmy_static.a
a - libmy_static_a.o
a - libmy_static_b.o
cc -c -fPIC libmy_shared.c -o libmy_shared.o
cc -shared -o libmy_shared.so libmy_shared.o
$ make clean
rm *.o
Dopo aver eseguito questi comandi, questi file dovrebbero essere aggiunti alla directory (eseguire ls
per vederli):
my_app
libmy_static.a
libmy_shared.so
Informazioni sul collegamento statico
Quando l'applicazione si collega a una libreria statica, il codice della libreria diventa parte dell'eseguibile risultante. Questo viene eseguito solo una volta al momento del collegamento e queste librerie statiche di solito terminano con un .a
estensione.
Una libreria statica è un archivio (ar) di file oggetto. I file oggetto sono generalmente nel formato ELF. ELF è l'abbreviazione di Executable and Linkable Format, compatibile con molti sistemi operativi.
L'output del file
comando ti dice che la libreria statica libmy_static.a
è il ar
tipo di archivio:
$ file libmy_static.a
libmy_static.a: current ar archive
Con ar -t
, puoi guardare in questo archivio; mostra due file oggetto:
$ ar -t libmy_static.a
libmy_static_a.o
libmy_static_b.o
Puoi estrarre i file dell'archivio con ar -x <archive-file>
. I file estratti sono file oggetto in formato ELF:
$ ar -x libmy_static.a
$ file libmy_static_a.o
libmy_static_a.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
Informazioni sul collegamento dinamico
Più risorse Linux
- Comandi Linux cheat sheet
- Cheat sheet sui comandi avanzati di Linux
- Corso online gratuito:Panoramica tecnica RHEL
- Cheat sheet della rete Linux
- Cheat sheet di SELinux
- Cheat sheet dei comandi comuni di Linux
- Cosa sono i container Linux?
- I nostri ultimi articoli su Linux
Collegamento dinamico significa l'uso di librerie condivise. Le librerie condivise di solito terminano con .so
(abbreviazione di "oggetto condiviso").
Le librerie condivise sono il modo più comune per gestire le dipendenze sui sistemi Linux. Queste risorse condivise vengono caricate in memoria prima dell'avvio dell'applicazione e, quando diversi processi richiedono la stessa libreria, questa verrà caricata solo una volta sul sistema. Questa funzione consente di risparmiare sull'utilizzo della memoria da parte dell'applicazione.
Un'altra cosa da notare è che quando un bug viene corretto in una libreria condivisa, ogni applicazione che fa riferimento a questa libreria ne trarrà vantaggio. Ciò significa anche che se il bug non viene rilevato, ogni applicazione di riferimento ne risentirà (se l'applicazione utilizza le parti interessate).
Può essere molto difficile per i principianti quando un'applicazione richiede una versione specifica della libreria, ma il linker conosce solo la posizione di una versione incompatibile. In questo caso, devi aiutare il linker a trovare il percorso per la versione corretta.
Anche se questo non è un problema quotidiano, la comprensione del collegamento dinamico ti aiuterà sicuramente a risolvere tali problemi.
Fortunatamente, i meccanismi per questo sono abbastanza semplici.
Per rilevare quali librerie sono necessarie per l'avvio di un'applicazione, puoi utilizzare ldd
, che stamperà le librerie condivise utilizzate da un determinato file:
$ ldd my_app
linux-vdso.so.1 (0x00007ffd1299c000)
libmy_shared.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007f56b869b000)
/lib64/ld-linux-x86-64.so.2 (0x00007f56b8881000)
Nota che la libreria libmy_shared.so
fa parte del repository ma non viene trovato. Questo perché il linker dinamico, che è responsabile del caricamento di tutte le dipendenze in memoria prima di eseguire l'applicazione, non riesce a trovare questa libreria nelle posizioni standard in cui cerca.
Errori associati ai linker che trovano versioni incompatibili di librerie comuni (come bzip2
, per esempio) può creare confusione per un nuovo utente. Un modo per aggirare questo è aggiungere la cartella del repository alla variabile di ambiente LD_LIBRARY_PATH
per dire al linker dove cercare la versione corretta. In questo caso, la versione corretta è in questa cartella, quindi puoi esportarla:
$ LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH
$ export LD_LIBRARY_PATH
Ora il linker dinamico sa dove trovare la libreria e l'applicazione può essere eseguita. Puoi eseguire nuovamente ldd
per richiamare il linker dinamico, che esamina le dipendenze dell'applicazione e le carica in memoria. L'indirizzo di memoria viene visualizzato dopo il percorso dell'oggetto:
$ ldd my_app
linux-vdso.so.1 (0x00007ffd385f7000)
libmy_shared.so => /home/stephan/library_sample/libmy_shared.so (0x00007f3fad401000)
libc.so.6 => /lib64/libc.so.6 (0x00007f3fad21d000)
/lib64/ld-linux-x86-64.so.2 (0x00007f3fad408000)
Per scoprire quale linker viene invocato, puoi usare file
:
$ file my_app
my_app: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=26c677b771122b4c99f0fd9ee001e6c743550fa6, for GNU/Linux 3.2.0, not stripped
Il linker /lib64/ld-linux-x86–64.so.2
è un collegamento simbolico a ld-2.30.so
, che è il linker predefinito per la mia distribuzione Linux:
$ file /lib64/ld-linux-x86-64.so.2
/lib64/ld-linux-x86-64.so.2: symbolic link to ld-2.31.so
Guardando indietro all'output di ldd
, puoi anche vedere (accanto a libmy_shared.so
) che ogni dipendenza termina con un numero (ad es. /lib64/libc.so.6
). Il solito schema di denominazione degli oggetti condivisi è:
**lib** XYZ.so **.<MAJOR>** . **<MINOR>**
Sul mio sistema, libc.so.6
è anche un collegamento simbolico all'oggetto condiviso libc-2.30.so
nella stessa cartella:
$ file /lib64/libc.so.6
/lib64/libc.so.6: symbolic link to libc-2.31.so
Se stai riscontrando il problema che un'applicazione non si avvia perché la libreria caricata ha la versione errata, è molto probabile che tu possa risolvere questo problema ispezionando e riorganizzando i collegamenti simbolici o specificando il percorso di ricerca corretto (vedi "Il caricatore dinamico :ld.so" di seguito).
Per ulteriori informazioni, guarda il ldd
pagina man.
Caricamento dinamico
Caricamento dinamico significa che una libreria (ad esempio, un .so
file) viene caricato durante il runtime di un programma. Questo viene fatto utilizzando un determinato schema di programmazione.
Il caricamento dinamico viene applicato quando un'applicazione utilizza plug-in che possono essere modificati durante il runtime.
Vedi il dlopen
pagina man per ulteriori informazioni.
Il caricatore dinamico:ld.so
Su Linux, hai a che fare principalmente con oggetti condivisi, quindi deve esserci un meccanismo che rilevi le dipendenze di un'applicazione e le carichi in memoria.
ld.so
cerca gli oggetti condivisi in questi luoghi nel seguente ordine:
- Il percorso relativo o assoluto nell'applicazione (codificato con
-rpath
opzione del compilatore su GCC) - Nella variabile d'ambiente
LD_LIBRARY_PATH
- Nel file
/etc/ld.so.cache
Tieni presente che l'aggiunta di una libreria all'archivio della libreria di sistema /usr/lib64
richiede privilegi di amministratore. Puoi copiare libmy_shared.so
manualmente nell'archivio della libreria e far funzionare l'applicazione senza impostare LD_LIBRARY_PATH
:
unset LD_LIBRARY_PATH
sudo cp libmy_shared.so /usr/lib64/
Quando esegui ldd
, puoi vedere il percorso dell'archivio della biblioteca ora visualizzato:
$ ldd my_app
linux-vdso.so.1 (0x00007ffe82fab000)
libmy_shared.so => /lib64/libmy_shared.so (0x00007f0a963e0000)
libc.so.6 => /lib64/libc.so.6 (0x00007f0a96216000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0a96401000)
Personalizza la libreria condivisa in fase di compilazione
Se desideri che la tua applicazione utilizzi le tue librerie condivise, puoi specificare un percorso assoluto o relativo durante la fase di compilazione.
Modificare il makefile (riga 10) e ricompilare il programma invocando make -B
. Quindi, l'output di ldd
mostra libmy_shared.so
è elencato con il suo percorso assoluto.
Cambia questo:
CFLAGS =-Wall -Werror -Wl,-rpath,$(shell pwd)
A questo (assicurati di modificare il nome utente):
CFLAGS =/home/stephan/library_sample/libmy_shared.so
Quindi ricompila:
$ make
Conferma che sta utilizzando il percorso assoluto che hai impostato, che puoi vedere sulla riga 2 dell'output:
$ ldd my_app
linux-vdso.so.1 (0x00007ffe143ed000)
libmy_shared.so => /lib64/libmy_shared.so (0x00007fe50926d000)
/home/stephan/library_sample/libmy_shared.so (0x00007fe509268000)
libc.so.6 => /lib64/libc.so.6 (0x00007fe50909e000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe50928e000)
Questo è un buon esempio, ma come funzionerebbe se stessi creando una libreria per l'uso da parte di altri? È possibile registrare nuove posizioni di libreria scrivendole in /etc/ld.so.conf
o creando un <library-name>.conf
file contenente il percorso in /etc/ld.so.conf.d/
. Successivamente, ldconfig
deve essere eseguito per riscrivere il ld.so.cache
file. Questo passaggio è talvolta necessario dopo aver installato un programma che porta con sé alcune librerie condivise speciali.
Vedi il ld.so
pagina man per ulteriori informazioni.
Come gestire più architetture
Di solito esistono librerie diverse per le versioni a 32 bit ea 64 bit delle applicazioni. L'elenco seguente mostra le posizioni standard per le diverse distribuzioni Linux:
Famiglia Red Hat
- 32 bit:
/usr/lib
- 64 bit:
/usr/lib64
Famiglia Debian
- 32 bit:
/usr/lib/i386-linux-gnu
- 64 bit:
/usr/lib/x86_64-linux-gnu
Famiglia Arch Linux
- 32 bit:
/usr/lib32
- 64 bit:
/usr/lib64
FreeBSD (tecnica non una distribuzione Linux)
- 32 bit:
/usr/lib32
- 64 bit:
/usr/lib
Sapere dove cercare queste librerie di chiavi può rendere i collegamenti alle librerie interrotti un problema del passato.
Sebbene all'inizio possa creare confusione, comprendere la gestione delle dipendenze nelle librerie Linux è un modo per sentirsi in controllo del sistema operativo. Esegui questi passaggi con altre applicazioni per acquisire familiarità con le librerie comuni e continua a imparare come risolvere eventuali problemi di libreria che potrebbero presentarsi lungo il tuo percorso.