Pourquoi l’ordre dans lequel les bibliothèques sont liées provoque-t-il parfois des erreurs dans GCC?

Pourquoi l’ordre dans lequel les bibliothèques sont liées provoque-t-il parfois des erreurs dans GCC?

(Voir l’historique sur cette réponse pour obtenir le texte le plus élaboré, mais je pense maintenant qu’il est plus facile pour le lecteur de voir de vraies lignes de commande).


Fichiers communs partagés par toutes les commandes ci-dessous

 $ cat a.cpp extern int a; int main() { return a; } $ cat b.cpp extern int b; int a = b; $ cat d.cpp int b; 

Liaison à des bibliothèques statiques

 $ g++ -c b.cpp -o bo $ ar cr libb.a bo $ g++ -c d.cpp -o do $ ar cr libd.a do $ g++ -L. -ld -lb a.cpp # wrong order $ g++ -L. -lb -ld a.cpp # wrong order $ g++ a.cpp -L. -ld -lb # wrong order $ g++ a.cpp -L. -lb -ld # right order 

L’éditeur de liens recherche de gauche à droite et note les symboles non résolus au fur et à mesure. Si une bibliothèque résout le symbole, il faut les fichiers objects de cette bibliothèque pour résoudre le symbole (bo dans libb.a dans ce cas).

Les dépendances des bibliothèques statiques les unes par rapport aux autres fonctionnent de la même manière: la bibliothèque qui a besoin de symboles doit être la première, puis la bibliothèque qui résout le symbole.

Si une bibliothèque statique dépend d’une autre bibliothèque, mais que l’autre bibliothèque dépend à nouveau de l’ancienne bibliothèque, il existe un cycle. Vous pouvez résoudre ce problème en entourant les bibliothèques dépendantes du cycle par -( et -) , telles que -( -la -lb -) (vous devrez peut-être échapper aux parens, telles que -\( et -\) ). L’éditeur de liens recherche ensuite les bibliothèques fermées plusieurs fois pour s’assurer que les dépendances cycliques sont résolues. Vous pouvez également spécifier les bibliothèques plusieurs fois, chacune se faisant l’une devant l’autre: -la -lb -la .

Liaison aux bibliothèques dynamics

 $ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc $ g++ -fpic -shared d.cpp -o libd.so $ g++ -fpic -shared b.cpp -L. -ld -o libb.so # specifies its dependency! $ g++ -L. -lb a.cpp # wrong order (works on some dissortingbutions) $ g++ -Wl,--as-needed -L. -lb a.cpp # wrong order $ g++ -Wl,--as-needed a.cpp -L. -lb # right order 

C’est la même chose ici – les bibliothèques doivent suivre les fichiers objects du programme. La différence ici avec les bibliothèques statiques est que vous ne devez pas vous soucier des dépendances des bibliothèques, car les bibliothèques dynamics sortingent elles-mêmes leurs dépendances .

Certaines dissortingbutions récentes utilisent apparemment par défaut l’indicateur --as-needed linker, qui impose que les fichiers objects du programme viennent avant les bibliothèques dynamics. Si cet indicateur est transmis, l’éditeur de liens ne sera pas lié aux bibliothèques qui ne sont pas réellement nécessaires à l’exécutable (et il le détecte de gauche à droite). Ma dissortingbution archlinux récente n’utilise pas cet indicateur par défaut, il n’a donc pas commis d’erreur pour ne pas suivre le bon ordre.

Il n’est pas correct d’omettre la dépendance de b.so contre d.so lors de la création du premier. Il vous sera demandé de spécifier la bibliothèque lors de la liaison de alors, mais a n’a pas vraiment besoin de l’entier b lui-même, il ne faut donc pas se soucier des propres dépendances de b .

Voici un exemple des implications si vous ne spécifiez pas les dépendances pour libb.so

 $ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc $ g++ -fpic -shared d.cpp -o libd.so $ g++ -fpic -shared b.cpp -o libb.so # wrong (but links) $ g++ -L. -lb a.cpp # wrong, as above $ g++ -Wl,--as-needed -L. -lb a.cpp # wrong, as above $ g++ a.cpp -L. -lb # wrong, missing libd.so $ g++ a.cpp -L. -ld -lb # wrong order (works on some dissortingbutions) $ g++ -Wl,--as-needed a.cpp -L. -ld -lb # wrong order (like static libs) $ g++ -Wl,--as-needed a.cpp -L. -lb -ld # "right" 

Si vous regardez maintenant quelles dépendances le binary a, vous notez que le binary lui-même dépend également de libd , pas seulement de la libb comme il se doit. Le fichier binary devra être relié si libb dépend plus tard d’une autre bibliothèque, si vous le faites de cette façon. Et si quelqu’un charge la libb utilisant dlopen à l’exécution (pensez à charger dynamicment les plugins), l’appel échouera également. Donc, le "right" devrait aussi être wrong .

