84 votes

Liaison avec une bibliothèque dynamique avec dépendances

Considérons le scénario suivant :

  • Bibliothèque partagée libA.so ,sans aucune dépendance.
  • Bibliothèque partagée libB.so, avec libA.so comme dépendance.

Je veux compiler un fichier binaire qui se lie avec la libB. Dois-je lier le fichier binaire avec la libB uniquement ou avec la libA également ?

Existe-t-il un moyen d'établir un lien uniquement avec les dépendances directes, en laissant la résolution des symboles non résolus des dépendances pour l'exécution ?

Je m'inquiète du fait que l'implémentation de la bibliothèque libB puisse changer dans le futur, introduisant d'autres dépendances (libC, libD, libE par exemple). Est-ce que je vais avoir des problèmes avec cela ?

En d'autres termes :

  • Fichiers libA : a.cpp a.h
  • Fichiers libB : b.cpp b.h
  • Fichiers du programme principal : main.cpp

Bien entendu, b.cpp inclut a.h et main.cpp inclut b.h.

Commandes de compilation :

g++ -fPIC a.cpp -c
g++ -shared -o libA.so a.o

g++ -fPIC b.cpp -c -I.
g++ -shared -o libB.so b.o -L. -lA

Laquelle des options suivantes dois-je utiliser ?

g++ main.cpp -o main -I. -L. -lB

ou

g++ main.cpp -o main -I. -L. -lB -lA

Je n'ai pas pu utiliser la première option. L'éditeur de liens se plaint des symboles non résolus de la bibliothèque libA. Mais cela me semble un peu étrange.

Merci beaucoup.

-- Commentaires mis à jour :

Lorsque je lie le binaire, l'éditeur de liens va essayer de résoudre tous les symboles de la main et de la libB. Cependant, la libB a des symboles non définis provenant de la libA. C'est pourquoi l'éditeur de liens se plaint de cela.

C'est pourquoi j'ai également besoin d'un lien avec la libA. Cependant, j'ai trouvé un moyen d'ignorer les symboles non résolus des bibliothèques partagées. Il semble que je doive utiliser la ligne de commande suivante pour le faire :

g++ main.cpp -o main -I. -L. -lB -Wl,-unresolved-symbols=ignore-in-shared-libs

Il semble qu'il soit encore possible d'utiliser la fonction -rpath option. Cependant, j'ai besoin de comprendre un peu mieux.

Quelqu'un connaît-il des pièges possibles lors de l'utilisation de l'option -Wl,-unresolved-symbols=ignore-in-shared-libs option ?

-- Commentaires mis à jour 2 :

-rpath ne doit pas être utilisé à cette fin. Il est utile de forcer une bibliothèque à être trouvée dans un répertoire donné. L'option -unresolved-symbol semble bien meilleure.

Merci encore.

45voto

kithril Points 828

Il semble que vous ayez déjà parcouru une bonne partie du chemin. Bravo pour votre enquête. Voyons si je peux vous aider à éclaircir le "pourquoi".

Voici ce que fait l'éditeur de liens. Lorsque vous liez votre exécutable ("main" ci-dessus), certains symboles (fonctions et autres) ne sont pas résolus. Il regardera la liste des bibliothèques qui suit, en essayant de résoudre les symboles non résolus. En cours de route, il découvre que certains des symboles sont fournis par libB.so, il note donc qu'ils sont maintenant résolus par cette bibliothèque.

Cependant, il découvre également que certains de ces symboles utilisent d'autres symboles qui ne sont pas encore résolus dans votre exécutable, et il doit donc les résoudre également. Sans liaison avec libA.so, votre application serait incomplète. Une fois l'enchaînement effectué avec libA.so, tous les symboles sont résolus et l'enchaînement est terminé.

Comme vous l'avez vu, l'utilisation de -unresolved-symbols-in-shared-libs ne résout pas le problème. Il ne fait que le reporter afin que ces symboles soient résolus au moment de l'exécution. C'est ce que fait -rpath est destiné à : spécifier les bibliothèques à rechercher au moment de l'exécution. Si ces symboles ne peuvent pas être résolus, votre application ne démarrera pas.

Il n'est pas facile de déterminer les dépendances des bibliothèques, car un symbole peut être fourni par plusieurs bibliothèques et être satisfait par l'établissement d'un lien avec n'importe laquelle d'entre elles.

Une autre description de ce processus est disponible ici : Pourquoi l'ordre dans lequel les bibliothèques sont liées provoque-t-il parfois des erreurs dans GCC ?

23voto

user1047271 Points 301

Pour l'établissement de liens dynamiques uniquement avec des dépendances directes, vous pouvez utiliser -Wl,--as-needed en ajoutant les librairies après -Wl,--as-needed :

gcc main.c -o main -I. -L. -Wl,--as-needed -lB -lA

Pour vérifier les dépendances directes, vous devez utiliser readelf au lieu de ldd car ldd montre également les dépendances indirectes.

$ readelf -d main | grep library
0x0000000000000001 (NEEDED)             Shared library: [libB.so]
0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

ldd indique également les dépendances indirectes :

$ LD_LIBRARY_PATH=. ldd ./main
linux-vdso.so.1 (0x00007fff13717000)
libB.so => ./libB.so (0x00007fb6738ed000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb6734ea000)
libA.so => ./libA.so (0x00007fb6732e8000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb673af0000)

Si vous utilisez cmake vous pouvez ajouter les lignes suivantes pour n'inclure que les dépendances directes :

set(CMAKE_EXE_LINKER_FLAGS    "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_SHARED_LINKER_FLAGS}")

1voto

mattmilten Points 368

Une autre option consiste à utiliser libtool

Si vous modifiez le g++ appeler à libtool --mode=compile g++ pour compiler le code source, puis libtool --mode=link g++ pour créer l'application à partir de libB entonces libA seront automatiquement liés.

0voto

El Sampsa Points 710

Il s'agit d'un article intéressant - je me suis également heurté à cette question, mais je pense que vous manquez un point

L'idée est la suivante, n'est-ce pas ?

main.cpp =(depends)=> libB.so =(depends)=> libA.so

Considérons en outre que

  • Dans a.cpp (et seulement là) vous définissez une classe / variable, appelons-la "symA"
  • Dans b.cpp (et seulement là) vous définissez une classe / variable, appelons-la "symB".
  • symB utilise symA
  • main.cpp utilise symB

Maintenant, libB.so et libA.so ont été compilés comme vous l'avez décrit ci-dessus. Après cela, votre première option debe travail, c'est-à-dire

g++ main.cpp -o main -I. -L. -lB

Je suppose que votre problème provient du fait que

dans main.cpp vous faites également référence à symA

Ai-je raison ?

Si vous utilisez un symbole dans votre code, ce symbole doit se trouver dans un fichier .so

L'idée d'interférencer les bibliothèques partagées (c'est-à-dire de créer des API) est que les symboles des couches profondes sont cachés (comme si on pelait des oignons) et ne sont pas utilisés. Par exemple, ne faites pas référence à symA dans votre main.cpp, mais seulement à symB (et laissez symB faire référence à symA uniquement).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X