dlopen(3) Library Functions Manual dlopen(3) NOM dlclose, dlopen, dlmopen - Ouvrir et fermer un objet partage BIBLIOTHEQUE Bibliotheque de liens dynamiques (libdl, -ldl) SYNOPSIS #include void *dlopen(const char *filename, int flags); int dlclose(void *handle); #define _GNU_SOURCE #include void *dlmopen(Lmid_t lmid, const char *filename, int flags); DESCRIPTION dlopen() La fonction dlopen() charge la bibliotheque dynamique dont le nom est fourni dans la chaine filename (terminee par l'octet NULL) et renvoie un descripteur opaque (<< handle >>) representant la bibliotheque dynamique. Ce descripteur est utilise 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 renvoye correspond au programme principal. Si filename contient une barre oblique (<< / >>), il est interprete comme un chemin (relatif ou absolu). Autrement, l'editeur dynamique de liens cherche la bibliotheque de la facon suivante (consultez ld.so(8) pour plus de details) : - (ELF seulement) Si l'objet appelant (c'est-a-dire la bibliotheque partagee ou l'executable depuis lequel dlopen() est appelee) contient la balise DT_RPATH mais pas la balise DT_RUNPATH, les repertoires listes dans la balise DT_RPATH seront parcourus. - Si a l'instant ou le programme est demarre, la variable d'environnement LD_LIBRARY_PATH est definie et contient une liste de repertoires separes par des deux-points << : >>, ces repertoires seront parcourus. Par mesure de securite, cette variable est ignoree dans le cas de programmes set-user-ID et set-group-ID. - (ELF seulement) Si l'objet appelant contient la balise DT_RUNPATH, les repertoires listes dans cette balise seront parcourus. - Le fichier de cache /etc/ld.so.cache (entretenu par ldconfig(8)) est verifie pour voir s'il contient une entree correspondant a filename. - Les repertoires /lib et /usr/lib sont parcourus (dans cet ordre). Si l'objet indique dans filename a des dependances sur d'autres objets partages, ceux-ci seront automatiquement charges par l'editeur dynamique de liens, en utilisant les memes regles. Le processus peut etre recursif si ces objets ont, a leur tour, des dependances, et ainsi de suite. L'une des deux valeurs suivantes doit etre incluse dans flag : RTLD_LAZY Effectuer des liaisons paresseuses. Ne resoudre les symboles que lorsque le code qui les reference est execute. Si le symbole n'est jamais reference, alors il n'est jamais resolu. Les liaisons paresseuses ne sont effectuees que pour les references de fonctions ; les references de variables sont toujours immediatement liees quand l'objet partage est charge. Depuis la version 2.1.1 de la glibc, ce drapeau est supplante par l'effet de la variable d'environnment LD_BIND_NOW. RTLD_NOW Si cette valeur est specifiee ou si la variable d'environnement LD_BIND_NOW est definie avec une chaine non vide, tous les symboles non definis de l'objet partage sont resolus avant le retour de dlopen(). Si cela ne peut pas etre fait, une erreur est renvoyee. Zero ou plusieurs des valeurs suivantes peuvent etre specifiees avec un OU binaire dans flag : RTLD_GLOBAL Les symboles definis par cet objet partage seront rendus disponibles pour la resolution des symboles des objets partages charges ulterieurement. RTLD_LOCAL C'est la reciproque de RTLD_GLOBAL et le comportement par defaut si aucun des drapeaux n'est specifie. Les symboles definis dans cet objet partage ne sont pas rendus disponibles pour resoudre les references des objets partages charges ulterieurement. RTLD_NODELETE (depuis la glibc 2.2) Ne pas decharger l'objet partage lors de dlclose(). En consequence, les variables statiques de l'objet ne sont pas reinitialisees si l'objet est charge ulterieurement avec dlopen(). RTLD_NOLOAD (depuis la glibc 2.2) Ne pas charger l'objet partage. Cela peut etre utilise pour tester si l'objet partage n'est pas deja charge (dlopen() renvoie NULL s'il n'est pas charge, ou le descripteur de l'objet partage s'il est deja charge). Ce drapeau peut aussi etre utilise pour promouvoir les drapeaux d'un objet partage deja charge. Par exemple, un objet partage qui a ete charge avec RTLD_LOCAL peut etre 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 partage avant l'espace global. Cela signifie qu'un objet autonome utilisera de preference ses propres symboles aux symboles globaux de meme noms contenus dans les objets deja charges. Si l'argument filename est un pointeur NULL, le descripteur renvoye correspond au programme principal. Lorsqu'il est passe a dlsym(), ce descripteur provoque la recherche d'un symbole dans le programme principal, puis dans tous les objets partages charges au demarrage du programme, puis dans toutes les objets partages charges par dlopen() avec l'attribut RTLD_GLOBAL. Les references aux symboles de l'objet partage sont resolues en utilisant (dans l'ordre) : les symboles dans la table de liens des objets charges pour le programme principal et ses dependances, les symboles dans les objets partages (et leurs dependances) qui ont ete ouverts par un appel anterieur a dlopen() avec le drapeau RTLD_GLOBAL et les definitions dans l'objet partage lui-meme (ainsi que toute dependance ayant ete chargee pour cet objet). Tout symbole global dans l'executable qui a ete place dans sa table de symboles dynamiques par ld(1) peut aussi etre utilise pour resoudre les references dans un objet partage dynamiquement charge. Les symboles peuvent etre places dans la table de symbole soit parce que les liens de l'executable ont ete edites avec le drapeau << -rdynamic >> (ou de facon synonyme << --export-dynamic >>), qui fait que tous les symboles globaux de l'executable sont places dans la table de symboles dynamiques, soit parce que ld(1) a identifie une dependance sur un symbole dans un autre objet durant l'edition de liens statiques. Si le meme objet partage est charge une nouvelle fois avec dlopen(), le meme descripteur sera renvoye. Un decompte du nombre de chargements est toutefois conserve par l'editeur dynamique de liens afin d'eviter de le decharger avant que la fonction dlclose() n'ait ete appelee autant de fois que dlopen() a reussi. Les constructeurs (voir ci-dessous) sont seulement appeles lorsque l'objet est reellement charge en memoire (c'est-a-dire lorsque le compteur de references est augmente et passe a 1). Un appel ulterieur a dlopen() qui charge le meme objet partage avec RTLD_NOW peut forcer la resolution de symboles pour un objet partage charge anterieurement avec RTLD_LAZY. De facon similaire, un objet prealablement ouvert avec RTLD_LOCAL peut etre promu a RTLD_GLOBAL lors d'un appel ulterieur a dlopen(). Si dlopen() echoue pour une raison quelconque, elle renvoie NULL. dlmopen() Cette fonction effectue la meme tache que dlopen() ; les arguments filename et flags, de meme que la valeur de renvoi, sont les memes a l'exception des differences decrites plus bas. La fonction dlmopen() differe de la fonction dlopen() principalement parce qu'elle accepte un argument supplementaire, lmid, qui indique la liste de tables de liens (aussi appelee espace de noms) dans laquelle l'objet partage doit etre charge. En comparaison, dlopen() ajoute l'objet partage dynamiquement charge au meme espace de noms que l'objet partage pour lequel l'appel dlopen() est fait. Le type Lmid_t est un gestionnaire opaque qui fait reference a un espace de noms. L'argument lmid est soit l'ID d'un espace de noms existant (pouvant etre obtenu en utilisant la requete dlinfo(3) RTLD_DI_LMID) ou l'une des valeurs speciales suivantes : LM_ID_BASE Charger l'objet partage dans l'espace de noms initial (c'est-a-dire l'espace de noms de l'application). LM_ID_NEWLM Creer un nouvel espace de noms et y charger l'objet partage. Les liens de l'objet doivent avoir ete lies pour referencer tous les autres objets partages dont il a besoin puisque l'espace de noms est initialement vide. Si filename est vide, alors l'unique valeur autorisee pour lmid est LM_ID_BASE. dlclose() La fonction dlclose() decremente le compteur de references de l'objet partage charge dynamiquement et indique par handle. Si le compteur de references de cet objet tombe en dessous de zero et qu'aucun symbole dans cet objet n'est requis par un autre objet, alors l'objet est decharge apres avoir appele tous les destructeurs definis pour l'objet. Des symboles dans cet objet peuvent etre requis par un autre objet parce qu'il a ete ouvert avec le drapeau RTLD_GLOBAL et que l'un de ses symboles a permis une relocalisation dans un autre objet. Tous les objets partages qui ont ete charges automatiquement lorsque dlopen() a ete invoquee sur l'objet reference par handle sont fermes recursivement de la meme facon. Un renvoi reussi de dlclose() ne garantit que les symboles associes avec handle sont supprimes de l'espace d'adressage de l'appelant. En plus de references resultant d'appels explicites a dlopen(), un objet partage a peut-etre ete charge de facon implicite (et les references prises en compte) a cause de references dans d'autres objets partages. Ce n'est que lorsque toutes les references sont relachees que l'objet partage peut etre supprime de l'espace d'adressage. VALEUR RENVOYEE En cas de succes, dlopen() et dlmopen() renvoient un gestionnaire non nul pour l'objet charge. En cas d'erreur (le fichier ne peut pas etre trouve, il n'est pas lisible, a le mauvais format ou bien a provoque des erreurs lors de son chargement), ces fonctions renvoient NULL. En cas de succes, dlclose() renvoie 0, en cas d'erreur une valeur non nulle est renvoyee. Les erreurs de ces fonctions peuvent etre diagnostiquees en utilisant dlerror(3). ATTRIBUTS Pour une explication des termes utilises dans cette section, consulter attributes(7). +---------------------------------+--------------------------+---------+ |Interface | Attribut | Valeur | +---------------------------------+--------------------------+---------+ |dlopen(), dlmopen(), dlclose() | Securite 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 definit un espace de noms isole pour la resolution de symboles par l'editeur dynamique de liens. A l'interieur d'un espace de noms, les objets partages dependants sont implicitement charges selon les regles usuelles, et les references aux symboles sont resolues selon les regles usuelles, mais un telle resolution est limitee aux definitions fournies aux objets qui ont ete charges (explicitement et implicitement) dans l'espace de noms. La fonction dlmopen() permet une isolation de chargement d'objet, c'est-a-dire la capacite a charger un objet partage 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 realiser cela puisque qu'il empeche les symboles des objets partages d'etre disponibles a tout autre objet partage. Dans certains cas, il peut etre souhaitable de rendre les symboles fournis par un objet partage charge dynamiquement disponibles a d'autres objets (ou a un sous-ensemble) partages sans exposer ces symboles a l'application entiere. Cela peut etre realise par l'utilisation d'un espace de noms separe et du drapeau RTLD_GLOBAL. La fonction dlmopen() peut egalement etre utilisee pour fournir une meilleure isolation que le drapeau RTLD_LOCAL. En particulier, les objets partages charges avec RTLD_LOCAL peuvent etre promus a RTLD_GLOBAL s'ils sont des dependances d'un autre objet partage charge avec RTLD_GLOBAL mis a part dans le cas (peu commun) ou l'on a un controle explicite sur les dependances de tous les objets partages. Les cas possibles d'utilisation de dlmopen() sont des greffons ou 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 definis du cadriciel greffon soient resolus en symboles du greffon. Une autre utilisation est de charger le meme objet plus d'une fois. Sans l'utilisation de dlmopen(), cela exigerait la creation de copies distinctes du fichier de l'objet partage. Grace a l'utilisation de dlmopen(), cela peut etre realise par le chargement du meme fichier d'objet partage dans differents espaces de noms. L'implementation de la glibc prend en charge un nombre maximal de 16 espaces de noms. Fonctions d'initialisation et de finalisation Les objets partages peuvent exporter des fonctions en utilisant les attributs de fonction __attribute__((constructor)) et __attribute__((destructor)). Les fonctions de construction sont executees avant que dlopen() ne renvoie, et les fonctions de destruction sont executees avant que dlclose() ne renvoie. Un objet partage peut exporter plusieurs constructeurs et destructeurs et des priorites peuvent etre associees a chaque fonction pour determiner l'ordre dans lequel elles s'executent. Consultez les pages d'information de gcc (sous << Attributs de fonction >>) pour plus d'informations. Une methode plus ancienne d'obtenir (partiellement) le meme resultat passe par l'utilisation de deux symboles speciaux reconnus par l'editeur de liens : _init et _fini. Si un objet partage charge dynamiquement exporte une routine nommee _init(), alors son code est execute apres le chargement d'un objet partage, avant le retour de dlopen(). Si l'objet partage exporte une routine nommee _fini, elle est appelee juste avant le dechargement de l'objet. Dans ce cas, vous voudrez eviter de lier l'executable avec les fichiers de demarrage du systeme, qui contiennent des versions par defaut de ces fichiers ; pour cela, vous pouvez specifier l'option -nostartfiles a la ligne de commande de gcc(1). L'utilisation de _init et _fini est rendue obsolete en faveur des constructeurs et destructeurs susmentionnes, ce qui entre autres avantages, permet la definition de plusieurs fonctions d'initialisation et de finalisation. Depuis la glibc 2.2.3, atexit(3) peut etre utilisee pour enregistrer un gestionnaire de sortie qui sera automatiquement appele quand un objet partage est decharge. Historique Ces fonctions font partie de l'API dlopen, derivee de SunOS. BOGUES Pour la version 2.24 de la glibc, specifier le drapeau RTLD_GLOBAL lors de l'appel a dlmopen() genere une erreur. De plus, specifier RTLD_GLOBAL lors d'un appel a dlopen() resulte en un plantage du programme (SIGSEGV) si l'appel est effectue depuis n'importe quel objet charge dans un autre espace de noms que celui initial. EXEMPLES Le programme suivant charge la bibliotheque 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'execution du programme : $ cc dlopen_demo.c -ldl $ ./a.out -0.416147 Source du programme #include #include #include #include /* Definit LIBM_SO (qui sera une chaine telle que << 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(); /* Supprime une erreur existante */ cosine = (double (*)(double)) dlsym(handle, "cos"); /* D'apres le standard ISO C, la conversion de type entre un pointeur de fonction et << void * >>, comme effectuee ci-dessus, produit des resultats indefinis. POSIX.1-2003 et POSIX.1-2008 ont admis cet etat de fait et propose le contournement ci-dessous : *(void **) (&cosine) = dlsym(handle, "cos"); Cette conversion (lourde) de type est conforme au standard ISO C and evitera tout avertissement du compilateur. La revision technique 2013 de POSIX.1-2008 a ameliore la situation en exigeant que les implementations 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 effectuee 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); } 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 francaise de cette page de manuel a ete creee par Christophe Blaess , Stephan Rafin , Thierry Vignaud , Francois Micaux, Alain Portal , Jean-Philippe Guerard , Jean-Luc Coulon (f5ibh) , Julien Cristau , Thomas Huriaux , Nicolas Francois , Florentin Duneau , Simon Paillard , Denis Barbier , David Prevot et Gregoire Scano Cette traduction est une documentation libre ; veuillez vous reporter a la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITE LEGALE. Si vous decouvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message a . Pages du manuel de Linux 6.06 31 octobre 2023 dlopen(3)