LinuxVox.com
Maison
Compilateur C++ en ligne
Tutoriels
Blog
Dernière mise à jour :19 juin 2026
Erreur de symbole non défini lors de l'appel à dlopen() sous Linux : comment trouver les symboles manquants lors du chargement de bibliothèques .so
Pour les développeurs travaillant avec des bibliothèques partagées dynamiques ( .sofichiers `lib`) sous Linux, l' dlopen()appel système `lib` est un outil puissant pour le chargement dynamique des bibliothèques. Cependant, il n'est pas rare de rencontrer la redoutable erreur « symbole non défini » lors de son utilisation dlopen(). Cette erreur se produit lorsque l'éditeur de liens dynamiques ne parvient pas à résoudre un symbole (par exemple, une fonction ou une variable) référencé par la bibliothèque en cours de chargement.
Bien que le message d'erreur soit clair, identifier la cause de l'absence du symbole et la corriger peut s'avérer complexe, notamment pour les applications complexes comportant de nombreuses dépendances. Cet article vous permettra de comprendre l'erreur « symbole indéfini », d'en expliquer les causes et de suivre une procédure pas à pas pour la diagnostiquer et la résoudre à l'aide des outils intégrés de Linux. Que vous soyez un développeur expérimenté ou novice en matière de liaison dynamique, ce guide vous aidera à identifier et à corriger rapidement les symboles manquants.
Table des matières #
- Compréhension dlopen()et liaison dynamique
- Quelles sont les causes des erreurs « Symbole non défini » ?
- Scénarios courants conduisant à des symboles indéfinis
- Outils essentiels pour diagnostiquer les symboles indéfinis
- Guide étape par étape pour corriger l'erreur
- Techniques de dépannage avancées
- Meilleures pratiques de prévention
- Conclusion
- Références
1. Compréhension dlopen()et liaison dynamique #
Avant d'aborder les erreurs, récapitulons le dlopen()fonctionnement des liens dynamiques.
Qu'est-ce que dlopen()? #
dlopen()est une fonction POSIX qui charge une bibliothèque partagée dynamique ( .sofichier) dans l'espace d'adressage d'un processus en cours d'exécution . Contrairement à la liaison statique (où les bibliothèques sont liées à la compilation), la liaison dynamique dlopen() offre une grande flexibilité : les bibliothèques peuvent être chargées/déchargées à la demande, ce qui réduit la consommation de mémoire et permet le développement d'architectures de plugins (par exemple, extensions de navigateur, plugins d'IDE).
La syntaxe de base est :
#include <dlfcn.h>
void *dlopen(const char *filename, int mode);
filename: Chemin d'accès à la .sobibliothèque (par exemple, "./libfoo.so").
mode: Mode de chargement, généralement RTLD_LAZY(résolution des symboles uniquement lorsqu'ils sont utilisés) ou RTLD_NOW(résolution immédiate de tous les symboles).
Liaison dynamique vs. Liaison au moment du chargement #
La plupart des programmes Linux utilisent la liaison dynamique au chargement , où le chargeur du système d'exploitation résout les dépendances (par exemple, `lib` libc.so) au démarrage du programme. dlopen()permet la liaison dynamique à l'exécution , où l'application contrôle explicitement le chargement des bibliothèques.
Une différence majeure : avec RTLD_LAZY, les symboles non résolus dans la bibliothèque peuvent ne pas déclencher d’erreur tant que le symbole n’est pas utilisé (par exemple, lors de l’appel d’une fonction). Avec RTLD_NOW, dlopen()une erreur se produit immédiatement si un symbole est non résolu.
Le rôle du lien dynamique ( ld-linux.so) #
L'éditeur de liens dynamique ( /lib64/ld-linux-x86-64.so.2sur x86_64) gère la résolution des symboles lors de la liaison au chargement et à l'exécution. Lors de la résolution des symboles dans un objet partagé chargé dynamiquement, il effectue la recherche dans l'ordre suivant :
- La carte des liens des objets chargés pour le programme principal et ses dépendances.
- Objets partagés (et leurs dépendances) précédemment ouverts à
dlopen()l'aide de l'RTLD_GLOBALindicateur. - La bibliothèque chargée elle-même et toutes ses dépendances chargées automatiquement.
Les symboles globaux dans l'exécutable peuvent également être utilisés si l'exécutable a été lié avec -rdynamic(ou --export-dynamic), ce qui place tous les symboles globaux dans la table des symboles dynamiques.
2. Quelles sont les causes des erreurs « Symbole non défini » ? #
Une erreur « symbole non défini » se produit lorsque l’éditeur de liens dynamiques ne trouve pas un symbole référencé par la bibliothèque en cours de chargement (ou par une fonction de celle-ci). Les causes principales les plus fréquentes sont :
2.1 Dépendances manquantes #
La bibliothèque chargée libfoo.sodépend d'une autre bibliothèque libbar.soqui n'est pas chargée ou qui est introuvable. Par exemple, si libfoo.so`lib` appelle bar_func()`lib` libbar.somais libbar.soque `lib` est manquante, l'éditeur de liens ne peut pas résoudre `lib` bar_func.
2.2 Symbole non exporté par la bibliothèque #
Le symbole existe dans le code source de la bibliothèque mais n'est pas exporté (par exemple, fonctions statiques ou symboles cachés via des options de compilation comme -fvisibility=hidden).
2.3 Incompatibilité de version #
La bibliothèque dépend d'une version plus récente/plus ancienne d'une dépendance. Par exemple, libfoo.soelle requiert libbar.so.2, mais seule libbar.so.1est installée.
2.4 Problèmes liés à l'ordre des liens #
La bibliothèque a été compilée sans liaison avec ses dépendances. Par exemple, libfoo.soelle est compilée avec gcc -shared -o libfoo.so libfoo.o(manquant -lbar), laissant bar_func()des dépendances non résolues dans libfoo.so.
2.5 Fautes de frappe ou sensibilité à la casse #
Une faute de frappe dans le nom du symbole (par exemple, BarFuncvs. bar_func) ou une sensibilité à la casse (Linux est sensible à la casse !) provoque l'échec de l'éditeur de liens.
3. Scénarios courants conduisant à des symboles non définis #
Explorons des scénarios du monde réel où des symboles indéfinis apparaissent avec dlopen().
Scénario 1 : Bibliothèque de dépendances non chargée #
Supposons libfoo.soque dépende de libbar.so, mais libbar.sone soit pas chargé avant libfoo.so.
Exemple:
// main.c: Loads libfoo.so and calls foo_func()
void *handle = dlopen("./libfoo.so", RTLD_LAZY);
if (!handle) { fprintf(stderr, "dlopen failed: %s\n", dlerror()); }
void (*foo_func)() = dlsym(handle, "foo_func");
foo_func(); // Error occurs here!
// libfoo.c: foo_func() calls bar_func() from libbar.so
void foo_func() { bar_func(); } // bar_func is undefined in libfoo.so
Si libbar.son'est pas chargé (par exemple, via dlopen("./libbar.so", RTLD_GLOBAL)), l'appel foo_func()déclenche :
undefined symbol: bar_func
Scénario 2 : Symbole masqué par des attributs de visibilité #
Les compilateurs modernes (GCC, Clang) prennent en charge le contrôle de la visibilité des symboles. Si bar_func()un symbole libbar.soest marqué comme visible __attribute__((visibility("hidden"))), il n'est pas exporté et libfoo.sone peut donc pas être résolu.
Exemple:
// libbar.c: bar_func is hidden
__attribute__((visibility("hidden"))) void bar_func() { /* ... */ }
nm -D libbar.so(pour lister les symboles dynamiques) ne s'affichera pasbar_func , ce qui entraînera une erreur de symbole non défini.
Scénario 3 : Liaison avec une version de dépendance incorrecte #
Si le système libfoo.soest compilé par rapport à , mais ne possède que , les symboles ajoutés dans (par exemple, ) seront indéfinis.libbar.so.2libbar.so.1libbar.so.2bar_func_v2()
Scénario 4 : Dépendances de bibliothèque statique manquantes #
Si libfoo.sodépend d'une bibliothèque statique (par exemple, libbaz.a), mais libbaz.an'est pas liée lors libfoo.sode la compilation de , les symboles de libbaz.aseront indéfinis dans libfoo.so.
Scénario 5 : Problèmes de modification de noms en C++ #
Les compilateurs C++ utilisent la transformation de noms pour encoder les signatures de fonctions en noms de symboles uniques (par exemple, `f` MyClass::myFunc(int)devient `f` _ZN7Class7myFuncEi). Si une bibliothèque partagée est compilée en C mais chargée depuis du code C++ (ou inversement), les noms transformés ne correspondront pas. Un symptôme courant est l'apparition d'erreurs impliquant des symboles comme `f` _ZTVN10__cxxabiv117__class_type_infoE, qui est la table virtuelle de `f` std::type_info.
Exemple de correction : encapsuler les en-têtes C avec `<script>` extern "C"pour éviter toute altération :
// libbar.h
#ifdef __cplusplus
extern "C" {
#endif
void bar_func();
#ifdef __cplusplus
}
#endif
4. Outils essentiels pour diagnostiquer les symboles indéfinis #
Pour corriger l'erreur, il faut d'abord identifier le symbole manquant et la raison de cette absence . Vous trouverez ci-dessous des outils Linux permettant de diagnostiquer le problème.
4.1 dlerror(): Obtenir le message d'erreur #
La première étape consiste à capturer l'erreur exacte à l'aide de dlerror()(from <dlfcn.h>). Vérifiez toujours dlerror()après dlopen()ou dlsym():
void *handle = dlopen("./libfoo.so", RTLD_NOW);
if (!handle) {
fprintf(stderr, "dlopen failed: %s\n", dlerror()); // Print error
exit(1);
}
Exemple de résultat :
dlopen failed: ./libfoo.so: undefined symbol: bar_func
4.2 ldd: Vérifier les dépendances de la bibliothèque #
lddCette commande liste les dépendances d'une bibliothèque dynamique ou d'un exécutable. Elle permet de vérifier si des bibliothèques requises sont manquantes ou introuvables.
Syntaxe:
ldd libfoo.so
Exemple de sortie (cas normal) :
linux-vdso.so.1 (0x00007ffd... )
libbar.so.1 => /usr/lib/libbar.so.1 (0x00007f... )
libc.so.6 => /lib64/libc.so.6 (0x00007f... )
/lib64/ld-linux-x86-64.so.2 (0x00007f... )
Si une dépendance est manquante :
libbar.so.1 => not found # libbar.so.1 is missing!
4.3 nm: Lister les symboles d'une bibliothèque #
nmAffiche les symboles des fichiers objets ou des bibliothèques. Utilisez -D(ou -g) pour afficher uniquement les symboles dynamiques (exportés).
Syntaxe:
nm -D libfoo.so | grep bar_func
Exemples de résultats :
U bar_func:U= symbole indéfini (l'éditeur de liens doit résoudre ce problème).0000000000001234 T bar_funcLe symbole :T= est défini (exporté) dans la section texte.
4.4 objdump: Inspecter les tables de symboles #
objdump -T(ou --dynamic-syms) affiche la table des symboles dynamiques, similaire à nm -Dmais avec plus de détails (par exemple, le versionnage des symboles).
Syntaxe:
objdump -T libfoo.so | grep bar_func
Exemple de résultat pour un symbole non défini :
0000000000000000 DF *UND* 0000000000000000 bar_func
4.5 readelf: Analyse détaillée ELF #
readelfanalyse la structure ELF (Executable and Linkable Format) de la bibliothèque, fournissant des informations granulaires sur les symboles.
Pour lister tous les symboles avec leur type et leur liaison :
readelf -Ws libfoo.so | grep bar_func
Résultat pour un symbole non défini :
42: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND bar_func
UND= indéfini.GLOBALLe symbole = est visible pour le linker.
4.6 LD_DEBUG: Tracer le lien dynamique #
Cette LD_DEBUGvariable d'environnement active l'affichage de journaux détaillés par l'éditeur de liens dynamiques. Utilisez-la pour suivre la résolution des symboles.
Pour déboguer la recherche de symboles pour libfoo.so:
LD_DEBUG=symbols,bindings ./your_program
Exemple d'extrait de résultat :
symbol=bar_func; lookup in file=./your_program [0]
symbol=bar_func; lookup in file=/lib64/libc.so.6 [0]
symbol=bar_func; lookup in file=./libfoo.so [0]
./your_program: symbol lookup error: ./libfoo.so: undefined symbol: bar_func
Cela montre que l'éditeur de liens a recherché your_program, libc.so.6, et libfoo.somais n'a pas réussi à trouver bar_func.
5. Guide étape par étape pour corriger l'erreur n°
Voyons comment résoudre une erreur de « symbole non défini » à l'aide d'un exemple concret.
Scénario n°
Vous développez un système de plugins :
main.cchargementslibplugin.soviadlopen().libplugin.soappelslog_message()deliblogger.so.- L'exécution
maindonne :undefined symbol: log_message.
Étape 1 : Reproduire l’erreur et capturer les détails #
Exécutez le programme et utilisez-le dlerror()pour obtenir le symbole exact :
dlopen failed: ./libplugin.so: undefined symbol: log_message
Étape 2 : Vérifier si log_messageest défini dans liblogger.so#
Utiliser nmpour vérifier log_messageest exporté par liblogger.so:
nm -D liblogger.so | grep log_message
0000000000001150 T log_message # Good: 'T' means defined!
Étape 3 : Vérifier libplugin.soles dépendances de 's #
Utilisez ldd-le pour voir si libplugin.socela dépend de liblogger.so:
ldd libplugin.so
linux-vdso.so.1 (0x00007ffd... )
libc.so.6 => /lib64/libc.so.6 (0x00007f... )
/lib64/ld-linux-x86-64.so.2 (0x00007f... )
Problème : liblogger.son'est pas répertorié ! libplugin.son'a pas été lié à liblogger.so.
Étape 4 : Recompiler libplugin.soavec les dépendances #
Recompilez libplugin.soet liez explicitement avec liblogger.so:
gcc -shared -fPIC -o libplugin.so plugin.c -L. -llogger
-L.: Rechercher les bibliothèques dans le répertoire courant.-llogger: Lien contreliblogger.so.
Étape 5 : Vérifier libplugin.somaintenant dépend du liblogger.so#
Rediffusion ldd:
ldd libplugin.so
linux-vdso.so.1 (0x00007ffd... )
liblogger.so => ./liblogger.so (0x00007f... ) # Now found!
libc.so.6 => /lib64/libc.so.6 (0x00007f... )
/lib64/ld-linux-x86-64.so.2 (0x00007f... )
Étape 6 : Charger liblogger.soavant libplugin.so(si nécessaire) #
Si liblogger.soelle ne figure pas dans le chemin de la bibliothèque système, chargez-la d'abord main.cavec RTLD_GLOBALpour rendre ses symboles disponibles à libplugin.so:
// Load liblogger.so first
void *logger_handle = dlopen("./liblogger.so", RTLD_NOW | RTLD_GLOBAL);
if (!logger_handle) { /* handle error */ }
// Now load libplugin.so
void *plugin_handle = dlopen("./libplugin.so", RTLD_NOW);
Étape 7 : Tester la correction #
Relancez le programme. L'erreur devrait être résolue !
6. Techniques avancées de dépannage #
Conflits de versions de symboles #
Les bibliothèques comme `lib` libc.soutilisent le versionnage des symboles (par exemple, `lib` printf@GLIBC_2.2.5). Si votre bibliothèque référence `lib` printf@GLIBC_2.34mais que le système utilise `lib` GLIBC_2.27, vous obtiendrez une erreur de symbole non défini. Utilisez `lib` objdump -Tpour vérifier les versions :
objdump -T libfoo.so | grep printf
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.34 printf
Solution : Recompilez avec une version plus ancienne de GLIBC ou mettez à jour la bibliothèque système.
Symboles faibles #
Un symbole faible (déclaré avec `--` __attribute__((weak))) permet à l'éditeur de liens de l'ignorer s'il n'est pas défini. Utilisez-le pour les fonctionnalités optionnelles :
__attribute__((weak)) void optional_func() { /* fallback */ }
Attributs de visibilité #
Pour exporter explicitement des symboles (par exemple, dans des plugins), utilisez__attribute__((visibility("default"))) :
// liblogger.c
__attribute__((visibility("default"))) void log_message() { /* ... */ }
Compilez avec l'option -fvisibility=hiddenpermettant de masquer tous les symboles, à l'exception de ceux explicitement exportés :
gcc -shared -fPIC -o liblogger.so logger.c -fvisibility=hidden
RTLD_DEEPBINDpour la priorité de recherche de symbole #
Depuis la version 2.3.4 de glibc, cette RTLD_DEEPBINDoption place les définitions de symboles propres à un objet partagé avant la portée globale lors de la recherche. Cela empêche l'interposition de symboles, où la définition d'une bibliothèque chargée globalement masque celle de votre plugin.
void *handle = dlopen("./libplugin.so", RTLD_NOW | RTLD_DEEPBIND);
Utilisez cette option lorsque les plugins doivent utiliser leurs propres définitions internes plutôt que d'hériter des symboles de l'application hôte ou d'autres bibliothèques chargées.
Liaison statique en dernier recours #
Si les dépendances dynamiques sont ingérables, liez statiquement la dépendance àlibfoo.so :
gcc -shared -fPIC -o libfoo.so foo.c libbar.a # Link static libbar.a
Avertissement : Augmente la taille de la bibliothèque et perd les avantages de la liaison dynamique.
7. Meilleures pratiques de prévention #
7.1 Tester minutieusement les dépendances #
Utilisez ` git check` lddet `git nmcheck` pour vérifier les bibliothèques avant le déploiement. Par exemple, dans un pipeline CI :
# Check for undefined symbols in libfoo.so
nm -D libfoo.so | grep ' U ' && echo "Undefined symbols found!" && exit 1
7.2 Lien avec -Wl,--no-undefined#
Indiquez à l'éditeur de liens d'échouer à la compilation si des symboles sont indéfinis :
gcc -shared -fPIC -o libfoo.so foo.c -L. -llogger -Wl,--no-undefined
7.3 Visibilité du symbole de contrôle #
Utilisez -fvisibility=hiddendes exportations explicites pour éviter de polluer la table des symboles et de masquer les symboles internes.
7.4 Dépendances des documents #
Incluez un READMEfichier DEPENDENCIESlistant les bibliothèques et versions requises (par exemple, libbar.so.2 >= 1.3.0).
7.5 Utilisation RTLD_NOWpour la détection précoce des erreurs #
RTLD_NOWIl est préférable RTLD_LAZYde détecter les symboles non définis au dlopen()moment de l'appel de la fonction, et non lors de son exécution :
void *handle = dlopen("./libfoo.so", RTLD_NOW); // Fails fast on undefined symbols
7.6 Exporter les symboles exécutables avec -rdynamic#
Si vos bibliothèques chargées doivent rappeler l'exécutable principal, liez-les avec -rdynamic(ou --export-dynamic) pour placer tous les symboles globaux dans la table des symboles dynamiques :
gcc -rdynamic -o myapp main.c -ldl
Sans cela, les symboles de l'exécutable ne sont pas visibles pour dlopen()les bibliothèques chargées.
8. Conclusion #
Les erreurs « symbole non défini » dlopen()sont fréquentes, mais résolubles avec les outils et les connaissances appropriés. En utilisant `ng` ldd, `ng` nm, ` readelfng` et ` LD_DEBUGng`, vous pouvez diagnostiquer les dépendances manquantes, les symboles non exportés ou les conflits de versions. Les solutions consistent souvent à lier les dépendances, à exporter explicitement les symboles ou à ajuster l'ordre de chargement. Pour les projets C++, soyez attentif aux problèmes de modification de noms et utilisez `ng` extern "C"lorsque cela est nécessaire.
La prévention est essentielle : utilisez des vérifications à la compilation -Wl,--no-undefined, exportez les symboles exécutables avec `export` -rdynamic, contrôlez la visibilité des symboles et documentez les dépendances. Ces bonnes pratiques vous permettront de minimiser les problèmes de liaison dynamique et de créer des applications robustes et fiables.
9. Références #
dlopen(3)page de manuel : man7.orgldd(1)page de manuel : man7.orgnm(1)page de manuel : man7.orgreadelf(1)page de manuel : man7.org- Visibilité du symbole du CCG : Wiki du CCG - Visibilité
- « Comment écrire des bibliothèques partagées » par Ulrich Drepper : akkadia.org
- Spécification ELF : Interface binaire d’application System V