La liaison d’un programme C directement avec ld échoue avec une référence non définie à `__libc_csu_fini`

J’essaie de comstackr un programme C sous Linux. Cependant, par curiosité, j’essaie d’exécuter certaines étapes à la main: j’utilise:

  • l’interface gcc pour produire du code assembleur
  • puis exécutez l’assembleur GNU pour obtenir un fichier object
  • puis reliez-le au runtime C pour obtenir un exécutable fonctionnel.

Maintenant, je suis coincé avec la partie de liaison.

Le programme est un “Hello world” très basique:

#include  int main() { printf("Hello\n"); return 0; } 

J’utilise la commande suivante pour produire le code d’assembly:

 gcc hello.c -S -masm=intel 

Je dis à gcc de quitter après la compilation et de vider le code assembleur avec la syntaxe Intel.

Ensuite, j’utilise l’assembleur GNU pour produire le fichier object:

 as -o hello.o hello.s 

Ensuite, j’essaie d’utiliser ld pour produire l’exécutable final:

 ld hello.o /usr/lib/libc.so /usr/lib/crt1.o -o hello 

Mais je continue à recevoir le message d’erreur suivant:

 /usr/lib/crt1.o: In function `_start': (.text+0xc): undefined reference to `__libc_csu_fini' /usr/lib/crt1.o: In function `_start': (.text+0x11): undefined reference to `__libc_csu_init' 

Les symboles __libc_csu_fini/init semblent faire partie de la glibc, mais je ne les trouve nulle part! J’ai essayé de lier statiquement contre libc (contre /usr/lib/libc.a ) avec le même résultat.

Quel pourrait être le problème?

J’ai trouvé un autre article contenant un indice: -dynamic-linker /lib/ld-linux.so.2 .

Essaye ça:

 $ gcc hello.c -S -masm=intel $ as -o hello.o hello.s $ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o hello.o -lc /usr/lib/crtn.o $ ./hello hello, world $ 

/usr/lib/libc.so est un script de l’éditeur de liens qui demande à l’éditeur de liens d’extraire la bibliothèque partagée /lib/libc.so.6 et une partie non partagée, /usr/lib/libc_nonshared.a .

__libc_csu_init et __libc_csu_fini proviennent de /usr/lib/libc_nonshared.a . Ils ne sont pas trouvés car les références aux symboles dans les bibliothèques non partagées doivent apparaître avant l’archive qui les définit sur la ligne de l’éditeur de liens. Dans votre cas, /usr/lib/crt1.o (qui les référence) apparaît après /usr/lib/libc.so (qui les attire), donc cela ne fonctionne pas.

Fixer la commande sur la ligne de lien vous mènera un peu plus loin, mais vous aurez probablement un nouveau problème, où __libc_csu_init et __libc_csu_fini (qui sont maintenant trouvés) ne peuvent pas trouver _init et _fini . Pour appeler les fonctions de la bibliothèque C, vous devez également associer /usr/lib/crti.o (après crt1.o mais avant la bibliothèque C) et /usr/lib/crtn.o ( après la bibliothèque C), qui contiennent les fonctions d’initialisation. et code de finalisation.

Ajouter ceux-ci devrait vous donner un exécutable lié avec succès. Cela ne fonctionne toujours pas, car il utilise la bibliothèque C liée dynamicment sans spécifier ce qu’est l’éditeur de liens dynamic. Vous devrez également indiquer à l’éditeur de liens, avec quelque chose comme -dynamic-linker /lib/ld-linux.so.2 (pour 32 bits au moins, le nom de l’éditeur de liens dynamic standard varie d’une plate-forme à l’autre).

Si vous faites tout cela (essentiellement selon la réponse de Rob), vous obtiendrez quelque chose qui fonctionne dans des cas simples. Mais vous pouvez rencontrer d’autres problèmes avec un code plus complexe, car GCC fournit certaines de ses propres routines de bibliothèque qui peuvent être nécessaires si votre code utilise certaines fonctionnalités. Ceux-ci seront enterrés quelque part au plus profond des répertoires d’installation de GCC …

Vous pouvez voir ce que gcc fait en l’exécutant soit avec l’option -v (qui vous montrera les commandes qu’il appelle en cours d’exécution), soit avec l’option -### (qui affiche simplement les commandes à exécuter, avec tous les arguments citent, mais n’exécutent réellement rien). La sortie sera source de confusion, à moins que vous ne sachiez qu’elle appelle habituellement ld indirectement via l’un de ses propres composants, collect2 (qui est utilisé pour coller des appels de constructeur C ++ au bon moment).

En supposant qu’un appel normal de gcc -o hello hello.c génère une version de travail, exécutez la commande suivante:

 gcc --verbose -o hello hello.c 

et gcc vous dira comment il relie les choses. Cela devrait vous donner une bonne idée de tout ce dont vous pourriez avoir besoin de rendre compte dans votre étape de lien.

Voici comment je l’ai corrigé sur Ubuntu 11.10:

 apt-get remove libc-dev 

Dites oui pour supprimer tous les packages, mais copiez la liste pour une réinstallation ultérieure.

 apt-get install libc-dev 

Dans Ubuntu 14.04 (GCC 4.8), la commande de liaison minimale est la suivante:

 ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \ /usr/lib/x86_64-linux-gnu/crt1.o \ /usr/lib/x86_64-linux-gnu/crti.o \ -L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \ -lc -lgcc -lgcc_s \ hello.o \ /usr/lib/x86_64-linux-gnu/crtn.o 

Bien qu’elles ne soient peut-être pas nécessaires, vous devez également vous connecter à -lgcc et à -lgcc_s , car GCC peut émettre des appels aux fonctions présentes dans ces bibliothèques pour les opérations que votre matériel ne met pas en œuvre de manière native, par exemple long long int opérations de long long int sur 32 bits. Voir aussi: Ai-je vraiment besoin de libgcc?

Je devais append:

  -L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \ 

car le script de l’éditeur de liens par défaut n’inclut pas ce répertoire, et c’est là que se trouvait libgcc.a .

Comme mentionné par Michael Burr, vous pouvez trouver les chemins avec gcc -v . Plus précisément, vous avez besoin de:

 gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n' 

Si vous utilisez un système d’exploitation 64 bits, votre glibc (-devel) est peut-être endommagée. En regardant ceci et cela, vous pouvez trouver ces 3 solutions possibles:

  1. append lib64 à LD_LIBRARY_PATH
  2. utiliser lc_noshared
  3. réinstaller glibc-devel

Étant donné que vous effectuez le processus de liaison à la main, vous oubliez de lier l’initialiseur d’exécution C, ou ce que l’on appelle.

Pour ne pas entrer dans les détails de l’endroit et de ce que vous devez lier pour votre plate-forme, après avoir récupéré votre fichier intel asm, utilisez gcc pour générer (comstackr et lier) votre exécutable.

faire simplement gcc hello.c -o hello devrait fonctionner.