web-dev-qa-db-fra.com

Pourquoi l'ordre de l'option '-l' dans gcc est-il important?

J'essaie de compiler un programme qui utilise la bibliothèque dis86 . En fait, j'utilise un exemple de programme donné dans le ser-manual de la bibliothèque. Mais lors de la compilation, cela donne une erreur. Les erreurs que j'obtiens sont:

example.c:(.text+0x7): undefined reference to 'ud_init'
example.c:(.text+0x7): undefined reference to 'ud_set_input_file'
.
.
example.c:(.text+0x7): undefined reference to 'ud_insn_asm'

La commande que j'utilise est:

$ gcc -ludis86 example.c -o example 

comme indiqué dans le manuel d'utilisation.

De toute évidence, l'éditeur de liens n'est pas en mesure de lier la bibliothèque libudis. Mais si je change ma commande en:

$ gcc example.c -ludis86 -o example 

Ça commence à fonctionner. Alors, s'il vous plaît, quelqu'un peut-il expliquer quel est le problème avec la première commande?

69
user1129237

Parce que c'est ainsi que fonctionne l'algorithme de liaison utilisé par GNU (du moins lorsqu'il s'agit de lier des bibliothèques statiques). L'éditeur de liens est un éditeur de liens à passage unique et il ne revisite pas les bibliothèques une fois qu'elles ont été vues .

Une bibliothèque est une collection (une archive) de fichiers objets. Lorsque vous ajoutez une bibliothèque à l'aide de -l option, l'éditeur de liens ne prend pas inconditionnellement tous les fichiers objets de la bibliothèque. Il ne prend que les fichiers objets qui sont actuellement nécessaires, c'est-à-dire les fichiers qui résolvent certains symboles actuellement non résolus (en attente). Après cela, l'éditeur de liens oublie complètement cette bibliothèque.

La liste des symboles en attente est maintenue en permanence par l'éditeur de liens lorsque l'éditeur de liens traite les fichiers d'objets d'entrée, l'un après l'autre, de gauche à droite. Au fur et à mesure qu'il traite chaque fichier objet, certains symboles sont résolus et supprimés de la liste, d'autres symboles non résolus nouvellement découverts sont ajoutés à la liste.

Donc, si vous avez inclus une bibliothèque en utilisant -l, l'éditeur de liens utilise cette bibliothèque pour résoudre autant de symboles actuellement en attente que possible, puis oublie complètement cette bibliothèque. S'il plus tard découvre soudainement qu'il a maintenant besoin de fichiers objet supplémentaires de cette bibliothèque, l'éditeur de liens ne "reviendra" pas à cette bibliothèque pour récupérer ces fichiers objets supplémentaires. Il est déjà trop tard.

Pour cette raison, c'est toujours une bonne idée d'utiliser -l option late dans la ligne de commande de l'éditeur de liens, de sorte qu'au moment où l'éditeur de liens arrive à ce -l il peut déterminer de manière fiable les fichiers objets dont il a besoin et ceux dont il n'a pas besoin. Placer le -l comme premier paramètre de l'éditeur de liens n'a généralement aucun sens: au tout début, la liste des symboles en attente est vide (ou, plus précisément, se compose d'un seul symbole main), ce qui signifie que le l'éditeur de liens ne prendra rien du tout de la bibliothèque.

Dans votre cas, votre fichier objet example.o contient des références aux symboles ud_init, ud_set_input_file etc. L'éditeur de liens doit d'abord recevoir ce fichier objet. Il ajoutera ces symboles à la liste des symboles en attente. Après cela, vous pouvez utiliser -l option pour ajouter votre bibliothèque: -ludis86. L'éditeur de liens recherchera votre bibliothèque et en prendra tout ce qui résout ces symboles en attente.

Si vous placez le -ludis86 option en premier dans la ligne de commande, l'éditeur de liens va effectivement ignorer votre bibliothèque, car au début il ne sait pas qu'il aura besoin de ud_init, ud_set_input_file etc. Plus tard, lors du traitement example.o il découvrira ces symboles et les ajoutera à la liste des symboles en attente. Mais ces symboles resteront irrésolus jusqu'à la fin, puisque -ludis86 a déjà été traité (et effectivement ignoré).

Parfois, lorsque deux (ou plusieurs) bibliothèques se réfèrent l'une à l'autre de manière circulaire, il peut même être nécessaire d'utiliser le -l option deux fois avec la même bibliothèque, pour donner à l'éditeur de liens deux chances de récupérer les fichiers objets nécessaires de cette bibliothèque.

101
AnT

J'ai touché ce même problème il y a quelque temps. L'essentiel est que les outils gnu ne "rechercheront pas" toujours dans la liste des bibliothèques pour résoudre les symboles manquants. Les correctifs faciles sont les suivants:

  1. Spécifiez simplement les libs et objs dans l'ordre des dépendances (comme vous l'avez découvert ci-dessus)

  2. OU si vous avez une dépendance circulaire (où libA fait référence à une fonction dans libB, mais libB fait référence à une fonction dans libA), alors spécifiez simplement les libs sur la ligne de commande deux fois. C'est ce que suggère également la page de manuel. Par exemple.

    gcc foo.c -lfoo -lbar -lfoo
    
  3. Utilisez le -( et -) params pour spécifier un groupe d'archives ayant de telles dépendances circulaires. Regardez le GNU linker manual for --start-group et --end-group. Voir ici pour plus de détails.

Lorsque vous utilisez l'option 2 ou 3, vous introduisez probablement un coût de performance pour la liaison. Si vous n'avez pas grand-chose à lier, cela n'a peut-être pas d'importance.

8
selbie

Ou utilisez rescan

à partir de la page 41 de Oracle Solaris 11.1 Linkers and Libraries Guide :

Des interdépendances peuvent exister entre les archives, de sorte que l'extraction des membres d'une archive doit être résolue en extrayant les membres d'une autre archive. Si ces dépendances sont cycliques, les archives doivent être spécifiées à plusieurs reprises sur la ligne de commande pour satisfaire les références précédentes.

$ cc -o prog .... -lA -lB -lC -lA -lB -lC -lA 

La détermination et la maintenance de spécifications d'archivage répétées peuvent être fastidieuses.

L'option -z rescan-now simplifie ce processus. L'option -z rescan-now est traitée par l'éditeur de liens immédiatement lorsque l'option est rencontrée sur la ligne de commande. Toutes les archives qui ont été traitées à partir de la ligne de commande avant cette option sont immédiatement retraitées. Ce traitement tente de localiser des membres d'archive supplémentaires qui résolvent les références de symboles. Cette nouvelle analyse des archives se poursuit jusqu'à ce qu'un passage sur la liste d'archives se produise dans lequel aucun nouveau membre n'est extrait. L'exemple précédent peut être simplifié comme suit.

$ cc -o prog .... -lA -lB -lC -z rescan-now 

Les options -z rescan-start et -z rescan-end peuvent également être utilisées pour regrouper des archives mutuellement dépendantes dans un groupe d'archives. Ces groupes sont retraités par l'éditeur de liens immédiatement lorsque le délimiteur de fermeture est rencontré sur la ligne de commande. Les archives trouvées dans le groupe sont retraitées pour tenter de localiser des membres d'archives supplémentaires qui résolvent les références de symboles. Cette nouvelle analyse des archives se poursuit jusqu'à ce qu'un passage sur le groupe d'archives se produise dans lequel aucun nouveau membre n'est extrait. En utilisant des groupes d'archives, l'exemple précédent peut être écrit comme suit.

$ cc -o prog .... -z rescan-start -lA -lB -lC -z rescan-end
4
flerb