Dopo le 3 del mattino, due sole categorie di persone sono al lavoro: le prostitute e i programmatori.

— Anonimo

Librerie statiche e dinamiche in ambiente Linux

Cerchiamo di illustrare il caricamento di una libreria dinamica tramite le funzioni della libreria dl dlopen(), dlsym() e dlclose().

La procedura può essere divisa in tre step: nel primo ci si occuperà di aprire la libreria, nel secondo si caricherà una funzione presente nella libreria e nel terzo si chiuderà.

1) Apertura della libreria
Per aprire e caricare una libreria dinamica a partire dal suo percorso assoluto si può far uso del seguente codice:

     /* defines dlopen(), etc. */
#include <dlfcn.h>

/* handle of the opened library */
void* lib_handle;
lib_handle = dlopen(”/home/argesino/Progetti/dynamic_library/mylib.so”, RTLD_LAZY);

if (!lib_handle) {
fprintf(stderr, “Errore nella funzione dlopen(): %sn”, dlerror());
exit(1);
}


La funzione dlopen() richiede due parametri: il primo rappresenta il percorso completo della libreria dinamica mentre il secondo reppresenta un flag che definisce tutti i simboli della libreria necessari. Nel caso preso in esame, si fa uso dell’approccio lazy (specificato tramite il flag RTLD_LAZY) che significa risolvere simboli non definiti tramite la libreria aperta. E’ possibile specificare altri [tag ma in questo articolo non saranno presi in considerazione.

La funzione dlopen() ritorna un puntatore alla libreria caricata, che può essere usato successivamente per referenziare simboli contenuti nella libreria. Ritornerà NULL in caso di errore. In tal caso, è buona abitudine usare la funzione dlerror() per stampare un messaggio d’errore più indicativo del problema che si è verificato, come è stato fatto nell’esempio sopra.

2) Chiamare funzioni dinamicamente usando dlsym()
Dopo aver aperto la libreria condivisa, è possibile trovare in essa simboli relativi alle funzioni e alle variabili. A questo punto bisogna stare molto attenti a non commettere errori in quanto il compilatore non sarebbe in grado di controllare queste dichiarazioni. Di seguito è mostrato come cercare l’indirizzo di una funzione chiamata ‘library_function()’ presente nella libreria che si occupa di stampare un messaggio a video.
/* locate the symbol in the library */
lib_func = dlsym(lib_handle, “library_function”);error = dlerror();
if (error) {
fprintf(stderr, “Error: %sn”, error);
exit(1);
}

/* call the library’s utility function. */
(*lib_func)();


E’ consigliabile inserire molti controlli di errore per capire possibili problemi che potrebbero verificarsi.

3) Chiusura di una libreria dinamica con dlclose()
Lo step conclusivo consiste nella chiusura della libreria in maniera tale da liberare la memoria che essa occupa. Questo codice sarà scritto nel momento in cui si è sicuri che non si desidera utilizzare altre funzioni della libreria. Se si ha intenzione di riusare la funzione è preferibile lasciarla aperta perché il caricamento della libreria richiede un po’ di tempo. Per chiudere la libreria è necessario scrivere il seguente codice:

/* finally, close the library. */
dlclose(lib_handle);

In tal modo tutte le risorse occupate dalla libreria saranno liberate.

Funzioni di Startup e Cleanup automatiche
Per concludere un’ultima funzionalità che potrebbe essere utile. Il caricamento della libreria dinamica dà la possibilità di definire due speciali funzioni in ciascuna libreria, chiamate rispettivamente _init e _fini. La funzione _init, se trovata nella libreria, è invocata automaticamente quando la libreria è aperta prima che dlopen() ritorni il valore di ritorno alla funzione chiamante. Essa può essere usata per invocare porzioni di codice di inizializzazione necessari ad inizializzare le strutture dati utilizzate dalla libreria, leggere i file di configurazione e così via.

La funzione _fini è chiamata quando la libreria è chiusa con dlclose(). Essa può essere usata per fare operazioni di cleanup richieste dalla libreria (liberare strutture dati, chiudere file, ecc..).

Un esempio di programma che fa uso della interfaccia dl e delle funzioni di startup e cleanup automatiche può essere reperito al seguente link. L’esempio è completo di Makefile perciò è sufficiente scaricare tutti i file nella stessa cartella, modificare il path della libreria dinamica e compilare con il comando make. L’esempio contiene due librerie dinamiche e in base al comando lanciato sarà caricata una delle due. Per eseguire il programma è necessario lanciare con il comando

./prog 1

oppure
./prog 2

a seconda della libreria dinamica che si vuole caricare. Un’occhiata a questo codice può risultare molto utile per capire la problematica trattata nell’articolo.