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?
Réponses:
(Voir l'historique de cette réponse pour obtenir le texte 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;
$ g++ -c b.cpp -o b.o
$ ar cr libb.a b.o
$ g++ -c d.cpp -o d.o
$ ar cr libd.a d.o
$ 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 effectue une 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 objets 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 en premier, 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 y a un cycle. Vous pouvez résoudre ce problème en enfermant les bibliothèques cycliquement dépendantes par -(
et -)
, comme -( -la -lb -)
(vous devrez peut-être échapper aux parens, comme -\(
et -\)
). L'éditeur de liens recherche ensuite ces bibliothèques incluses plusieurs fois pour s'assurer que les dépendances cycliques sont résolues. Alternativement, vous pouvez spécifier les bibliothèques plusieurs fois, de sorte que chacune est l'une devant l'autre:-la -lb -la
.
$ 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 distributions)
$ 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 objets du programme. La différence ici par rapport aux bibliothèques statiques est que vous n'avez pas à vous soucier des dépendances des bibliothèques les unes contre les autres, car les bibliothèques dynamiques trient elles-mêmes leurs dépendances .
Certaines distributions récentes semblent utiliser par défaut le --as-needed
indicateur de éditeur de liens, qui impose que les fichiers objets du programme passent avant les bibliothèques dynamiques. Si cet indicateur est passé, l'éditeur de liens ne sera pas lié à des bibliothèques qui ne sont pas réellement nécessaires à l'exécutable (et il le détecte de gauche à droite). Ma récente distribution archlinux n'utilise pas cet indicateur par défaut, donc il n'a pas donné d'erreur pour ne pas suivre l'ordre correct.
Il n'est pas correct d'omettre la dépendance de b.so
contre d.so
lors de la création de la première. Vous devrez alors spécifier la bibliothèque lors de la liaison a
, mais a
vous n'avez pas vraiment besoin de l'entier b
lui-même, donc il ne devrait pas être fait pour se soucier b
des propres dépendances de.
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 distributions)
$ 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 examinez maintenant les dépendances du binaire, vous noterez que le binaire lui-même dépend également libd
, pas seulement libb
comme il se doit. Le binaire devra être relié si libb
plus tard dépend d'une autre bibliothèque, si vous le faites de cette façon. Et si quelqu'un d'autre se charge d' libb
utiliser dlopen
au moment de l'exécution (pensez à charger dynamiquement les plugins), l'appel échouera également. Donc, le "right"
devrait aussi être un wrong
.
lorder
+ tsort
. Mais parfois, il n'y a pas d'ordre, si vous avez des références cycliques. Ensuite, il vous suffit de parcourir la liste des bibliothèques jusqu'à ce que tout soit résolu.
L'éditeur de liens GNU ld est un éditeur de liens intelligent. Il gardera une trace des fonctions utilisées par les bibliothèques statiques précédentes, jetant 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 plus tard sur la ligne de liaison.
L'éditeur de liens UNIX typique fonctionne de gauche à droite, alors placez toutes vos bibliothèques dépendantes à gauche et celles qui satisfont ces dépendances à droite de la ligne de liaison. 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 montrer clairement comment les choses fonctionnent avec GCC lorsque des bibliothèques statiques sont impliquées. Supposons donc que nous avons le scénario suivant:
myprog.o
- contenant la main()
fonction, dépend delibmysqlclient
libmysqlclient
- statique, pour les besoins de l'exemple (vous préférez la bibliothèque partagée, bien sûr, car elle libmysqlclient
est énorme); dans /usr/local/lib
; et dépend de choses delibz
libz
(dynamique)Comment lions-nous cela? (Remarque: exemples de compilation sur Cygwin à l'aide de 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 de l'éditeur de liens, peu importe l'ordre dans lequel ils se trouvent ou s'il existe des dépendances circulaires.
Sur Qt, cela signifie ajouter:
QMAKE_LFLAGS += -Wl,--start-group
Économise beaucoup de temps et ne semble pas ralentir beaucoup la liaison (ce qui prend beaucoup moins de temps que la compilation de toute façon).
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 !
Un petit conseil qui m'a fait trébucher: si vous appelez l'éditeur de liens en tant que "gcc" ou "g ++", alors l'utilisation de "--start-group" et "--end-group" ne transmettra pas ces options au éditeur de liens - il ne signalera pas non plus d'erreur. Il échouera simplement le lien avec des symboles non définis si vous avez mal commandé la bibliothèque.
Vous devez les écrire comme "-Wl, - start-group" etc. pour dire à GCC de passer l'argument à l'éditeur de liens.
L'ordre des liens est certainement important, du moins sur certaines plateformes. J'ai vu des plantages 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'ai beaucoup vu cela, certains de nos modules lient plus de 100 bibliothèques de notre code plus le système et des bibliothèques tierces.
En fonction des différents éditeurs de liens HP / Intel / GCC / SUN / SGI / IBM / etc, vous pouvez obtenir des fonctions / variables non résolues, etc. Sur certaines plates-formes, vous devez répertorier les bibliothèques deux fois.
Pour la plupart, nous utilisons une hiérarchie structurée de bibliothèques, noyau, plate-forme, 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 afin que le développeur suivant n'ait pas à la retravailler.
Mon ancien conférencier disait « haute cohésion et faible couplage », c'est toujours vrai aujourd'hui.
gcc
changé pour un comportement plus strict (relativement) récemment.