Statické a sdílené knihovny
Statické knihovny
Statické knihovny už vlastně použít umíte, ikdyž o tom možná nevíte. Statickou knihovnou je objektový soubor, který má v sobě zakompilovanou definici funkce (například).
Při překladu se každá definice použité funkce zkopíruje do výsledného spustitelného souboru. V příkladu na vytváření objektového souboru jsem vytvořil dvě „knihovny“ – number1.o a number2.o.
Každá z těchto knihoven měla v sobě definovanou jen jednu funkci, ale v praxi jich budete mít v každé knihovně spíše více (nějak logicky souvisejících).
Skutečná (statická) knihovna je archív objektových souborů. Archív je jeden
soubor, který v sobě obashuje více objektových souborů.
Vytváří se pomocí programu ar
.
Archiv vytvořený pomocí ar
je něco podobného, jako je třeba
tar
archiv, nebo zip
. (Ar ale není komprimovaný.)
Hlavní výhoda archivu ar
(třeba oproti tar archivu) je ta,
že si s ním rozumí gcc/g++/clang.
Je zvykem, že statická knihovna má příponu .a
a jméno
začíná na lib
.
V příkadu použiji zdrojové kódy z předchozí kapitoly:
$ gcc -c number2.c
$ ar -r libnumber.a number1.o number2.o
ar: creating libnumber.a
$ ar -t libnumber.a
number1.o
number2.o
Vytvořil jsem knihovnu libnumber.a
, do které se vložily objekty
number1.o
a number2.o
Volby programu ar
jsou následující:
Volba | Význam |
---|---|
-r | (replace) nahradí soubor v knihovně |
-t | (type) vypíše obsah knihovny |
-x | (extract) vyextrahuje soubor .o z knihovny |
-d | (delete) vymaže soubor z knihovny |
-S | zakáže vytváření indexu archivu, což urychlí vytvoření archivu. Index pak lze dodatečně vytvořit (když už bude knihovna hotová). |
-s | vytvoří nebo aktualizuje index archivu. |
V knihovně může být více souborů stejného jména. Je potřeba pužít volbu -r
replace,
aby se starý soubor stejného jména (pokud existuje) smazal a nahradil novým.
Index archivu můžete vytvořit příkazem ar -s archiv.a
,
nebo pomocí programu ranlib
.
Index archivu urychluje vyhledávání funkcí v archivu
(a tím i překlad programů s archivem) a také umožňuje, aby se
funkce v archivu volaly navzájem bez ohledu na pořadí, v jakém jsou v archivu
definovány. V předchozím příkladě jsem
nepoužil volbu -s
, proto index v archivu není.
A teď už tam je :-). Odkládat vytváření archivu až na konec má samozřejmě smysl
jen u nějakých mega velkých knihoven.
Pokud jednou volbu -s
použijete, bude index už aktualizován
automaticky (i bez volby -s
).
Užití
Knihovnu (.a) můžete použít při překladu stejně, jako se používají objektové soubory. Výhodu máte už v tom, že díky indexu najde překladač co potřebuje rychleji.
Knihovny také můžete umístit do nějakého adresáře a překladači pak říct,
aby je připojil pomocí volby -l
.
Knihovny se hledají ve standardních adresářích (jako např. /usr/lib/).
Pomocí volby -L
můžete překladači
říct o dalších adresářích, které má prohledávat.
Jméno knihovny se píše bez úvodního lib
a bez koncovky .a
.
Tečka za -L
překladači říká, aby prohledával aktuální adresář.
Místo volby -L
si můžete nastavit proměnnou prostředí
LD_LIBRARY_PATH
. Cesty uložené v této proměnné
(oddělené dvojtečkou) bude překladač také prohledávat.
Tento způsob se ale spíše nedoporučuje. Závislost na
LD_LIBRARY_PATH
většinou vede ke zmatkům.
Sdílené knihovny
Statické knihovny jsou fajn, ale mají jisté nevýhody. Pokud funkci z knihovny budete používat v mnoha programech, každý si ponese vlastní kopii. Výhoda je, že statická knihovna už pro běh programu není pořeba, nevýhoda je, že když najdete ve funkci chybu, musíte překompilovat každý program, který funkci používal. A samozřejmě, rozkopírovaná funkce zabírá místo na disku (což s dnešními velikostmi disků už vlastně není takový problém :-).
Odpovědí jsou sdílené (shared) knihovny. Ty už jste také používali.
Třeba knihovnu standardních funkcí jazyka C, libc.so
(která se linkuje
k programu automaticky), nebo matematickou knihovnu libm.so
(kterou linkujete pomocí volby -lm
). Pro C++ je tu zase
knihovna libc++
.
Jak vidíte, sdílené knihovny mají příponu .so
(ve Windows .dll
) a začínají také na lib
.
Funkce ze sdílených knihoven, jak už asi tušíte, se do výsledného programu neukládají. Do programu se jen uloží odkaz na sdílenou knihovnu. Nevýhodou je, že se při spuštění programu musí načítat i sdílené knihovny, což zabírá nepatrně času navíc. A program bez sdílené knihovny fungovat nebude.
Teď už jen potřebujete vědět, jak sdílenou knihovnu vytvořit. K programu se
připojuje stejně jako statická knihovna (za pomoci voleb -l
a
-L
). Mimochodem, lepší než používat -L
je nainstalovat sdílenou knihovnu do některého ze standardníh adresářů
(najděte si, kde máte soubory *.so
, viz třeba /usr/lib/,
/usr/lib64/) nebo jeho podadresářů.
Position independent code
Pro začátek je pořeba vytvořit objektové soubory s pozičně nezávislým kódem
(position-independent code, PIC).
PIC je kód, který funguje bez ohledu na to, kde je uložen v paměti.
Protože sdílenou knihovnu bude využívat více programů, nemůže knihovna
ukládat data na fixní adresu. Podrobnosti nejsou důležité
(můžete si je nastudovat na wiki, nebo
zde, nebo
zde (So what is
"PIC" code?)), stačí vědět, že musíte při překladu shared knihoven použít volbu -fpic
.
$ gcc -c -o snumber2.o number2.c -fpic -Wall
A teď můžete vytvořit sdílenou knihovnu:
Užití
Zkompilovat program s použitím sdílené knihovny už umíte. Zase platí, že musí
být sdílená knihovna ve standardním adresáři pro sdílené knohovny, nebo musíte
použít -L
.
Kdybyste náhodou pojmenovali sdílenou a statickou knihovnu stejně
(jen s jinou koncovkou), tak ty sdílené mají přednost. Já sem sdílené knihovně
na začátek jména přidal s
(libsnumber.so),
aby se to nepletlo.
Teď můžete spustit výsledný program smain
. Pokud není sdílená
knihovna ve standardním adresáři, musíte nastasvit proměnnou prostředí
LD_LIBRARY_PATH
.
./smain: error while loading shared libraries: libsnumber.so: cannot open shared object file: No such file or directory
$ LD_LIBRARY_PATH=. ./smain
n1 = 1
n2 = 2
$ export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
$ ./smain
n1 = 1
n2 = 2
Při druhém pokusu jsem nastavil LD_LIBRARY_PATH jen pro aktuálně spuštěný příkaz, což je
způsob, který preferuji před použitím příkazu export.
Druhý způsob ukazuje, jak
proměnné prostředí LD_LIBRARY_PATH na začátek přiřadit aktuální adresář (tečka)
a nastavení exportovat, takže bude platné pro celé sezení v terminálu.
To je mimochodem docela nebezpečné, protože vám teď může nějaký útočník vložit
do adresáře svojí hacknutou knihovnu libc.so
(třeba do /tmp). Když pak spustíte program z tohoto adresáře, program načte
útočníkovu knihovnu, místo té z /usr/lib/ …
Systémové sdílené knihovny
Pokud se rozhodnete uložit sdílenou knihovnu do systémových adresářů, musíte ještě spustit program
ldconfig
.
O načtení sdílených knihoven se stará knihovna ld.so
nebo ld-linux.so
.
Tyto knihovny přilinkují sdílené knihovny k programu „za běhu.“
Bývá zvykem, že součástí názvu sdílené knihovny jsou čísla verzí a na knihovnu vedou symbolické linky. Například v mém OpenSuSE mám knihovnu libc-2.22.so, na kterou vede link libc.so.6. Programy se mi pak linkují s knihovnou libc.so.6.
Program ldconfig se stará o vytváření potřebných symbolických linků,
ale také o vytvoření cache pro
knihovny ld.so
a ld-linux.so
, která obsahuje seznam všech
systémových statických khihoven. Pak nemusí ld.so a ld-linux.so při každém
spuštění jakéhokoliv programu znovu a znovu prohledávat adresáře a hledat nejaktuálnější
dostupné verze knihoven.
Instalace sdílené knihovny se tak sestává ze dvou kroků, které musíte provést jako root. Dejte si přitom pozor, ať si nepřepíšete nějakou existující systémovou knihovnu!
$ sudo /sbin/ldconfig
$ unset LD_LIBRARY_PATH
$ ./smain
n1 = 1
n2 = 2
Další příklady a podrobnosti můžete najít na cprogramming.com
Program pkg-config
Některé knihovny jsou nainstalované v nestandardních adresářích, nebo potřebují
k překladu hlavičkové soubory z nestandardních adresářů. Pak je obtíž zjistit,
jaké použít -l
, -L
nebo -I
. S tímto problémem
může pomoci program pkg-config
.
Program pkg-config
spoléhá na konfigurační soubory
(soubory s příponou .pc
), které se instalují do
adresáře /usr/share/pkgconfig/, nebo /usr/lib64/pkgconfig/, nebo
nějakého podobného (podívejte se do manuálové stránky
k pkg-config, kde se dočtete přesné informace). Tyto soubory vytváří
tvůrci knihoven, takže se automaticky instalují při instalaci vývojových
balíčků knihovny.
Použití je jednoduché. Pokud chcete zjistit volby potřebné pro překladač a linker, zadejte tyto příkazy:
$ pkg-config --libs nazev_knihovny
Například pro knihovnu ncursesw:
-D_GNU_SOURCE -D_DEFAULT_SOURCE -I/usr/include/ncursesw
$ pkg-config --libs ncursesw
-lncursesw -ltinfow
Výsledek volání tohoto programu může být (a také bývá) na různých
systémech různý. Proto je vhodné si zvyknout používat pkg-config
pomocí zpětných uvozovek takto:
$ gcc -o main neco-co-pouziva-ncuresw.o `pkg-config --libs ncursesw`
Nebo rovnou takto:
V této kapitole program pkg-config
není k ničemu potřeba.
Tento odstavec měl za cíl vás seznámit s tím, jak tvůrci sdílených knihoven obvykle
poskytují přístup k volbám potřebným pro překlad s jejich knihovnami
(pomocí konfiguračních souborů pro program pkg-config
).
V dalších kapitolách, kde budu představovat některé nestandardní knihovny,
už pkg-config
využijete.
libm
, soubory .pc
nedodávají, protože žádné nestandardní volby nepotřebují.
Výpis sdílených knihoven
Program ldd
vám vypíše všechny sdílené knihovny, na kterých program závisí.
linux-vdso.so.1 (0x00007fff83bb0000)
libsnumber.so => /usr/lib64/libsnumber.so (0x00007ffeb59f8000)
libc.so.6 => /lib64/libc.so.6 (0x00007ffeb5648000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffeb5c00000)
Vidíte že jsem nelhal, k programu smain
jsou přilinkované
knihovny libc, ld-linux i libsnumber.so. Knihovna linux-vdso.so je tu kvůli
nějakým systémovým voláním jádra, nic zajímavého :-).
Pokud knihovnu /usr/lib64/libsnumber.so smažete, dostanete takovýto výpis:
linux-vdso.so.1 (0x00007fff59298000)
libsnumber.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007ff7cc180000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff7cc530000)