man dlopen (Fonctions bibliothèques) - Interface de programmation pour le chargeur de bibliothèques dynamiques.

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);

DESCRIPTION

Les quatres 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 n'importe laquelle des routines « dl » (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 depuis le fichier 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 (voir 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 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 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.)

La valeur de flag peut être soit RTLD_LAZY, soit RTLD_NOW. Lorsque RTLD_NOW est spécifiée, ou que la variable d'environnement LD_BIND_NOW est positionnée avec une chaîne non vide, tous les symboles non définis sont résolus avant le retour de dlopen(). Si cela ne peut pas être fait, une erreur est renvoyée. Autrement, la liaison est fainéante : les symboles sont résolus la première fois lorsqu'on en a besoin.

En option, on peut ajouter (avec un OU binaire) l'attribut RTLD_GLOBAL rendant les symboles externes de la bibliothèque disponibles pour les bibliothèques chargées ultérieurement. (Le contraire de RTLD_GLOBAL est RTLD_LOCAL. C'est la valeur par défaut.)

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 l'attribut RTLD_GLOBAL. Si l'édition des liens de l'exécutable a été faite avec l'option « -rdynamic » (ou, de manière synonyme, « --export-dynamic »), alors ses symboles globaux seront également employés pour résoudre les références de la 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 de 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 un descripteur de bibliothèque dynamique renvoyée par dlopen() et un nom de symbole terminé par un caractère nul, et renvoie l'adresse où ce symbole a été chargé. 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 en largeur d'abord à 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 un encadrement 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 sur la bibliothèque dynamique dont le descripteur est handle. Si ce nombre descend à 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 si une erreur est survenue.

Les symboles obsolètes _init et _fini

L'éditeur de liens reconnait les symboles spéciaux _init et _fini. Si une bibliothèque dynamique exporte une routine nommée _init, 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 le lien avec les fichiers de démarrage du système, vous pouvez préciser le paramètre « -nostartfiles » sur la ligne de commande de gcc.

L'utilisation de ces routines ou des options gcc -nostartupfiles 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 attributs __attribute__((constructor)) et __attribute__((destructor)). Voir les pages info de gcc 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 GNU

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ù elle se trouve. L'information est stockée dans une structure Dl_info :

typedef struct {
  const char *dli_fname;/* File name of defining object */
  void *dli_fbase;      /* Load address of that object */
  const char *dli_sname;/* Name of nearest lower symbol */
  void *dli_saddr;      /* Exact value of nearest symbol */
} Dl_info;
dladdr() renvoie 0 en cas d'erreur et une valeur non nulle si elle réussit.

La fonction dlvsym() effectue la même chose que dlsym() mais prend une chaîne version comme argument supplémentaire.

EXEMPLE

Charger la bibliothèque mathématique et afficher le cosinus de 2.0 :

#include <stdio.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(1); }

dlerror(); /* Clear any existing error */ *(void **) (&cosine) = dlsym(handle, "cos"); if ((error = dlerror()) != NULL) { fprintf (stderr, "%s\n", error); exit(1); }

printf ("%f\n", (*cosine)(2.0)); dlclose(handle); return 0; }

Supposons que le programme s'appelle « foo.c », on doit le compiler ainsi :

gcc -rdynamic -o foo foo.c -ldl

Une bibliothèque (disons bar.c) qui exporte _init() et _fini() sera compilée comme suit :

gcc -shared -nostartfiles -o bar bar.c

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.

HISTORIQUE

L'interface standard dlopen provient de SunOS. Ce système a également dladdr mais pas dlvsym.

CONFORMITÉ

POSIX 1003.1-2003 describes dlclose, dlerror, dlopen, dlsym.

VOIR AUSSI

ld(1), ldd(1), ld.so(8), ldconfig(8), ld.so info pages, gcc info pages, ld info pages

TRADUCTION

Christophe Blaess, 2000-2003.

Alain Portal, 2005

CETTE PAGE DOCUMENTE AUSSI :