Provided by: manpages-pl-dev_4.27.0-1_all 

NAZWA
dlclose, dlopen, dlmopen - otwiera i zamyka obiekt dzielony
BIBLIOTEKA
Biblioteka konsolidacji dynamicznej (libdl, -ldl)
SKŁADNIA
#include <dlfcn.h>
void *dlopen(const char *filename, int flags);
int dlclose(void *handle);
#define _GNU_SOURCE
#include <dlfcn.h>
void *dlmopen(Lmid_t lmid, const char *filename, int flags);
OPIS
dlopen()
Funkcja dlopen() ładuje dynamiczny obiekt dzielony (bibliotekę dzieloną) pliku, o nazwie zawartej w
zakończonym znakiem nul łańcuchu filename i zwraca nieprzezroczysty „uchwyt” dla tego obiektu dzielonego.
Uchwytu tego używa się z innymi funkcjami z API dlopen, takimi jak dlsym(3), dladdr(3), dlinfo(3) i
dlclose().
Jeśli filename wynosi NULL, to zwracany jest uchwyt do głównego programu. Jeśli filename zawiera ukośnik
(„/”), to jest interpretowana jako (względna lub absolutna) ścieżka. W innych sytuacjach, konsolidator
dynamiczny szuka obiektu w następujący sposób (więcej szczegółów w podręczniku ld.so(8)):
• (tylko ELF) Jeśli wywołujący obiekt (tj. biblioteka współdzielona, lub plik wykonywalny, z którego
wywoływane jest dlopen()) zawiera znacznik DT_RPATH, natomiast nie zawiera znacznika DT_RUNPATH, to
przeszukiwane są katalogi wypisane w znaczniku DT_RPATH.
• Jeśli, w momencie uruchomienia tego programu, zdefiniowano zmienną środowiskową LD_LIBRARY_PATH i
zawierała ona listę katalogów rozdzielonych dwukropkiem, to przeszukiwane są te katalogi (ze względów
bezpieczeństwa, zmienna ta jest ignorowana w przypadku programów z ustawionym bitem set-user-ID lub
set-group-ID).
• (tylko ELF) Jeśli wywołujący obiekt zawiera znacznik DT_RUNPATH, to przeszukiwane są katalogi wypisane
w tym znaczniku.
• Sprawdzany jest plik bufora /etc/ld.so.cache (zarządzany przez ldconfig(8)) pod kątem zawierania wpisu
dla filename.
• Przeszukiwane są katalogi /lib i /usr/lib (w tej kolejności).
Jeśli obiekt określony w filename ma zależności od innych obiektów dzielonych, są one również
automatycznie ładowane przez konsolidator dynamiczny, za pomocą tych samych reguł (proces ten może
postępować rekurencyjnie, jeśli te obiekty mają z kolei swoje zależności itd.).
We flags należy podać jedną z dwóch poniższych wartości:
RTLD_LAZY
Przeprowadza leniwe dowiązanie. Rozwiązuje symbole jedynie wtedy, gdy wykonywany jest kod, który
się do nich odnosi. Jeśli do danego symbolu nic się nie odniesie, to nie zostanie nigdy rozwiązany
(leniwe dowiązanie jest wykonywane tylko przy odniesieniach do funkcji; odniesienia do zmiennych
są zawsze bezzwłocznie przypisywane przy ładowaniu obiektu dzielonego). Od glibc 2.1.1, znacznik
ten jest przesłaniany przez efekt zmiennej środowiskowej LD_BIND_NOW.
RTLD_NOW
Jeśli poda się tę wartość lub zmienna środowiskowa LD_BIND_NOW jest ustawiona na niepusty łańcuch,
wszystkie niezdefiniowane symbole obiektu dzielonego są rozwiązywane przed powrotem dlopen().
Jeśli nie da się tego wykonać, zwracany jest błąd.
Ponadto, we flags można zsumować (OR) zero lub więcej z poniższych wartości:
RTLD_GLOBAL
Symbole zdefiniowane za pomocą tego obiektu dzielonego zostaną udostępnione do rozwiązywania
symboli kolejno ładowanych obiektów dzielonych.
RTLD_LOCAL
Jest to odwrotność RTLD_GLOBAL, a także wartość domyślna, jeśli nie podano żadnego znacznika.
Symbole zdefiniowane w tym obiekcie dzielonym nie są udostępniane do rozwiązywania symboli kolejno
ładowanych obiektów dzielonych.
RTLD_NODELETE (od glibc 2.2)
Nie odłącza obiektu dzielonego podczas dlclose(). Statyczne i globalne zmienne obiektu nie są
zatem inicjowane ponownie, jeśli obiekt zostanie później przeładowany za pomocą dlopen().
RTLD_NOLOAD (od glibc 2.2)
Nie ładuje obiektu dzielonego. Można to wykorzystać do sprawdzenia, czy obiekt jest już rezydentny
(dlopen() zwróci NULL jeśli nie jest, albo uchwyt obiektu jeśli jest). Znacznik może posłużyć
również do dodania znaczników do już załadowanego obiektu dzielonego. Przykładowo, wcześniej
załadowany obiekt dzielony ze znacznikiem RTLD_LOCAL może być otwarty ponownie z
RTLD_NOLOAD | RTLD_GLOBAL.
RTLD_DEEPBIND (od glibc 2.3.4)
Umieszcza dziedzinę przeszukiwania dla symboli, w tym obiekcie dzielonym przed dziedziną globalną.
Oznacza to, że samodzielny obiekt będzie preferował używanie swoich symboli, zamiast symboli
globalnych o tej samej nazwie w obiektach, które zostały już załadowane.
Jeśli filename wynosi NULL, to zwracany jest uchwyt do głównego programu. Przy przekazaniu tego uchwytu
do dlsym(3), powoduje on wyszukiwanie symbolu w głównym programie, następnie we wszystkich obiektach
dzielonych załadowanych przy rozruchu programu, a potem we wszystkich obiektach dzielonych załadowanych
za pomocą dlopen() ze znacznikiem RTLD_GLOBAL.
Odniesienia symboli w obiekcie dzielonym są rozwiązywane za pomocą (w tej kolejności): symboli w mapie
linkowań obiektów załadowanych do głównego programu i jego zależności; symboli w obiektach dzielonych (i
ich zależności), które były uprzednio otworzone za pomocą dlopen() i znacznika RTLD_GLOBAL; definicji w
samym obiekcie dzielonym (i zależności, które zostały załadowane dla tego obiektu).
Wszelkie symbole globalne w pliku wykonywalnym, które zostały umieszczone w jego tablicy symboli
dynamicznych przez ld(1), mogą być użyte również do rozwiązywania odniesień w dynamicznie załadowanym
obiekcie dzielonym. Symbole mogą być umieszczone w tablicy symboli dynamicznych albo ponieważ plik
wykonywalny skonsolidowano z „-rdynamic” (lub synonimicznie: z „--export-dynamic”), co powoduje
umieszczenie wszystkich symboli globalnych pliku wykonywalnego w tablicy symboli dynamicznych, albo z
powodu odnotowania przez ld(1) zależności od symbolu w innym obiekcie, podczas konsolidowania
statycznego.
Jeśli ten sam obiekt dzielony zostanie powtórnie otworzony za pomocą dlopen(), to zwracany jest ten sam
uchwyt obiektu. Dynamiczny konsolidator utrzymuje liczniki odniesień dla uchwytów obiektów, tak więc
dynamicznie załadowany obiekt dzielony nie jest dealokowany, dopóki dlclose() nie zostanie na nim
wywołany tak wiele razy, jak udanie wywołano na nim dlopen(). Konstruktory (zob. niżej) są wywoływane
tylko wtedy, gdy obiekt jest faktycznie ładowany do pamięci (tj. gdy licznik odniesień wzrośnie do 1).
Kolejne wywołanie dlopen(), ładujące ten sam obiekt za pomocą RTLD_NOW, może wymusić rozwiązanie symbolu
dla obiektu dzielonego wcześniej załadowanego za pomocą RTLD_LAZY. Podobnie, obiekt uprzednio załadowany
za pomocą RTLD_LOCAL może awansować na RTLD_GLOBAL w kolejnym wywołaniu dlopen().
Jeśli dlopen() z jakiegoś powodu zawiedzie, zwróci NULL.
dlmopen()
Funkcja ta wykonuje te same zadania co dlopen() — argumenty filename i flags oraz wartości zwraca są
takie same, z wyjątkiem różnic opisanych niżej.
Funkcja dlmopen() różni się od dlopen() głównie tym, że akceptuje dodatkowy argument, lmid, który określa
listę map linkowań (link-map) (do której można odnosić się również jako namespace), w której obiekt
dzielony powinien być załadowany (dla porównania, dlopen() dodaje dynamicznie ładowany obiekt dzielony do
tej samej przestrzeni nazw, jak obiekt dzielony, z którego wywołano dlopen()). Typ Lmid_t jest
nieprzezroczystym uchwytem odnoszącym się do tej przestrzeni nazw.
Argument lmid jest albo identyfikatorem istniejącej przestrzeni nazw (który można pozyskać za pomocą
żądania RTLD_DI_LMID dlinfo(3)) lub jedną z następujących wartości specjalnych:
LM_ID_BASE
Ładuje obiekt dzielony w pierwotnej przestrzeni nazw (tj. w przestrzeni nazw aplikacji).
LM_ID_NEWLM
Tworzy nową przestrzeń nazw i ładuje w niej obiekt dzielony. Obiekt musi być prawidłowo
skonsolidowany do odniesień wszystkich innych obiektów dzielonych jakich wymaga, ponieważ nowa
przestrzeń nazw jest początkowo pusta.
Jeśli filename wynosi NULL, to jedyną dopuszczalną wartością lmid jest LM_ID_BASE.
dlclose()
Funkcja dlclose() zmniejsza licznik odniesień na dynamicznie załadowanych obiektach, do których odnosi
się handle.
Jeśli licznik odniesień obiektu spadnie do zera, a żadne symbole w tym obiekcie nie są wymagane przez
inne obiekty, obiekt ten jest odłączany po pierwszym wywołaniu dowolnych destruktorów zdefiniowanych dla
obiektu (symbole w obiekcie mogą być wymagane przez inny obiekt, ponieważ otworzono go ze znacznikiem
RTLD_GLOBAL, a jeden z jego symboli umożliwił relokację w innym obiekcie).
Wszystkie obiekty dzielone załadowane automatycznie, gdy dlopen() przywołano na obiekcie, do którego
odnosi się handle, są rekurencyjnie zamykane w ten sam sposób.
Pomyślny powrót z dlclose() nie gwarantuje, że symbole powiązane z handle są usunięte z przestrzeni
adresowej wywołującego. Oprócz odniesień wynikających z bezpośrednich wywołań dlopen(), mogły zostać
niebezpośrednio załadowane obiekty dzielone (i policzone odniesienia), ze względu na zależności w innych
obiektach dzielonych. Tylko gdy wszystkie odniesienia zostały uwolnione, obiekt dzielony może być
usunięty z przestrzeni adresowej.
WARTOŚĆ ZWRACANA
W przypadku powodzenia, dlopen() i dlmopen() zwracają uchwyt do ładowanego obiektu, niebędący NULL-em. W
razie wystąpienia błędu (nie można znaleźć pliku, nie był odczytywalny, miał zły format lub spowodował
błędy przy załadowaniu) funkcje te zwracają NULL.
W przypadku powodzenia, dlclose() zwraca 0; w razie wystąpienia błędu, zwraca wartość niezerową.
Błędy z opisywanych funkcji można diagnozować za pomocą dlerror(3).
ATRYBUTY
Informacje o pojęciach używanych w tym rozdziale można znaleźć w podręczniku attributes(7).
┌──────────────────────────────────────────────────────────────┬────────────────────────┬───────────────┐
│ Interfejs │ Atrybut │ Wartość │
├──────────────────────────────────────────────────────────────┼────────────────────────┼───────────────┤
│ dlopen(), dlmopen(), dlclose() │ Bezpieczeństwo wątkowe │ MT-bezpieczne │
└──────────────────────────────────────────────────────────────┴────────────────────────┴───────────────┘
STANDARDY
dlopen()
dlclose()
POSIX.1-2008.
dlmopen()
RTLD_NOLOAD
RTLD_NODELETE
GNU.
RTLD_DEEPBIND
Solaris.
HISTORIA
dlopen()
dlclose()
glibc 2.0. POSIX.1-2001.
dlmopen()
glibc 2.3.4.
UWAGI
dlmopen() i przestrzenie nazw
Lista map linków (link-map) definiuje izolowaną przestrzeń nazw do rozwiązywania symboli przez
konsolidator dynamiczny. Wewnątrz przestrzeni nazw, zależny obiekt dzielony jest niebezpośrednio ładowany
zgodnie ze zwykłymi regułami, a odniesienia do symboli są podobnie rozwiązywane zgodnie ze zwykłymi
regułami, lecz rozwiązywanie to jest ograniczone do definicji zapewnionych przez obiekty, które zostały
(bezpośrednio lub niebezpośrednio) załadowane do tej przestrzeni nazw.
Funkcja dlmopen() pozwala na izolację załadowań obiektów — możliwość załadowania obiektu dzielonego do
nowej przestrzeni nazw, bez ujawniania reszcie aplikacji symboli udostępnionych przez nowy obiekt. Proszę
zauważyć, że użycie znacznika RTLD_LOCAL nie jest wystarczające do tego celu, ponieważ zapobiega ono
udostępnieniu symboli obiektu dzielonego dla wszystkich innych obiektów dzielonych. W niektórych
przypadkach, zachodzi potrzeba udostępnienia symboli zapewnionych przez dynamicznie załadowany obiekt
dzielony dla (podzbioru) innych obiektów dzielonych, bez ujawniania tych symboli całej aplikacji. Można
to uzyskać za pomocą oddzielnej przestrzeni nazw i znacznika RTLD_GLOBAL.
Funkcja dlmopen() może służyć również do zapewnienia lepszej izolacji, niż znacznik RTLD_LOCAL. W
szczególności, obiekty dzielone załadowane za pomocą RTLD_LOCAL mogą być awansowane do RTLD_GLOBAL, jeśli
są zależnościami innego obiektu dzielonego załadowanego ze znacznikiem RTLD_GLOBAL. Z tego względu,
RTLD_LOCAL jest niewystarczające do izolowania załadowanego obiektu dzielonego, z wyjątkiem (nieczęstej)
sytuacji, gdy ma się całkowitą kontrolę nad wszystkich zależnościami obiektu dzielonego.
Możliwe zastosowania dlmopen() to wtyczki, gdzie autor struktury ładowania wtyczek nie może ufać autorom
wtyczek i nie chce, aby jakiekolwiek niezdefiniowane symbole ze struktury wtyczek były rozwiązywane do
symboli wtyczek. Kolejne zastosowanie, to ładowanie tego samego obiektu więcej niż raz. Bez korzystania z
dlmopen(), wymagałoby to utworzenia odrębnej kopii pliku obiektu dzielonego. Za pomocą dlmopen(), można
to uzyskać ładując ten sam plik obiektu dzielonego do różnych przestrzeni nazw.
Implementacja glibc obsługuje co najwyżej 16 przestrzeni nazw.
Funkcje inicjalizacji i kończenia
Obiekty dzielone mogą eksportować funkcje za pomocą atrybutów funkcji __attribute__((constructor)) i
__attribute__((destructor)). Funkcje konstrukcyjne są wykonywane przed powrotem dlopen(), a funkcje
destrukcyjne są wykonywane przez powrotem dlclose(). Obiekty dzielone mogą eksportować wiele
konstruktorów i destruktorów, a każdej funkcji można przypisać priorytety, aby określić kolejność
wykonywania. Więcej informacji na stronach info gcc (w rozdziale „Function attributes”).
Starszą metodą uzyskiwania (częściowo) tego samego wyniku jest użycie dwóch symboli specjalnych
rozpoznawanych przez konsolidator: _init i _fini. Jeśli dynamicznie ładowany obiekt dzielony eksportuje
algorytm nazwany _init(), to kod ten jest wykonywany po załadowaniu obiektu dzielonego, przed powrotem
dlopen(). Jeśli obiekt dzielony eksportuje algorytm nazwany _fini(), to jest on wywoływany zaraz przed
odłączeniem obiektu. W tym przypadku, konieczne jest unikania linkowania wobec rozruchowych plików
systemowych, które zawierają domyślne wersje tych plików; można to uczynić za pomocą opcji wiersza
poleceń gcc(1) -nostartfiles.
Używanie _init i _fini jest obecnie przestarzałe, na korzyść opisanych wcześniej konstruktorów i
destruktorów, które oprócz innych zalet, pozwalają na zdefiniowanie wielu funkcji inicjalizacji i
kończenia.
Od glibc 2.2.3, atexit(3) może posłużyć do zarejestrowania uchwytu wyjścia, który jest automatycznie
wywoływany przez odłączaniu obiektu dzielonego.
Historia
Funkcje te są częścią API dlopen, wywodzącego się z SunOS.
USTERKI
Według stanu na glibc 2.24, podanie znacznika RTLD_GLOBAL przy wywoływaniu dlmopen() generuje błąd. Co
więcej, podanie RTLD_GLOBAL przy wywoływaniu dlopen() powoduje załamanie programu (SIGSEGV), jeśli
wywołanie nastąpiło z dowolnego obiektu załadowanego w przestrzeni nazw innej niż pierwotna przestrzeń
nazw.
PRZYKŁADY
Poniższy program ładuje bibliotekę matematyczną (glibc), szuka adresu funkcji cos(3) i wypisuje cosinus
liczby 2.0. Pokazano przykład tworzenia i działania programu:
$ cc dlopen_demo.c -ldl
$ ./a.out
-0.416147
Kod źródłowy programu
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <gnu/lib-names.h> /* Definiuje LIBM_SO (będzie to
łańcuch podobny do "libm.so.6") */
int
main(void)
{
void *handle;
double (*cosine)(double);
char *error;
handle = dlopen(LIBM_SO, RTLD_LAZY);
if (!handle) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror(); /* Czyści ewentualne istniejące błędy */
cosine = (double (*)(double)) dlsym(handle, "cos");
/* Zgodnie ze standardem ISO C, rzutowanie pomiędzy wskaźnikami
funkcji a 'void *', jak poniżej, daje niezdefiniowany wynik.
POSIX.1-2001 i POSIX.1-2008 akceptują ten stan rzeczy i proponują
następujące obejście problemu:
*(void **) (&cosine) = dlsym(handle, "cos");
To (niezgrabne) rzutowanie jest zgodne ze standem ISO C i uniknie
ostrzeżeń kompilatora.
2013 Technical Corrigendum 1 do POSIX.1-2008 poprawia stan rzeczy,
wymagając od zgodnych implementacji obsługi rzutowań 'void *'
do wskaźnika funkcji. Tym niemniej, niektóre kompilatory (np. gcc
z opcją '-pedantic') mogą narzekać na rzutowanie użyte
w tym programie. */
error = dlerror();
if (error != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
printf("%f\n", (*cosine)(2.0));
dlclose(handle);
exit(EXIT_SUCCESS);
}
ZOBACZ TAKŻE
ld(1), ldd(1), pldd(1), dl_iterate_phdr(3), dladdr(3), dlerror(3), dlinfo(3), dlsym(3), rtld-audit(7),
ld.so(8), ldconfig(8)
strony info gcc, strony info ld
TŁUMACZENIE
Tłumaczenie niniejszej strony podręcznika: Przemek Borys <pborys@dione.ids.pl>, Andrzej Krzysztofowicz
<ankry@green.mf.pg.gda.pl> i Michał Kułach <michal.kulach@gmail.com>
Niniejsze tłumaczenie jest wolną dokumentacją. Bliższe informacje o warunkach licencji można uzyskać
zapoznając się z GNU General Public License w wersji 3 lub nowszej. Nie przyjmuje się ŻADNEJ
ODPOWIEDZIALNOŚCI.
Błędy w tłumaczeniu strony podręcznika prosimy zgłaszać na adres listy dyskusyjnej manpages-pl-
list@lists.sourceforge.net.
Linux man-pages 6.9.1 15 czerwca 2024 r. dlopen(3)