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.
DESCRIPTION
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 (consultez
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 chargements 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 pseudodescripteurs 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)). Consultez 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 /* Consultez feature_test_macros(7) */
#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 dont la
définition
chevauche 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 */
cosine = (double (*)(double)) dlsym(handle, "cos");
/*
D’après le standard ISO C, la conversion
de type entre un pointeur
de fonction et « void * », comme
effectuée ci−dessus, produit des
résultats indéfinis.
POSIX.1−2003 et POSIX.1−2008 ont admis cet
état de fait et proposé
le contournement ci−dessous :
*(void **) (&cosine) = dlsym(handle, "cos");
Cette
conversion (lourde) de type est conforme au standard
ISO C and évitera tout avertissement du
compilateur.
La
révision technique 2013 de POSIX.1−2008 (aussi
appelée
POSIX.1−2013) a amélioré la situation en
exigeant que les
implémentations prennent en charge la conversion du
type
« void * » vers un pointeur de
fonction.’
Cependant, certains compilateurs (par exemple gcc avec
l’option « −pedantic ) peuvent
se plaindre de la conversion
effectuée dans ce programme. */
error =
dlerror();
if (error != 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), 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.65 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
Depuis 2010, cette traduction est maintenue à l’aide de l’outil po4a <http://po4a.alioth.debian.org/> par l’équipe de traduction francophone au sein du projet perkamon <http://perkamon.alioth.debian.org/>.
Christophe Blaess <http://www.blaess.fr/christophe/> (1996-2003), Alain Portal <http://manpagesfr.free.fr/> (2003-2006). Florentin Duneau et l’équipe francophone de traduction de Debian (2006-2009).
Veuillez signaler toute erreur de traduction en écrivant à <debian−l10n−french [AT] lists.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> ».