[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home] [volume] [parte]


Capitolo 566.   Librerie dinamiche e librerie statiche

La compilazione dei programmi, secondo quanto descritto nei capitoli precedenti, genera sempre file eseguibili «completi», in quanto incorporano tutto il codice necessario al proprio funzionamento. Oltre che suddividere il sorgente in file separati, da riunire assieme in un file eseguibile unico, è possibile costruire una libreria di funzioni, a cui i programmi accedono dopo essere stati avviati, senza incorporarne il codice. Un libreria di questo genere è nota come libreria dinamica, in quanto richiede la creazione di un «collegamento» (link) istantaneo, mentre il programma che la richiede è in funzione.

Il concetto di libreria dinamica si contrappone a quello di libreria statica, la quale comporta l'inclusione del proprio codice nel file eseguibile, in fase di compilazione.

566.1   Il processo di «collegamento» dinamico

Il programma eseguibile che ha bisogno di utilizzare una libreria dinamica, si avvale di un altro programma che a sua volta deve eseguire il «collegamento dinamico» (dynamic link). Il nome di questo collegatore dinamico viene definito in fase di compilazione del primo programma e in un sistema GNU/Linux è costituito generalmente dal file /lib/ld-linux.so.2. A sua volta, il collegatore dinamico cerca il file contenente la libreria richiesta dal programma in un gruppo di directory che solitamente sono /lib/, /usr/lib/ e altre, secondo la configurazione contenuta nel file /etc/ld.so.conf.

Il file /etc/ld.so.conf deve essere elaborato attraverso il programma ldconfig che a sua volta produce il file /etc/ld.so.cache, il quale viene interpellato effettivamente da /lib/ld-linux.so.2. Pertanto, quando si modifica il file /etc/ld.so.conf, occorre ricordarsi di riavviare ldconfig.

Se esiste la variabile di ambiente LD_LIBRARY_PATH, i file delle librerie vengono cercati nei percorsi che questa contiene. Per esempio, per utilizzare i file contenuti nella directory corrente, continuando eventualmente in altre directory consuete, basta assegnare il percorso ., seguito dagli altri a cui si è interessati:

export LD_LIBRARY_PATH=".:/lib:/usr/lib:/usr/local/lib"[Invio]

566.2   Creazione di una libreria dinamica

Per compilare dei file sorgenti in modo che diventino una libreria dinamica, occorre usare delle opzioni particolari in fase di collegamento (link) e nei file sorgenti è necessario pubblicizzare le funzioni in modo particolare. A titolo di esempio si prendono due funzioni, rispettivamente per il calcolo della potenza e del fattoriale (sono già state usate nel capitolo 559 in programmi compilati in modo statico), contenute in due file separati. La coppia di listati è completa e vengono mostrate entrambe le versioni per GNU AS e NASM, evidenziando le direttive significative per ottenere una libreria dinamica.

# lib_pwr.s
.section .text
.globl f_pwr
.type f_pwr, @function
#
f_pwr:
    enter $4, $0
    pusha
    #
    mov   8(%ebp), %esi    # Base.
    mov   12(%ebp), %edi   # Esponente.
    #
    cmp   $0, %esi         # Se la base è pari a 0, restituisce 0.
    jz    f_pwr_end_0      #
    #
    cmp   $0, %edi         # Se l'esponente è pari a 0, restituisce 1.
    jz    f_pwr_end_1      #
    #
    dec   %edi             # Riduce l'esponente di una unità.
    push  %edi             # f_pwr (ESI, EDI) ==> EAX
    push  %esi             #
    call  f_pwr            #
    add   $8, %esp         #
    mul   %esi             # EDX:EAX = EAX*ESI
    mov   %eax, -4(%ebp)   # Salva il risultato.
    jmp   f_pwr_end_X      # Conclude la funzione.
    #
f_pwr_end_0:
    popa                   # Conclude la funzione con EAX = 0.
    mov $0, %eax           #
    leave                  #
    ret                    #
