NOM
dladdr, dlclose, dlerror, dlopen, dlsym, dlvsym - Interface de
programmation pour le chargeur de bibliothèques dynamiques
SYNOPSIS
#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
char *dlerror(void);
void *dlsym(void *handle, const char *symbol);
int dlclose(void *handle);
Effectuez l’édition des liens avec l’option -ldl.
Les quatre fonctions dlopen(), dlsym(), dlclose(), dlerror()
implémentent l’interface pour le chargeur de bibliothèques dynamiques.
dlerror()
La fonction dlerror() renvoie une chaîne de caractères, compréhensible
par l’homme, décrivant la dernière erreur survenue dans dlopen(),
dlsym() ou dlclose() depuis le dernier appel à dlerror(). Elle renvoie
NULL si aucune erreur n’est survenue depuis l’initialisation ou depuis
son dernier appel.
dlopen()
La fonction dlopen() charge la bibliothèque dynamique dont le nom est
fourni dans la chaîne filename (terminée par un caractère nul) et
renvoie un descripteur opaque (« handle ») représentant la bibliothèque
dynamique. 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, le chargeur dynamique cherche la bibliothèque de la
façon suivante (voyez ld.so(8) pour plus de détails) :
o (ELF seulement) si le fichier exécutable pour le programme appelant
contient la balise DT_RPATH mais pas la balise DT_RUNPATH, les
répertoires listés dans la balise DT_RPATH seront parcourus.
o 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-UID et set-GID).
o (ELF seulement) si le fichier exécutable pour le programme appelant
contient la balise DT_RUNPATH, les répertoires listés dans cette
balise seront parcourus.
o Le fichier de cache /etc/ld.so.cache (maintenu par ldconfig(8)) est
vérifié pour voir s’il contient une entrée correspondant à
filename.
o Les répertoires /lib et /usr/lib sont parcourus (dans cet ordre).
Si la bibliothèque a des dépendances sur d’autres bibliothèques
partagées, celles-ci seront automatiquement chargées par le chargeur
dynamique, en utilisant les mêmes règles. (Le processus peut être
récursif si ces bibliothèques 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. Résoudre seulement les
symboles dont 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 bindings paresseux ne sont seulement effectués que pour les
références de fonctions ; les références de variables sont
toujours immédiatement liées quand la bibliothèque est chargée).
RTLD_NOW
Si cette valeur est spécifiée, ou que la variable
d’environnement LD_BIND_NOW est définie avec une chaîne non
vide, tous les symboles non définis de la bibliothèque 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 cette bibliothèque seront disponibles
pour la résolution des symboles des futurs chargements de
bibliothèques.
RTLD_LOCAL
C’est la réciproque de RTLD_GLOBAL, et le comportement par
défaut si aucun drapeau n’est spécifié. Les symboles définis
dans cette bibliothèque ne sont pas disponibles pour résoudre
les références des chargement de bibliothèques futurs.
RTLD_NODELETE (depuis la glibc 2.2)
Ne pas décharger la bibliothèque lors de dlclose(). En
conséquence, les variables statiques de la bibliothèque ne sont
pas ré-initialisées si la bibliothèque est chargée
ultérieurement avec dlopen() . Ce drapeau n’est pas spécifié
dans POSIX.1-2001.
RTLD_NOLOAD (depuis la glibc 2.2)
Ne pas charger la bibliothèque. Ceci peut être utilisé pour
tester si la bibliothèque n’est pas déjà chargée (dlopen()
renvoie NULL si elle n’est pas chargée, ou le descripteur de la
bibliothèque si elle déjà chargée). Ce drapeau peut aussi être
utilisé pour promouvoir les drapeaux d’une bibliothèque déjà
chargée. Par exemple, une bibliothèque qui a été chargée avec
RTLD_LOCAL peut être de nouveau ouverte avec
RTLD_NOLOAD |RTLD_GLOBAL. Ce drapeau n’est pas spécifié dans
POSIX.1-2001.
RTLD_DEEPBIND (depuis la glibc 2.3.4)
Placer l’espace de recherche des symboles de cette bibliothèque
avant l’espace global. Cela signifie qu’une bibliothèque
autonome utilisera ses propres symboles de préférence aux
symboles globaux de même noms contenus dans les bibliothèques
déjà chargées. Ce drapeau n’est pas spécifié dans POSIX.1-2001.
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 toutes les bibliothèques partagées chargées au
démarrage du programme, puis dans toutes les bibliothèques partagées
chargées par dlopen() avec l’attribut RTLD_GLOBAL.
Les références externes de la bibliothèque sont résolues en utilisant
les bibliothèques mentionnées dans sa liste de dépendances, et toutes
les autres bibliothèques éventuellement ouvertes auparavant avec le
drapeau RTLD_GLOBAL. Si l’édition des liens de l’exécutable a été faite
avec l’option « -rdynamic » (ou, de manière synonyme, avec
« --export-dynamic»), alors les symboles globaux du programme seront
également utilisés pour résoudre les références d’une bibliothèque
chargée dynamiquement.
Si la même bibliothèque est chargée une nouvelle fois avec dlopen(), le
même descripteur sera renvoyé. Un compte du nombre de chargements est
toutefois conservé afin d’éviter de la décharger avant que la fonction
dlclose() n’ait été appelée autant de fois que dlopen() a réussi. La
routine _init, si elle existe, est appelée une seule fois. Mais un
appel postérieur avec RTLD_NOW peut forcer la résolution des symboles
pour une bibliothèque précédemment chargée avec RTLD_LAZY.
Si dlopen() échoue pour une raison quelconque, elle renvoie NULL.
dlsym()
La fonction dlsym() prend comme arguments, un « descripteur » de
bibliothèque dynamique renvoyé par dlopen() et un nom de symbole
terminé par un caractère nul, et renvoie l’adresse où ce symbole a été
chargé en mémoire. Si le symbole n’est pas trouvé, soit dans la
bibliothèque spécifiée, soit dans n’importe quelle bibliothèque chargée
automatiquement par dlopen() lorsque cette bibliothèque a été chargée,
dlsym() renvoie NULL. (La recherche effectuée par dlsym() est d’abord
en largeur à travers l’arbre des dépendances de ces bibliothèques). Le
symbole pouvant légitimement avoir la valeur NULL (la valeur NULL
renvoyée par dlsym() n’indique pas nécessairement une erreur), la bonne
manière de vérifier si une erreur s’est produite est d’appeler
dlerror() pour effacer toute ancienne condition d’erreur, puis
d’appeler dlsym() et appeler une nouvelle fois dlerror() en
sauvegardant sa valeur de retour dans une variable et vérifier si la
valeur sauvegardée n’est pas NULL.
Il y a deux pseudo-descripteurs spéciaux : RTLD_DEFAULT et RTLD_NEXT.
Le premier recherche la première occurrence du symbole désiré en
utilisant l’ordre de recherche des bibliothèques par défaut. Le second
recherche l’occurrence suivante d’une fonction à partir de la
bibliothèque en cours. Ceci permet de fournir une enveloppe pour une
fonction se trouvant dans une autre bibliothèque partagée.
dlclose()
La fonction dlclose() décrémente le nombre de références d’une
bibliothèque dynamique dont le descripteur est handle. Si ce nombre
atteint zéro et si aucune autre bibliothèque n’emploie des symboles
exportés par celle-ci, elle est déchargée.
La fonction dlclose() renvoie 0 si elle réussit, et une valeur non
nulle en cas d’erreur.
Les symboles obsolètes _init() et _fini()
L’éditeur de liens reconnaît les symboles spéciaux _init et _fini. Si
une bibliothèque dynamique exporte une routine nommée _init(), alors
son code est exécuté après le chargement, avant le retour de dlopen().
Si la bibliothèque exporte une routine nommée _fini, elle est appelée
juste avant le déchargement. Au cas où vous voudriez éviter de lier
l’exécutable avec les fichiers de démarrage du système, vous pouvez
spécifier le paramètre -nostartfiles à la ligne de commande de gcc(1).
L’utilisation de ces routines ou des options gcc -nostartfiles ou
-nostdlib n’est pas recommandée. Il peut en résulter un comportement
non désiré tant que les routines constructeur/destructeur ne sont pas
exécutées (à moins que des mesures spéciales ne soient prises).
À la place, les bibliothèques devraient exporter les routines en
utilisant les fonctions attribut __attribute__((constructor)) et
__attribute__((destructor)). Voyez la documentation de gcc au format
Info pour plus d’information sur celles-ci. Les routines constructeur
sont exécutées avant que dlopen revienne et les routines destructeur
sont exécutées avant que dlclose revienne.
Extensions de la glibc :dladdr() et dlvsym()
La glibc a ajouté deux fonctions, qui ne sont pas décrites par POSIX,
dont les prototypes sont :
#define _GNU_SOURCE
#include <dlfcn.h>
int dladdr(void *addr, Dl_info *info);
void *dlvsym(void *handle, char *symbol, char *version);
La fonction dladdr() prend un pointeur vers une fonction et essaie de
résoudre le nom et le fichier où il se trouve. L’information est
stockée dans une structure Dl_info :
typedef struct {
const char *dli_fname; /* Chemin du fichier de l’objet partagé
contenant l’adresse */
void *dli_fbase; /* Adresse à laquelle l’objet partagé
est chargé */
const char *dli_sname; /* Nom du symbole le plus proche avec
une adresse inférieure à addr */
void *dli_saddr; /* Adresse exacte du symbole dont
le nom est dli_sname */
} Dl_info;
Si aucun symbole correspondant à l’adresse addr ne peut être trouvé,
dli_sname et dli_saddr sont définis à NULL.
dladdr() renvoie 0 en cas d’erreur et une valeur non nulle en cas de
succès.
La fonction dlvsym(), fournie par la glibc depuis la version 2.1,
effectue la même chose que dlsym() mais prend une version sous forme de
chaîne comme argument supplémentaire.
CONFORMITÉ
POSIX.1-2001 décrit dlclose(), dlerror(), dlopen() et dlsym().
NOTES
Les symboles RTLD_DEFAULT et RTLD_NEXT sont définis dans <dlfcn.h>
seulement si _GNU_SOURCE a été définie avant l’inclusion.
Depuis la glibc 2.2.3, atexit(3) peut être utilisée pour enregistrer un
gestionnaire de sortie qui sera automatiquement appelé quand une
bibliothèque sera déchargée.
Historique
L’interface standard dlopen provient de SunOS. Ce système possède
également dladdr() mais pas dlvsym().
BOGUES
Quelquefois, les pointeurs de fonctions passés à dladdr() peuvent vous
surprendre. Sur certaines architectures (notablement i386 et x86_64),
dli_fname et dli_fbase peuvent pointés sur l’objet depuis lequel vous
appelez dladdr(), même si la fonction utilisée en paramètre semble
provenir d’une bibliothèque liée dynamiquement.
Le problème est que le pointeur de fonction ne sera résolu que lors de
la compilation, mais pointe simplement vers la section de l’objet
original plt (table de procédure d’édition des liens), qui redirige
l’appel après avoir demandé à l’éditeur de liens dynamique de résoudre
le symbole). Un contournement consiste à compiler le code pour qu’il
soit indépendant de son adressage : dans ce cas le compilateur ne peut
pas préparer le pointeur à la compilation, et de nos jours, gcc(1)
générera du code qui chargera juste l’adresse finale du symbole depuis
la table GOT (table d’offset globale) lors de l’exécution, avant de la
passer à dladdr().
EXEMPLE
Charger la bibliothèque mathématique et afficher le cosinus de 2,0 :
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int
main(int argc, char **argv)
{
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 */
/* Writing: cosine = (double (*)(double)) dlsym(handle, "cos");
would seem more natural, but the C99 standard leaves
casting from "void *" to a function pointer undefined.
The assignment used below is the POSIX.1-2003 (Technical
Corrigendum 1) workaround; see the Rationale for the
POSIX specification of dlsym(). */
*(void **) (&cosine) = dlsym(handle, "cos");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
printf("%f\n", (*cosine)(2.0));
dlclose(handle);
exit(EXIT_SUCCESS);
}
Supposons que le programme s’appelle « foo.c », on doit le compiler
ainsi:
gcc -rdynamic -o foo foo.c -ldl
Une bibliothèque (bar.c dans l’exemple suivant) qui exporte _init() et
_fini() sera compilée comme suit :
gcc -shared -nostartfiles -o bar bar.c
VOIR AUSSI
ld(1), ldd(1), dl_iterate_phdr(3), feature_test_macros(7),
rtld-audit(7), ld.so(8), ldconfig(8), les pages Info de ld.so, gcc, ld
COLOPHON
Cette page fait partie de la publication 3.23 du projet man-pages
Linux. Une description du projet et des instructions pour signaler des
anomalies peuvent être trouvées à l’adresse
http://www.kernel.org/doc/man-pages/.
TRADUCTION
Cette page de manuel a été traduite et mise à jour par Christophe
Blaess <http://www.blaess.fr/christophe/> entre 1996 et 2003, puis par
Alain Portal <aportal AT univ-montp2 DOT fr> jusqu’en 2006, et mise à
disposition sur http://manpagesfr.free.fr/.
Les mises à jour et corrections de la version présente dans Debian sont
directement gérées par Florentin Duneau <fduneau@gmail.com> et l’équipe
francophone de traduction de Debian.
Veuillez signaler toute erreur de traduction en écrivant à
<debian-l10n-french@lists.debian.org> ou par un rapport de bogue sur le
paquet manpages-fr.
Vous pouvez toujours avoir accès à la version anglaise de ce document
en utilisant la commande « man -L C <section> <page_de_man> ».