web-dev-qa-db-fra.com

Liaison avec bibliothèque dynamique avec dépendances

Considérez le scénario suivant:

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

Je veux compiler un fichier binaire qui est lié à la libB . Dois-je lier le binaire à la libB uniquement ou à la libA?

Est-il possible de créer un lien uniquement avec les dépendances directes, en laissant la résolution des symboles non résolus à partir des dépendances pour l'exécution?

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

En d'autres termes:

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

Bien sûr, 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 ci-dessous dois-je utiliser?

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

ou

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

Je ne pouvais pas 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 essaiera de résoudre tous les symboles du principal et de la libB. Cependant, libB a des symboles non définis de libA. C'est pourquoi l'éditeur de liens se plaint de cela.

C'est pourquoi je dois aussi créer un lien avec libA ..____. Cependant, j'ai trouvé un moyen d'ignorer les symboles non résolus des bibliothèques partagées .

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

On dirait qu'il est encore possible d'utiliser l'option -rpath . Cependant, j'ai besoin de comprendre un peu mieux.

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

- Commentaires mis à jour 2:

-rpath ne doit pas être utilisé à cette fin. Il est utile de forcer une bibliothèque à se trouver dans un répertoire donné. L'approche -unresolved-symbol a l'air beaucoup mieux.

Merci encore.

63
Marcus

On dirait que vous êtes déjà au bout du chemin. Bravo à votre enquête. Voyons si je peux aider à clarifier le «pourquoi» derrière cela. 

Voici ce que fait l'éditeur de liens. Lorsque vous liez votre exécutable ("principal" ci-dessus), certains symboles (fonctions et autres choses) ne sont pas résolus. Il examinera la liste des bibliothèques qui suivent, en essayant de résoudre les symboles non résolus. En cours de route, il trouve que certains des symboles sont fournis par libB.so. Il est donc noté 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. Il doit donc les résoudre également. Sans lier contre libA.so, votre application serait incomplète. Une fois qu'il est lié à libA.so, tous les symboles sont résolus et la liaison est terminée.

Comme vous l'avez vu, l'utilisation de -unresolved-symbols-in-shared-libs ne résout pas le problème. Cela diffère simplement pour que ces symboles soient résolus au moment de l'exécution. C'est à cela que sert -rpath: 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 d’une bibliothèque, puisqu’un symbole peut être fourni par plusieurs bibliothèques.

Il existe une autre description de ce processus ici: Pourquoi l’ordre dans lequel les bibliothèques sont liées provoque-t-il parfois des erreurs dans GCC?

35
kithril

Pour les liens dynamiques uniquement avec des dépendances directes, vous pouvez utiliser-Wl,--as-neededen ajoutant les bibliothèques after -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 affiche également les dépendances indirectes.

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

ldd montre aussi 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 inclure uniquement 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}")
13
user1047271

Une autre option consiste à utiliser libtool

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

1
mattmilten

Ceci est un article intéressant - je me cognais la tête avec cela aussi, mais je pense que vous manquez un point ici ..

L'idée est la suivante, non?

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

Considérons plus loin que ..

  • Dans a.cpp (et seulement là) vous définissez une classe/variable, appelons-le "symA" 
  • Dans b.cpp (et seulement là) vous définissez une classe/variable, appelons-le "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 devrait fonctionner, 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 même d’interférencer des bibliothèques partagées (c’est-à-dire la création d’API), c’est que les symboles des couches les plus profondes sont cachés (pensez à éplucher des oignons) et ne sont pas utilisés ... c’est-à-dire qu’ils ne font pas référence à symA. .cpp, mais seulement à symB à la place (et laissez symB faire référence à symA uniquement).

0
El Sampsa