f_pwr_end_1:
    popa                   # Conclude la funzione con EAX = 1.
    mov $1, %eax           #
    leave                  #
    ret                    #
f_pwr_end_X:
    popa                   # Conclude la funzione con EAX pari
    mov -4(%ebp), %eax     # al valore salvato nella variabile
    leave                  # locale.
    ret                    #
# lib_fact.s
.section .text
.globl f_fact
.type f_fact, @function
#
f_fact:
    enter $4, $0
    pusha
    #
    mov   8(%ebp), %edi    # Valore di cui calcolare il fattoriale.
    #
    cmp   $1, %edi         # Il fattoriale di 1 è 1.
    jz    f_fact_end_1     #
    #
    mov   %edi, %esi       # ESI contiene il valore di cui si vuole
    dec   %esi             # il fattoriale, ridotto di una unità.
    #
    push  %esi             # f_fact (ESI) ==> EAX
    call  f_fact           #
    add   $4, %esp         #
    mul   %edi             # EDX:EAX = EAX*EDI
    mov   %eax, -4(%ebp)   # Salva il risultato.
    jmp   f_fact_end_X     # Conclude la funzione.
    #
f_fact_end_1:
    popa                   # Conclude la funzione con EAX = 1.
    mov $1, %eax           #
    leave                  #
    ret                    #
f_fact_end_X:
    popa                   # Conclude la funzione con EAX pari
    mov -4(%ebp), %eax     # al valore salvato nella variabile
    leave                  # locale.
    ret                    #
; lib_pwr.s
section .text
global f_pwr:function
;
f_pwr:
    enter 4,0
    pusha
    ;
    mov   esi, [ebp+8]     ; Base.
    mov   edi, [ebp+12]    ; Esponente.
    ;
    cmp   esi, 0           ; Se la base è pari a 0, restituisce 0.
    jz    f_pwr_end_0      ;
    ;
    cmp   edi, 0           ; Se l'esponente è pari a 0, restituisce 1.
    jz    f_pwr_end_1      ;
    ;
    dec   edi              ; Riduce l'esponente di una unità.
    push  edi              ; f_pwr (ESI, EDI) ==> EAX
    push  esi              ;
    call  f_pwr            ;
    add   esp, 8           ;
    mul   esi              ; EDX:EAX = EAX*ESI
    mov   [ebp-4], eax     ; Salva il risultato.
    jmp   f_pwr_end_X      ; Conclude la funzione.
    ;
f_pwr_end_0:
    popa                   ; Conclude la funzione con EAX = 0.
    mov eax, 0             ;
    leave                  ;
    ret                    ;
f_pwr_end_1:
    popa                   ; Conclude la funzione con EAX = 1.
    mov eax, 1             ;
    leave                  ;
    ret                    ;
f_pwr_end_X:
    popa                   ; Conclude la funzione con EAX pari
    mov eax, [ebp-4]       ; al valore salvato nella variabile
    leave                  ; locale.
    ret                    ;
; lib_fact.s
section .text
global f_fact:function
;
f_fact:
    enter 4,0
    pusha
    ;
    mov   edi, [ebp+8]     ; Valore di cui calcolare il fattoriale.
    ;
    cmp   edi, 1           ; Il fattoriale di 1 è 1.
    jz    f_fact_end_1     ;
    ;
    mov   esi, edi         ; ESI contiene il valore di cui si vuole
    dec   esi              ; il fattoriale, ridotto di una unità.
    ;
    push  esi              ; f_fact (ESI) ==> EAX
    call  f_fact           ;
    add   esp, 4           ;
    mul   edi              ; EDX:EAX = EAX*EDI
    mov   [ebp-4], eax     ; Salva il risultato.
    jmp   f_fact_end_X     ; Conclude la funzione.
    ;
f_fact_end_1:
    popa                   ; Conclude la funzione con EAX = 1.
    mov eax, 1             ;
    leave                  ;
    ret                    ;
