web-dev-qa-db-fra.com

GCC liant dynamiquement libc statique et une autre bibliothèque, revisité?

Les questions suivantes sont pertinentes mais ne répondent pas à ma question:

Liaison partiellement statique et partiellement dynamique dans GCC

Lier une bibliothèque dynamique à une bibliothèque statique qui lie à d'autres bibliothèques statiques

GCC: liaison statique uniquement avec certaines bibliothèques

Lien statique de la fonction de bibliothèque partagée dans gcc

J'ai posé une question très similaire plus tôt, mais comme la question précédente que j'ai commencée est devenue un peu encombrée dans la section des commentaires et n'a pas été entièrement répondue (mais je l'ai signalée comme ayant répondu car c'était un bon effort et y ai au moins partiellement répondu). poser une nouvelle question. La question est précisément de savoir comment lier libc en tant que statique, tout en liant dynamiquement une autre bibliothèque (par exemple libm). Cela a été suggéré que cela ne peut pas être fait dans la première question, est-ce vrai? Si c'est le cas, il serait très intéressant de savoir pourquoi.

Est-il même possible de le faire? Quelqu'un a fait un commentaire (qui a été supprimé pour une raison quelconque, c'était peut-être incorrect?) Qu'il est possible, mais il doit alors aussi exister un lien dynamique version de libc, car elle sera requise par la bibliothèque dynamique (par exemple, libm dynamique nécessitera libc dynamique (?)).

C'est bien pour moi, mais il n'est pas évident pour moi de dire à GCC de le faire, c'est-à-dire un lien dans libc à la fois statique et dynamique. Comment dois-je procéder (j'ai fait quelques tentatives, certaines sont présentées plus loin dans la question)? Ou existe-t-il une autre façon de faire ce que je veux?

Nous voyons d'abord qu'en exécutant simplement gcc test.c -lm, tout est lié dynamiquement, comme suit:

$ gcc test.c -lm
$ ldd a.out 
        linux-vdso.so.1 (0x00007fffb37d1000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f3b0eeb6000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f3b0eb10000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f3b0f1b0000)

Pour lier uniquement libm en tant que statique, tout en permettant à libc de rester dynamique, nous pouvons le faire (comme l'a souligné Z boson dans l'une des questions susmentionnées):

$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libm.a

$ ldd a.out 
        linux-vdso.so.1 (0x00007fff747ff000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f09aaa0c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f09aadb2000)

Cependant, tenter la même procédure pour lier libc static et libm dynamic ne semble pas fonctionner:

$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a -lm
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status

Que veux dire ce message d'erreur?

Quelques autres tentatives (la plupart ont également été incluses dans ma première question):

$ gcc test.c /usr/lib64/libc.a
linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
urned 1 exit status
$ gcc test.c -Wl,-Bdynamic -lm -Wl,-Bstatic -lc
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
collect2: error: ld returned 1 exit status
$ gcc -Wl,-Bdynamic -lm -Wl,-Bstatic -lc test.c
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
collect2: error: ld returned 1 exit status
$ gcc -Wl,-Bstatic -lc -Wl,-Bdynamic -lm test.c
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status
$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.so -lm
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status
$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.so /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a -lm

Notez que le dernier a été compilé/lié avec succès. Cependant libc n'a pas été lié statiquement, seulement dynamiquement, c'est donc une autre tentative qui a échoué.

Le programme de test est simplement le suivant:

$ cat test.c 
#include <stdio.h>
#include <math.h>

int main(int argc, char **argv)
{
        int i;
        int result;

        for(i = 0; i < 65535; i++) {
                result = sin(i);
        }

        return 0;
}

Éditer:

J'ai également essayé le statifiant et l'hermine, comme suggéré dans cette question:

Lien statique de la fonction de bibliothèque partagée dans gcc

Ni l'un ni l'autre ne fonctionne.

16

Fondamentalement, votre première approche est la bonne façon de procéder:

gcc test.c libc.a -lm

Après que gcc ait ajouté les bibliothèques implicites, il ressemblera (conceptuellement) à ceci:

gcc crt1.o test.c libc.a -lm -lc -lgcc -lc

Cela signifie donc que toutes les fonctions libc appelées par crt1.o Ou test.c Seront extraites de libc.a Et liées statiquement, tandis que toutes les fonctions appelées niquement à partir de libm ou libgcc sera lié dynamiquement (mais il réutilisera les fonctions statiques si libm appelle quelque chose qui a déjà été récupéré).

L'éditeur de liens commence toujours par le fichier/bibliothèque le plus à gauche et fonctionne à droite; ça ne remonte jamais. Les fichiers .c Et .o Sont liés inconditionnellement, mais les fichiers .a Et les options -l Sont uniquement utilisés pour rechercher des fonctions déjà référencées mais pas encore définies. Par conséquent, une bibliothèque à gauche est inutile (et -lc Doit apparaître deux fois car -lc Dépend de -lgcc, Et -lgcc Dépend de -lc). L'ordre des liens est important!

Malheureusement, vous semblez avoir été déjoué par ce qui pourrait être un bogue dans strcmp (ou plutôt dans la libc qui contient strcmp): la chose STT_GNU_IFUNC Est une fonctionnalité intelligente qui permet d'inclure plusieurs versions d'une fonction et de sélectionner la plus optimale au moment de l'exécution, en fonction du matériel disponible. Je ne suis pas sûr, mais il semble que cette fonctionnalité ne soit disponible que dans une construction PIE (Position Independent Executable) ou une bibliothèque partagée.

Pourquoi cela serait dans un libc.a Statique est un mystère pour moi, mais il existe une solution simple: implémentez votre propre strcmp (une implémentation basique et lente n'est que quelques lignes de C), et liez-le dans avantlibc.a.

gcc test.c mystrcmp.c libc.a -lm

Alternativement, vous pouvez extraire les fonctions de libc.a Que vous voulez vraiment, et lier uniquement celles de manière statique:

ar x libc.a
gcc test.c somefile.o -lm

ar est vers les fichiers .a, comme tar vers les fichiers .tar, bien que l'utilisation de la commande varie un peu, donc cet exemple extrait les .o à partir du fichier .a, puis les lie explicitement.

23
ams

Sur la base de la réponse de ams, j'ai fait le suivi

mystrcmp.c

int strcmp(const char *s1, const char *s2) {
}

Compiler

gcc -c test.c
gcc -c mystrcmp.c

Fichiers d'installation

ln -s `gcc -print-file-name=crt1.o`
ln -s `gcc -print-file-name=crti.o`
ln -s `gcc -print-file-name=crtn.o`
ln -s `gcc -print-file-name=libgcc_eh.a`
ln -s `gcc -print-file-name=libc.a`
ln -s `gcc -print-file-name=libm.so`

Lien

ld -m elf_x86_64 -o math crt1.o crti.o test.o mystrcmp.o libc.a libgcc_eh.a libc.a libm.so -dynamic-linker /lib64/ld-linux-x86-64.so.2 crtn .o

Ce lien et fonctionne correctement. Cependant, ldd affiche

linux-vdso.so.1 =>  (0x00007fff51911000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8182470000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f81820a9000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8182793000)

Il semble que dynamique libm nécessite dynamique libc. En fait, c'est facile à montrer

rapports ldd libm.so

linux-vdso.so.1 =>  (0x00007fff20dfe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcaf74fe000)
/lib64/ld-linux-x86-64.so.2 (0x00007fcaf7bed000)

Il est donc impossible de lier à libm.so sans lier également libc.so à moins que vous parveniez à compiler libm sans dépendre de libc.

5
Z boson