Le lieur GNU ld est un soi-disant éditeur de liens intelligent. Il gardera une trace des fonctions utilisées par les bibliothèques statiques précédentes, rejetant en permanence les fonctions qui ne sont pas utilisées à partir de ses tables de recherche. Le résultat est que si vous liez une bibliothèque statique trop tôt, les fonctions de cette bibliothèque ne sont plus disponibles pour les bibliothèques statiques ultérieurement sur la ligne de liaison.

L’éditeur de liens UNIX typique fonctionne de gauche à droite, donc mettez toutes vos bibliothèques dépendantes à gauche, et celles qui satisfont ces dépendances à droite de la ligne de lien. Vous pouvez constater que certaines bibliothèques dépendent d’autres alors que d’autres bibliothèques en dépendent. C’est là que ça se complique. En ce qui concerne les références circulaires, corrigez votre code!

Voici un exemple pour expliquer comment les choses fonctionnent avec GCC lorsque des bibliothèques statiques sont impliquées. Alors supposons que nous avons le scénario suivant:

  • myprog.o – contenant main() fonction main() , dépendante de libmysqlclient
  • libmysqlclient – statique, par exemple (vous préféreriez la bibliothèque partagée, bien sûr, car libmysqlclient est énorme); dans /usr/local/lib ; et dépendant de choses de libz
  • libz (dynamic)

Comment pouvons-nous associer cela? (Remarque: exemples de compilation sur Cygwin en utilisant gcc 4.3.4)

 gcc -L/usr/local/lib -lmysqlclient myprog.o # undefined reference to `_mysql_init' # myprog depends on libmysqlclient # so myprog has to come earlier on the command line gcc myprog.o -L/usr/local/lib -lmysqlclient # undefined reference to `_uncompress' # we have to link with libz, too gcc myprog.o -lz -L/usr/local/lib -lmysqlclient # undefined reference to `_uncompress' # libz is needed by libmysqlclient # so it has to appear *after* it on the command line gcc myprog.o -L/usr/local/lib -lmysqlclient -lz # this works 

Si vous ajoutez -Wl,--start-group aux indicateurs du lieur, il ne se soucie pas de leur ordre ou de leurs dépendances circulaires.

Sur Qt, cela signifie append:

 QMAKE_LFLAGS += -Wl,--start-group 

Économise beaucoup de temps et ne semble pas ralentir la liaison (ce qui prend beaucoup moins de temps que la compilation).

Une autre alternative serait de spécifier deux fois la liste des bibliothèques:

 gcc prog.o libA.a libB.a libA.a libB.a -o prog.x 

En faisant cela, vous n’avez pas à vous préoccuper de la bonne séquence car la référence sera résolue dans le deuxième bloc.

Vous pouvez utiliser l’option -Xlinker.

 g++ -o foobar -Xlinker -start-group -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a -Xlinker -end-group 

est presque égal à

 g++ -o foobar -Xlinker -start-group -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a -Xlinker -end-group 

Prudent !

  1. L’ordre dans un groupe est important! Voici un exemple: une bibliothèque de débogage a une routine de débogage, mais la bibliothèque non-débogage a une version faible de la même. Vous devez mettre la bibliothèque de débogage FIRST dans le groupe ou vous allez résoudre à la version non-débogage.
  2. Vous devez faire précéder chaque bibliothèque de la liste de groupes avec -Xlinker

Une astuce rapide qui m’a fait trébucher: si vous invoquez l’éditeur de liens en tant que “gcc” ou “g ++”, alors en utilisant “–start-group” et “–end-group” ne passerez pas ces options au linker – il ne signalera pas non plus une erreur. Si vous ne tenez pas la commande de bibliothèque, le lien avec des symboles non définis échouera.

Vous devez les écrire en tant que “-Wl, – start-group” etc. pour indiquer à GCC de transmettre l’argument à l’éditeur de liens.

Je l’ai beaucoup vu, certains de nos modules sont plus de 100 bibliothèques de notre code plus système et de nos bibliothèques tierces.

Selon les différents linkers HP / Intel / GCC / SUN / SGI / IBM / etc, vous pouvez obtenir des fonctions / variables non résolues, etc. Sur certaines plateformes, vous devez lister deux fois les bibliothèques.

Pour la plupart, nous utilisons une hiérarchie structurée de bibliothèques, de cœurs, de plates-formes, de différentes couches d’abstraction, mais pour certains systèmes, vous devez toujours jouer avec l’ordre dans la commande de lien.

Une fois que vous avez trouvé une solution, documentez-la pour que le développeur suivant ne soit pas obligé de résoudre le problème.

Mon ancien conférencier disait: ” haute cohésion et faible couplage “, c’est toujours vrai aujourd’hui.

L’ordre des liens compte certainement, du moins sur certaines plates-formes. J’ai vu des accidents pour des applications liées à des bibliothèques dans le mauvais ordre (où mauvais signifie A lié avant B mais B dépend de A).

J’imagine que c’est parce que certaines de ces bibliothèques ont des dépendances sur d’autres bibliothèques, et si elles n’ont pas encore été liées, vous obtiendrez des erreurs de l’éditeur de liens.