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
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ě:
LD_LIBRARY_PATH
/etc/ld.so.cache
/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>.