f_fact_end_X:
    popa                   ; Conclude la funzione con EAX pari
    mov eax, [ebp-4]       ; al valore salvato nella variabile
    leave                  ; locale.
    ret                    ;

Come si può osservare, non basta dichiarare come globale il simbolo della funzione: occorre anche specificare il suo ruolo di funzione.

Ammesso che i file si chiamino, rispettivamente, lib_pwr.s e lib_fact.s, si compilano come di consueto per ottenere i file oggetto relativi:

as --gstabs -o lib_pwr.o lib_pwr.s[Invio]

as --gstabs -o lib_fact.o lib_fact.s[Invio]

Ovvero:

nasm -g -f elf -o lib_pwr.o lib_pwr.s[Invio]

nasm -g -f elf -o lib_fact.o lib_fact.s[Invio]

Poi, per ottenere la libreria vera e propria, si procede con ld nel modo seguente (a questo punto non fa differenza l'origine dei file oggetto):

ld -shared -o libmate.so lib_pwr.o lib_fact.o[Invio]

Così facendo si ottiene il file libmate.so che costituisce la libreria voluta (la sigla «so» sta per Shared object).

566.3   Creare un programma che utilizza una libreria dinamica

Seguendo l'esempio della sezione precedente, si può creare un programma che si avvale della funzione f_fact, contenuta nella libreria dinamica libmate.so:

# op1!
#
.section .data
op1:    .int    5
#
.section .text
.globl _start
.extern f_fact
#
_start:
    push  op1         # f_fact (op1) ==> EAX
    call  f_fact      #
    add   $4, %esp    #
    #
    mov   %eax, %ebx  # Restituisce il valore del fattoriale,
    mov   $1, %eax    # ammesso che sia abbastanza piccolo
    int   $0x80       # da poter essere rappresentato come
                      # valore di uscita.
; op1!
;
section .data
op1:    dd    5
;
section .text
global _start
extern f_fact
;
_start:
    push  long [op1] ; f_fact (op1) ==> EAX
    call  f_fact     ;
    add   esp, 4     ;
    ;
    mov   ebx, eax   ; Restituisce il valore del fattoriale,
    mov   eax, 1     ; ammesso che sia abbastanza piccolo
    int   0x80       ; da poter essere rappresentato come
                     ; valore di uscita.

La compilazione per produrre il file oggetto avviene nel modo consueto:

as --gstabs -o fact.o fact.s[Invio]

Ovvero:

nasm -g -f elf -o fact.o fact.s[Invio]

Poi, la trasformazione in file eseguibile richiede l'uso di opzioni particolari per ld:

ld -L . \
  \   -dynamic-linker /lib/ld-linux.so.2 \
  \   -lmate \
  \   -o fact \
  \   fact.o
[Invio]

Vanno osservate alcune opzioni:

Opzione Descrizione
-L .
Indica di cercare la libreria nella directory corrente.
-dynamic-linker /lib/ld-linux.so.2
Indica di usare, al momento dell'avvio del programma che si sta creando, il «collegatore dinamico» costituito dal file /lib/ld-linux.so.2.
-lmate
Indica di instaurare il collegamento dinamico con la libreria «mate», ovvero il file libmate.so (che secondo l'opzione -L . si trova nella directory corrente).

Dato il modo in cui viene usata l'opzione -l, si comprende che i file delle librerie devono avere sempre un nome che inizia per lib....

Dall'ultimo comando mostrato si ottiene il file eseguibile fact nella directory corrente, il quale ha bisogno della libreria libmate.so. Se si vuole avviare questo programma, è necessario che il file della libreria si trovi in uno dei percorsi previsti. In questo caso si trova provvisoriamente nella directory corrente e si può utilizzare la variabile di ambiente LD_LIBRARY_PATH per istruire di conseguenza il collegatore dinamico:

LD_LIBRARY_PATH="." ./fact ; echo $?[Invio]

120

Con ldd si può verificare la dipendenza del programma dalle librerie, ma anche in questo caso va utilizzata la variabile di ambiente LD_LIBRARY_PATH:

LD_LIBRARY_PATH="." ldd fact[Invio]

        linux-gate.so.1 =>  (0xffffe000)
        libmate.so => ./libmate.so (0xb7f46000)

566.4   Creare un file che utilizza una libreria dinamica standard

Così come è possibile utilizzare le proprie librerie dinamiche, si possono sfruttare benissimo quelle scritte da altri autori. Per poter utilizzare le funzioni comuni del linguaggio C, ci si può avvalere della libreria omonima, c, ovvero libc.so, che di norma si trova nella directory /lib/. A titolo di esempio viene mostrato un programma che emette un messaggio attraverso lo standard output:

# hello.s
#
.section .data
msg:    .ascii  "Ciao mondo!\n\0"
#
.section .text
.globl _start
.extern printf
.extern exit
#
_start:
    push  $msg        # printf (msg)
    call  printf      #
    add   $4, %esp    #
    #
    push  $0          # exit (0)
    call  exit        #
; hello.s
;
section .data
msg:    db      "Ciao mondo!", 0x0A, 0x00
;
section .text
global _start
extern printf
extern exit
;
_start:
    push  long msg    ; printf (msg)
    call  printf      ;
    add   esp, 4      ;
    ;
    push  long 0      ; exit (0)
    call  exit        ;

Il programma utilizza due funzioni, printf e exit, la prima per visualizzare un messaggio e la seconda per concluderne il funzionamento. La funzione printf richiede come primo argomento (in questo caso anche l'unico) l'indirizzo iniziale di una stringa terminata da un byte completamente a zero: nel sorgente per GNU AS il codice di interruzione di riga e lo zero vengono inseriti con le sequenze \n\0, mentre in quello per NASM i codici relativi sono messi direttamente in forma numerica.

Il sorgente si compila come di consueto per ottenere il file oggetto. Successivamente, il collegamento avviene con il comando seguente:

ld -dynamic-linker /lib/ld-linux.so.2 \
  \   -lc \
  \   -o hello \
  \   hello.o
[Invio]

Rispetto al caso descritto nella sezione precedente, si può osservare che manca l'opzione -L, in quanto la libreria va cercata nei percorsi standard previsti; inoltre, conformemente all'esempio già visto, per indicare la libreria è stato usato solo il nome c, da cui l'opzione -lc.

Dal momento che la libreria si trova nei percorsi standard, per avviare il programma non servono accorgimenti particolari:

./hello[Invio]

Ciao mondo!

Con ldd si può verificare la dipendenza del programma dalle librerie:

ldd hello[Invio]

        linux-gate.so.1 =>  (0xffffe000)
        libc.so.6 => /lib/tls/libc.so.6 (0xb7e71000)
        /lib/ld-linux.so.2 (0xb7fb3000)

566.5   Librerie statiche

È utile sapere come sono organizzate le «librerie statiche» in un sistema GNU. Di per sé sono semplicemente file oggetto, compilati in modo da rendere pubblici i simboli delle funzioni a cui si può essere interessati esternamente, ma raccolti in un archivio che costituisce la libreria.

ar -cvq libmate.a lib_pwr.o lib_fact.o[Invio]

Il comando appena mostrato crea la libreria «mate» nel file libmate.a, composta dai file oggetto lib_pwr.o e lib_fact.o. Il file della libreria è un semplice archivio «ar», che non prevede la compressione.

Il modo più semplice per collegare un programma che utilizza una libreria statica di questo genere è quello di indicare il file della libreria come se fosse un file oggetto:

ld -o fact fact.o libmate.a[Invio]

566.6   Riferimenti


Appunti di informatica libera 2008 --- Copyright © 2000-2008 Daniele Giacomini -- <appunti2 (ad) gmail·com>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome librerie_dinamiche_e_librerie_statiche.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home]

Valid ISO-HTML!

CSS validator!

Gjlg Metamotore e Web Directory