Sdílené knihovny


[Minulá]

Sdílené knihovny

Dynamicky linkované obrazy neobsahují veškerý kód a data, která ke své činnosti potřebují. Některé části jsou umístěny ve sdílených knihovnách, které se zavádějí až v okamžiku spuštění programu. Tabulky sdílených knihoven formátu ELF dále slouží dynamickému linkeru při linkování sdílených knihoven do běžícího programu. Linux používá nekolik dynamických linkerů, ld.so, libc.so, ld-linux.so, všechny jsou umístěny v adresáři /lib. Při dynamickém linkování jsou v tabulkých obrazu ELF uloženy informace pro každou knihovní funkci, na níž se program odkazuje. Tyto informace dynamickému linkeru říkají, jak nalézt knihovní funkci a jak ji vlinkovat do adresového prostoru programu.

Pokud je třeba zavést dynamickou knihovnu, loader se ji nejprve snaží najít na cestě určené proměnnou prostředí LD_LIBRARY_PATH, dále hledá v adresářích definovaných v souboru /etc/ld.so.conf. Mezi standardní místa v systému patří následující adresáře:

     /lib
     /usr/lib
     /usr/X11R6/lib
     /usr/local/lib

Interaktivní rozhraní dynamických knihoven

Na přednášce bylo prezentováno tzv. explicitní využívání sdílených knihoven, tj. o manipulaci s knihovnami, jejich nahrávání či uvolňování během chodu programu, se stará sám programátor. Čož bezesporu vede k větší pružnosti, na druhou stranu, Linux disponuje dostatečně flexibilním mechanismem nahrávání obrazů (vynuceným nahráváním), níže popsaný interface by měli používat jen uživatelé, kteří vědí, co dělají.

Základní funkce pro práci se sdílenými knihovnami jsou dlclose, dlerror, dlopen a dlsym. Najdeme je v hlavičkovém souboru dlfcn.h.

     void *dlopen (const char *filename, int flag);

Funkce dlopen nahraje dynamickou knihovnu ze souboru určeného nulou ukončeným řetězcem filename a vrací `handle' této knihovny. Pokud není jmého absolutní cesta (tj. nezačíná znakem `/'), potom se soubor hledá následovně:

  1. prohledá se seznam cest oddělený dvojtečkami v proměnné prostředí (kterou může definovat uživatel) LD_LIBRARY_PATH
  2. seznam knihoven v souboru /etc/ld.so.cache
  3. adresáře /usr/lib a /lib

Jestliže specifikujeme jméno knihovny jako nulový řetězec, bude vrácen handle hlavního programu. Exrterní odkazy v knihovně se řeší pomocí vnitřního seznameu knihovních závislostí a pomocí již otevřených knihoven s maskou RTLD_GLOBAL. Jestliže byl objektový program linkován s přepínačem `-rdynamic', potom budou také globální symboly v tomto spustitelném souboru užity k řešení závislostí v knihovně.

Přepínač může mít ještě hodnotu RTLD_LAZY, což znamená vyřešit nedefinované symboly, jakmile je knihovna spustěna, nebo RTLD_NOW, což vyřeší všechny problémy s nedefinovanými symboly před tím, než se ukončí dlopen anebo skončí dlopen s chybou, jestliže se tak nemůže stát. Můžeme také provést logický součet RTLD_GLOBAL a přepínače flag, což zajistí, že externí symboly budou dostupné postupně nahrávaným knihovnám.

Jestliže knihovna exportuje rutinu nazvanou init, potom je vyvolána ještě před ukončením dlopen. Jesliže tutéž knihovnu nahrajete dvakrát (nebo vícekrát) pomocí ldopen, bude vám vrácen stejný handler. Dynamická knihovna není dealokována, dokud se dlclose nezavolá tolikrát, kolikrát byla s úspěchem registrována funkcí dlopen.

     const char *dlerror (void);

Pokud dlopen z nějakého důvodu selže, vrací NULL. Pokud chcete dostat nějakou lidskou hlášku o tom, co kde selhalo (ve smusly dl :))), můžete použít funkci dlerror. Funkce dlerror vrací NULL, jestliže se nestala žádná chyba od posledního zavolání nebo inicializace. Voláme-li dlerror dvakrát, podruhé se vždycky dočkáme NULL; V případě chyby je vrácen řetězec popisující tuto chybu.

     void *dlsym (void *handle, char *symbol);

Funkce dlsym akceptuje `handle' vrácený funkcí dlopen při otevření knihovny, dále bere řetězec sumbol a vrací adresu, na které je symbol nahraný. Jestliže není symbol naleze, dlsym vrací NULL. Jednoduchý způsob, jak otestovat, zda nedošlo k chybě, je uchovat si výsledek volání dlerrorm a porovnat, jestli jde o NULL nebo jiný řetězec.

     int dlclose (void *handle);

Funkce dclose dekrementuje počet odkazů na `handle' dynamické knihovny. Jestliže počet odkazů klesne k nule a žádné jiné knihovny nepoužívají jejich symbolů v této knihovně, potom dojde k uvolnění dynamické knihovny. Jestliže knihovna exportuje rutinu fini, potom se rutina zavolá před samotným uvolněním knihovny.

Nyní si můžeme ukázat malé příklady:

     #include <dlfcn.h>
     int main (int argc, char **argv) {
       void *handle = dlopen ("/lib/libm.so", RTLD_LAZY);
       double (*cosine) (double) = dlsym (handle, "cos");

       printf ("%f\n", (*cosine) (2.0));
       dlclose (handle);
       }

Dejme tomu, že program jsme umístili do souboru `doze1.c', můžeme jej sestavit následujícím příkazem:

     gcc -rdynamic -o doze1 doze1.c -ldl

Následující příklad je obdobný s tím rozdílem, že jde na věc poněkud bezpečněji. :)))

     #include <stdio.h>
     #include <dlfcn.h>

     int main (int argc, char **argv) {
       void *handle;
       double (*cosine) (double);
       char *error;

       handle = dlopen ("/lib/libm.so", RTLD LAZY);
                                                       -
       if (! handle) {
         fputs (dlerror (), stderr);
         exit (1);
         }

       cosine = dlsym (handle, "cos");
       if ((error = dlerror ()) != NULL)  {
         fputs (error, stderr);
         exit (1);
         }

       printf ("%f\n", (*cosine) (2.0));
       dlclose (handle);
       }


Send me a mail to <vychodiv@alpha.inf.upol.cz>.