dlopen
Table des matières
Retour à l'index
NOM
dlclose, dlopen, dlmopen - Ouvrir et fermer un objet partagé
BIBLIOTHÈQUE
Bibliothèque de liens dynamiques (libc, -lc)
SYNOPSIS
#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);
DESCRIPTION
dlopen()
La fonction dlopen() charge la bibliothèque dynamique dont le nom est
fourni dans la chaîne filename (terminée par l'octet NULL) et renvoie un
descripteur opaque (« handle ») représentant la bibliothèque dynamique. Ce
descripteur est utilisé avec d'autres fonctions dans l'API dlopen, telles
que dlsym(3), dladdr(3), dlinfo(3) et dlclose().
Si l'argument filename est un pointeur NULL, le descripteur renvoyé
correspond au programme principal. Si filename contient une barre oblique
(« / »), il est interprété comme un chemin (relatif ou absolu). Autrement,
l'éditeur dynamique de liens cherche la bibliothèque de la façon suivante
(consultez ld.so(8) pour plus de détails) :
- -
-
(ELF seulement) Si l'objet appelant (c'est-à-dire la bibliothèque partagée
ou l'exécutable depuis lequel dlopen() est appelée) contient la balise
DT_RPATH mais pas la balise DT_RUNPATH, les répertoires listés dans la
balise DT_RPATH seront parcourus.
- -
-
Si à l'instant où le programme est démarré, la variable d'environnement
LD_LIBRARY_PATH est définie et contient une liste de répertoires séparés
par des deux-points « : », ces répertoires seront parcourus. Par mesure de
sécurité, cette variable est ignorée dans le cas de programmes set-user-ID
et set-group-ID.
- -
-
(ELF seulement) Si l'objet appelant contient la balise DT_RUNPATH, les
répertoires listés dans cette balise seront parcourus.
- -
-
Le fichier de cache /etc/ld.so.cache (entretenu par ldconfig(8)) est
vérifié pour voir s'il contient une entrée correspondant à filename.
- -
-
Les répertoires /lib et /usr/lib sont parcourus (dans cet ordre).
Si l'objet indiqué dans filename a des dépendances sur d'autres objets
partagés, ceux-ci seront automatiquement chargés par l'éditeur dynamique de
liens, en utilisant les mêmes règles. Le processus peut être récursif si ces
objets ont, à leur tour, des dépendances, et ainsi de suite.
L'une des deux valeurs suivantes doit être incluse dans flag :
- RTLD_LAZY
-
Effectuer des liaisons paresseuses. Ne résoudre les symboles que lorsque le
code qui les référence est exécuté. Si le symbole n'est jamais référencé,
alors il n'est jamais résolu. Les liaisons paresseuses ne sont effectuées
que pour les références de fonctions ; les références de variables sont
toujours immédiatement liées quand l'objet partagé est chargé. Depuis la
version 2.1.1 de la glibc, ce drapeau est supplanté par l'effet de la
variable d'environnment LD_BIND_NOW.
- RTLD_NOW
-
Si cette valeur est spécifiée ou si la variable d'environnement
LD_BIND_NOW est définie avec une chaîne non vide, tous les symboles non
définis de l'objet partagé sont résolus avant le retour de dlopen(). Si
cela ne peut pas être fait, une erreur est renvoyée.
Zéro ou plusieurs des valeurs suivantes peuvent être spécifiées avec un OU
binaire dans flag :
- RTLD_GLOBAL
-
Les symboles définis par cet objet partagé seront rendus disponibles pour la
résolution des symboles des objets partagés chargés ultérieurement.
- RTLD_LOCAL
-
C'est la réciproque de RTLD_GLOBAL et le comportement par défaut si aucun
des drapeaux n'est spécifié. Les symboles définis dans cet objet partagé ne
sont pas rendus disponibles pour résoudre les références des objets partagés
chargés ultérieurement.
- RTLD_NODELETE (depuis la glibc 2.2)
-
Ne pas décharger l'objet partagé lors de dlclose(). En conséquence, les
variables statiques de l'objet ne sont pas réinitialisées si l'objet est
chargé ultérieurement avec dlopen().
- RTLD_NOLOAD (depuis la glibc 2.2)
-
Ne pas charger l'objet partagé. Cela peut être utilisé pour tester si
l'objet partagé n'est pas déjà chargé (dlopen() renvoie NULL s'il n'est
pas chargé, ou le descripteur de l'objet partagé s'il est déjà chargé). Ce
drapeau peut aussi être utilisé pour promouvoir les drapeaux d'un objet
partagé déjà chargé. Par exemple, un objet partagé qui a été chargé avec
RTLD_LOCAL peut être de nouveau ouvert avec RTLD_NOLOAD | RTLD_GLOBAL.
- RTLD_DEEPBIND (depuis la glibc 2.3.4)
-
Placer l'espace de recherche des symboles de cet objet partagé avant
l'espace global. Cela signifie qu'un objet autonome utilisera de préférence
ses propres symboles aux symboles globaux de même noms contenus dans les
objets déjà chargés.
Si l'argument filename est un pointeur NULL, le descripteur renvoyé
correspond au programme principal. Lorsqu'il est passé à dlsym(), ce
descripteur provoque la recherche d'un symbole dans le programme principal,
puis dans tous les objets partagés chargés au démarrage du programme, puis
dans toutes les objets partagés chargés par dlopen() avec l'attribut
RTLD_GLOBAL.
Les références aux symboles de l'objet partagé sont résolues en utilisant
(dans l'ordre) : les symboles dans la table de liens des objets chargés pour
le programme principal et ses dépendances, les symboles dans les objets
partagés (et leurs dépendances) qui ont été ouverts par un appel antérieur à
dlopen() avec le drapeau RTLD_GLOBAL et les définitions dans l'objet
partagé lui-même (ainsi que toute dépendance ayant été chargée pour cet
objet).
Tout symbole global dans l'exécutable qui a été placé dans sa table de
symboles dynamiques par ld(1) peut aussi être utilisé pour résoudre les
références dans un objet partagé dynamiquement chargé. Les symboles peuvent
être placés dans la table de symbole soit parce que les liens de
l'exécutable ont été édités avec le drapeau « -rdynamic » (ou de façon
synonyme « --export-dynamic »), qui fait que tous les symboles globaux de
l'exécutable sont placés dans la table de symboles dynamiques, soit parce
que ld(1) a identifié une dépendance sur un symbole dans un autre objet
durant l'édition de liens statiques.
Si le même objet partagé est chargé une nouvelle fois avec dlopen(), le
même descripteur sera renvoyé. Un décompte du nombre de chargements est
toutefois conservé par l'éditeur dynamique de liens afin d'éviter de le
décharger avant que la fonction dlclose() n'ait été appelée autant de
fois que dlopen() a réussi. Les constructeurs (voir ci-dessous) sont
seulement appelés lorsque l'objet est réellement chargé en mémoire
(c'est-à-dire lorsque le compteur de références est augmenté et passe à 1).
Un appel ultérieur à dlopen() qui charge le même objet partagé avec
RTLD_NOW peut forcer la résolution de symboles pour un objet partagé
chargé antérieurement avec RTLD_LAZY. De façon similaire, un objet
préalablement ouvert avec RTLD_LOCAL peut être promu à RTLD_GLOBAL
lors d'un appel ultérieur à dlopen().
Si dlopen() échoue pour une raison quelconque, elle renvoie NULL.
dlmopen()
Cette fonction effectue la même tâche que dlopen() ; les arguments
filename et flags, de même que la valeur de renvoi, sont les mêmes à
l'exception des différences décrites plus bas.
La fonction dlmopen() diffère de la fonction dlopen() principalement
parce qu'elle accepte un argument supplémentaire, lmid, qui indique la
liste de tables de liens (aussi appelée espace de noms) dans laquelle
l'objet partagé doit être chargé. En comparaison, dlopen() ajoute l'objet
partagé dynamiquement chargé au même espace de noms que l'objet partagé pour
lequel l'appel dlopen() est fait. Le type Lmid_t est un gestionnaire
opaque qui fait référence à un espace de noms.
L'argument lmid est soit l'ID d'un espace de noms existant (pouvant être
obtenu en utilisant la requête dlinfo(3) RTLD_DI_LMID) ou l'une des
valeurs spéciales suivantes :
- LM_ID_BASE
-
Charger l'objet partagé dans l'espace de noms initial (c'est-à-dire l'espace
de noms de l'application).
- LM_ID_NEWLM
-
Créer un nouvel espace de noms et y charger l'objet partagé. Les liens de
l'objet doivent avoir été liés pour référencer tous les autres objets
partagés dont il a besoin puisque l'espace de noms est initialement vide.
Si filename est vide, alors l'unique valeur autorisée pour lmid est
LM_ID_BASE.
dlclose()
La fonction dlclose() décrémente le compteur de références de l'objet
partagé chargé dynamiquement et indiqué par handle.
Si le compteur de références de cet objet tombe en dessous de zéro et
qu'aucun symbole dans cet objet n'est requis par un autre objet, alors
l'objet est déchargé après avoir appelé tous les destructeurs définis pour
l'objet. Des symboles dans cet objet peuvent être requis par un autre objet
parce qu'il a été ouvert avec le drapeau RTLD_GLOBAL et que l'un de ses
symboles a permis une relocalisation dans un autre objet.
Tous les objets partagés qui ont été chargés automatiquement lorsque
dlopen() a été invoquée sur l'objet référencé par handle sont fermés
récursivement de la même façon.
Un renvoi réussi de dlclose() ne garantit que les symboles associés avec
handle sont supprimés de l'espace d'adressage de l'appelant. En plus de
références résultant d'appels explicites à dlopen(), un objet partagé a
peut-être été chargé de façon implicite (et les références prises en compte)
à cause de références dans d'autres objets partagés. Ce n'est que lorsque
toutes les références sont relachées que l'objet partagé peut être supprimé
de l'espace d'adressage.
VALEUR RENVOYÉE
En cas de succès, dlopen() et dlmopen() renvoient un gestionnaire non
nul pour l'objet chargé. En cas d'erreur (le fichier ne peut pas être
trouvé, il n'est pas lisible, a le mauvais format ou bien a provoqué des
erreurs lors de son chargement), ces fonctions renvoient NULL.
En cas de succès, dlclose() renvoie 0, en cas d'erreur une valeur non
nulle est renvoyée.
Les erreurs de ces fonctions peuvent être diagnostiquées en utilisant
dlerror(3).
ATTRIBUTS
Pour une explication des termes utilisés dans cette section, consulter
attributes(7).
| Interface | Attribut | Valeur
|
|
dlopen(),
dlmopen(),
dlclose()
| Sécurité des threads | MT-Safe
|
STANDARDS
- dlopen()
-
dlclose()
POSIX.1-2008.
- dlmopen()
-
RTLD_NOLOAD
RTLD_NODELETE
GNU.
- RTLD_DEEPBIND
-
Solaris.
HISTORIQUE
- dlopen()
-
dlclose()
glibc 2.0. POSIX.1-2001.
- dlmopen()
-
glibc 2.3.4.
NOTES
dlmopen() et espace de noms
Une liste de table de liens définit un espace de noms isolé pour la
résolution de symboles par l'éditeur dynamique de liens. À l'intérieur d'un
espace de noms, les objets partagés dépendants sont implicitement chargés
selon les règles usuelles, et les références aux symboles sont résolues
selon les règles usuelles, mais un telle résolution est limitée aux
définitions fournies aux objets qui ont été chargés (explicitement et
implicitement) dans l'espace de noms.
La fonction dlmopen() permet une isolation de chargement d'objet,
c'est-à-dire la capacité à charger un objet partagé dans un nouvel espace de
noms sans exposer le reste de l'application aux symboles rendus disponibles
par le nouvel objet. Notez que l'utilisation du drapeau RTLD_LOCAL n'est
pas suffisante pour réaliser cela puisque qu'il empêche les symboles des
objets partagés d'être disponibles à tout autre objet partagé. Dans
certains cas, il peut être souhaitable de rendre les symboles fournis par un
objet partagé chargé dynamiquement disponibles à d'autres objets (ou à un
sous-ensemble) partagés sans exposer ces symboles à l'application
entière. Cela peut être réalisé par l'utilisation d'un espace de noms séparé
et du drapeau RTLD_GLOBAL.
La fonction dlmopen() peut également être utilisée pour fournir une
meilleure isolation que le drapeau RTLD_LOCAL. En particulier, les objets
partagés chargés avec RTLD_LOCAL peuvent être promus à RTLD_GLOBAL
s'ils sont des dépendances d'un autre objet partagé chargé avec
RTLD_GLOBAL mis à part dans le cas (peu commun) où l'on a un contrôle
explicite sur les dépendances de tous les objets partagés.
Les cas possibles d'utilisation de dlmopen() sont des greffons où
l'auteur du cadriciel de chargement de greffon ne peut pas faire confiance
aux auteurs du greffon et ne souhaite pas que des symboles non définis du
cadriciel greffon soient résolus en symboles du greffon. Une autre
utilisation est de charger le même objet plus d'une fois. Sans l'utilisation
de dlmopen(), cela exigerait la création de copies distinctes du fichier
de l'objet partagé. Grâce à l'utilisation de dlmopen(), cela peut être
réalisé par le chargement du même fichier d'objet partagé dans différents
espaces de noms.
L'implémentation de la glibc prend en charge un nombre maximal de 16 espaces
de noms.
Fonctions d'initialisation et de finalisation
Les objets partagés peuvent exporter des fonctions en utilisant les
attributs de fonction __attribute__((constructor)) et
__attribute__((destructor)). Les fonctions de construction sont exécutées
avant que dlopen() ne renvoie, et les fonctions de destruction sont
exécutées avant que dlclose() ne renvoie. Un objet partagé peut exporter
plusieurs constructeurs et destructeurs et des priorités peuvent être
associées à chaque fonction pour déterminer l'ordre dans lequel elles
s'exécutent. Consultez les pages d'information de gcc (sous « Attributs
de fonction ») pour plus d'informations.
Une méthode plus ancienne d'obtenir (partiellement) le même résultat passe
par l'utilisation de deux symboles spéciaux reconnus par l'éditeur de
liens : _init et _fini. Si un objet partagé chargé dynamiquement
exporte une routine nommée _init(), alors son code est exécuté après le
chargement d'un objet partagé, avant le retour de dlopen(). Si l'objet
partagé exporte une routine nommée _fini, elle est appelée juste avant le
déchargement de l'objet. Dans ce cas, vous voudrez éviter de lier
l'exécutable avec les fichiers de démarrage du système, qui contiennent des
versions par défaut de ces fichiers ; pour cela, vous pouvez spécifier
l'option -nostartfiles à la ligne de commande de gcc(1).
L'utilisation de _init et _fini est rendue obsolète en faveur des
constructeurs et destructeurs susmentionnés, ce qui entre autres avantages,
permet la définition de plusieurs fonctions d'initialisation et de
finalisation.
Depuis la glibc 2.2.3, atexit(3) peut être utilisée pour enregistrer un
gestionnaire de sortie qui sera automatiquement appelé quand un objet
partagé est déchargé.
Historique
Ces fonctions font partie de l'API dlopen, dérivée de SunOS.
BOGUES
Pour la version 2.24 de la glibc, spécifier le drapeau RTLD_GLOBAL lors
de l'appel à dlmopen() génère une erreur. De plus, spécifier
RTLD_GLOBAL lors d'un appel à dlopen() résulte en un plantage du
programme (SIGSEGV) si l'appel est effectué depuis n'importe quel objet
chargé dans un autre espace de noms que celui initial.
EXEMPLES
Le programme suivant charge la bibliothèque de maths (de la glibc),
recherche l'adresse de la fonction cos(3) et affiche le cosinus
de 2.0. Ci-dessous, un exemple de construction et d'exécution du programme :
$ cc dlopen_demo.c -ldl
$ ./a.out
-0.416147
Source du programme
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <gnu/lib-names.h> /* Defines LIBM_SO (which will be a
string such as "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(); /* Clear any existing error */
cosine = (double (*)(double)) dlsym(handle, "cos");
/* According to the ISO C standard, casting between function
pointers and 'void *', as done above, produces undefined results.
POSIX.1-2001 and POSIX.1-2008 accepted this state of affairs and
proposed the following workaround:
*(void **) (&cosine) = dlsym(handle, "cos");
This (clumsy) cast conforms with the ISO C standard and will
avoid any compiler warnings.
The 2013 Technical Corrigendum 1 to POSIX.1-2008 improved matters
by requiring that conforming implementations support casting
'void *' to a function pointer. Nevertheless, some compilers
(e.g., gcc with the '-pedantic' option) may complain about the
cast used in this program. */
error = dlerror();
if (error != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
printf("%f\n", (*cosine)(2.0));
dlclose(handle);
exit(EXIT_SUCCESS);
}
VOIR AUSSI
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)
pages Info de ld, pages Info de gcc
TRADUCTION
La traduction française de cette page de manuel a été créée par
Christophe Blaess <https://www.blaess.fr/christophe/>,
Stéphan Rafin <stephan.rafin@laposte.net>,
Thierry Vignaud <tvignaud@mandriva.com>,
François Micaux,
Alain Portal <aportal@univ-montp2.fr>,
Jean-Philippe Guérard <fevrier@tigreraye.org>,
Jean-Luc Coulon (f5ibh) <jean-luc.coulon@wanadoo.fr>,
Julien Cristau <jcristau@debian.org>,
Thomas Huriaux <thomas.huriaux@gmail.com>,
Nicolas François <nicolas.francois@centraliens.net>,
Florentin Duneau <fduneau@gmail.com>,
Simon Paillard <simon.paillard@resel.enst-bretagne.fr>,
Denis Barbier <barbier@debian.org>,
David Prévot <david@tilapin.org>
et
Grégoire Scano <gregoire.scano@malloc.fr>
Cette traduction est une documentation libre ; veuillez vous reporter à la
GNU General Public License version 3
concernant les conditions de copie et
de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.
Si vous découvrez un bogue dans la traduction de cette page de manuel,
veuillez envoyer un message à
Index
- NOM
-
- BIBLIOTHÈQUE
-
- SYNOPSIS
-
- DESCRIPTION
-
- dlopen()
-
- dlmopen()
-
- dlclose()
-
- VALEUR RENVOYÉE
-
- ATTRIBUTS
-
- STANDARDS
-
- HISTORIQUE
-
- NOTES
-
- dlmopen() et espace de noms
-
- Fonctions d'initialisation et de finalisation
-
- Historique
-
- BOGUES
-
- EXEMPLES
-
- Source du programme
-
- VOIR AUSSI
-
- TRADUCTION
-
This document was created by
man2html,
using the manual pages.
Time: 05:06:11 GMT, September 19, 2025