liaison statique uniquement certaines bibliothèques

108

Comment puis-je relier statiquement uniquement certaines bibliothèques spécifiques à mon binaire lors de la liaison avec GCC?

gcc ... -static ...essaie de lier statiquement toutes les bibliothèques liées, mais je n'ai pas la version statique de certaines d'entre elles (par exemple: libX11).

peoro
la source

Réponses:

112

gcc -lsome_dynamic_lib code.c some_static_lib.a

Šimon Tóth
la source
5
Liez les bibliothèques après les fichiers objets - en particulier les bibliothèques statiques. Dans les versions anciennes et modernes de l'environnement de lien (je ne suis pas sûr du statu quo pour les versions modestement anciennes à partir de novembre 2010), répertorier la bibliothèque statique avant le code.cfichier garantit que les symboles qu'il contient seront ignorés à moins qu'il ne se produise une main()fonction dans l'un des fichiers objets de la bibliothèque.
Jonathan Leffler
44
Coule vous veuillez expliquer comment cela fonctionne? Les réponses au code uniquement ne sont pas utiles pour les débutants.
jb.
8
@jb par défaut, gcc se lie dynamiquement. Lorsque vous utilisez -lsome_dynamic_lib, il est lié dynamiquement comme prévu. Mais, lorsque gcc reçoit explicitement une bibliothèque statique, il essaiera toujours de la lier de manière statique. Il y a cependant quelques détails délicats sur l'ordre dans lequel les symboles sont résolus; Je ne sais pas trop comment cela fonctionne. J'ai appris qu'en cas de doute, essayez de réorganiser l'ordre des drapeaux de la bibliothèque :-)
bchurchill
4
il y a un problème de lincense si vous liez statiquement par exemple une bibliothèque GPL
HiB
1
@HiB GPL s'applique de la même manière aux liens statiques et dynamiques
osvein
50

Vous pouvez également utiliser l' ldoption-Bdynamic

gcc <objectfiles> -static -lstatic1 -lstatic2 -Wl,-Bdynamic -ldynamic1 -ldynamic2

Toutes les bibliothèques suivantes (y compris celles système liées automatiquement par gcc) seront liées dynamiquement.

Dmitry Yudakov
la source
19
-Wl, -Bdynamic nécessite GNU ld, donc cette solution ne fonctionne pas sur les systèmes où gcc utilise le système ld (par exemple Mac OS X).
pts
33
gcc objectfiles -o program -Wl,-Bstatic -ls1 -ls2 -Wl,-Bdynamic -ld1 -ld2

vous pouvez également utiliser: des -static-libgcc -static-libstdc++indicateurs pour les bibliothèques gcc

gardez à l'esprit que si libs1.soet les libs1.adeux existent, l'éditeur de liens choisira libs1.sosi c'est avant -Wl,-Bstaticou après -Wl,-Bdynamic. N'oubliez pas de passer -L/libs1-library-location/avant d'appeler -ls1.

wgodoy
la source
1
Au moins, cette solution fonctionne pour un lien statique avec libgomp!
Jérôme
Cela fonctionne bien pour moi, tandis que l'utilisation de -staticquelque part dans la commande échoue (je suppose qu'elle essaie de lier plus de choses statiquement que seulement les bibliothèques que je veux).
nh2
4
NB. L'ordre de -Wl,-Bstaticet -Wl,-Bdynamicest important.
Pavel Vlasov
27

Depuis la page de manuel de ld(cela ne fonctionne pas avec gcc), en se référant à l' --staticoption:

Vous pouvez utiliser cette option plusieurs fois sur la ligne de commande: elle affecte la recherche dans la bibliothèque des options -l qui la suivent.

Une solution consiste à placer vos dépendances dynamiques avant l' --staticoption sur la ligne de commande.

Une autre possibilité est de ne pas utiliser --static, mais plutôt de fournir le nom / chemin complet du fichier objet statique (c'est-à-dire sans utiliser l'option -l) pour créer une liaison statique dans une bibliothèque spécifique. Exemple:

# echo "int main() {}" > test.cpp
# c++ test.cpp /usr/lib/libX11.a
# ldd a.out
linux-vdso.so.1 =>  (0x00007fff385cc000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f9a5b233000)
libm.so.6 => /lib/libm.so.6 (0x00007f9a5afb0000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007f9a5ad99000)
libc.so.6 => /lib/libc.so.6 (0x00007f9a5aa46000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9a5b53f000)

Comme vous pouvez le voir dans l'exemple, libX11n'est pas dans la liste des bibliothèques liées dynamiquement, car il était lié statiquement.

Attention: un .sofichier est toujours lié dynamiquement, même lorsqu'il est spécifié avec un nom de fichier / chemin complet.

ypnos
la source
Quelle est la relation entre libX11.a et la sortie de ldd a.out?
Raffi Khatchadourian
1
Ah, je vois. lddrenvoie les bibliothèques partagées requises et libX11 n'apparaît pas dans cette liste.
Raffi Khatchadourian
ce n'est pas clair. vous dites «cette option» et «cette option». quelle option?
Octopus
19

Le problème tel que je le comprends est le suivant. Vous disposez de plusieurs bibliothèques, certaines statiques, d'autres dynamiques et d'autres à la fois statiques et dynamiques. Le comportement par défaut de gcc est de lier "principalement dynamique". Autrement dit, gcc crée des liens vers des bibliothèques dynamiques lorsque cela est possible, mais revient autrement aux bibliothèques statiques. Lorsque vous utilisez l' option -static pour gcc, le comportement consiste à ne lier que les bibliothèques statiques et à quitter avec une erreur si aucune bibliothèque statique ne peut être trouvée, même s'il existe une bibliothèque dynamique appropriée.

Une autre option, que j'ai à plusieurs reprises souhaité que gcc ait, est ce que j'appelle -mostly-static et est essentiellement l'opposé de -dynamic (la valeur par défaut). -mostly-static , s'il existait, préférerait se lier à des bibliothèques statiques mais retomberait dans des bibliothèques dynamiques.

Cette option n'existe pas mais elle peut être émulée avec l'algorithme suivant:

  1. Construire la ligne de commande de lien sans inclure -static .

  2. Parcourez les options de lien dynamique.

  3. Accumuler les chemins de bibliothèque, c'est-à-dire les options de la forme -L <lib_dir> dans une variable <lib_path>

  4. Pour chaque option de lien dynamique, c'est-à-dire celles de la forme -l <lib_name> , exécutez la commande gcc <lib_path> -print-file-name = lib <lib_name> .a et capturez la sortie.

  5. Si la commande imprime autre chose que ce que vous avez transmis, ce sera le chemin complet de la bibliothèque statique. Remplacez l'option de bibliothèque dynamique par le chemin complet de la bibliothèque statique.

Rincez et répétez jusqu'à ce que vous ayez traité toute la ligne de commande du lien. En option, le script peut également prendre une liste de noms de bibliothèques à exclure de la liaison statique.

Le script bash suivant semble faire l'affaire:

#!/bin/bash

if [ $# -eq 0 ]; then
    echo "Usage: $0 [--exclude <lib_name>]. . . <link_command>"
fi

exclude=()
lib_path=()

while [ $# -ne 0 ]; do
    case "$1" in
        -L*)
            if [ "$1" == -L ]; then
                shift
                LPATH="-L$1"
            else
                LPATH="$1"
            fi

            lib_path+=("$LPATH")
            echo -n "\"$LPATH\" "
            ;;

        -l*)
            NAME="$(echo $1 | sed 's/-l\(.*\)/\1/')"

            if echo "${exclude[@]}" | grep " $NAME " >/dev/null; then
                echo -n "$1 "
            else
                LIB="$(gcc $lib_path -print-file-name=lib"$NAME".a)"
                if [ "$LIB" == lib"$NAME".a ]; then
                    echo -n "$1 "
                else
                    echo -n "\"$LIB\" "
                fi
            fi
            ;;

        --exclude)
            shift
            exclude+=(" $1 ")
            ;;

        *) echo -n "$1 "
    esac

    shift
done

echo

Par exemple:

mostlyStatic gcc -o test test.c -ldl -lpthread

sur mon système renvoie:

gcc -o test test.c "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libdl.a" "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a"

ou avec une exclusion:

mostlyStatic --exclude dl gcc -o test test.c -ldl -lpthread

J'obtiens alors:

gcc -o test test.c -ldl "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a"
jcoffland
la source
7

Il existe également une -l:libstatic1.avariante (moins l deux-points) de l'option -l dans gcc qui peut être utilisée pour lier une bibliothèque statique (Merci à https://stackoverflow.com/a/20728782 ). Est-ce documenté? Pas dans la documentation officielle de gcc (ce qui n'est pas exact pour les bibliothèques partagées également): https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

-llibrary
-l library 

Recherchez la bibliothèque nommée bibliothèque lors de la liaison. (La deuxième alternative avec la bibliothèque comme argument séparé est uniquement pour la conformité POSIX et n'est pas recommandée.) ... La seule différence entre l'utilisation d'une option -l et la spécification d'un nom de fichier est que -l entoure la bibliothèque avec 'lib' et '.a' et recherche plusieurs répertoires.

Le doc binutils ld le décrit. L' -lnameoption recherchera libname.soalors l' libname.aajout du préfixe lib et .so(si activé pour le moment) ou du .asuffixe. Mais l' -l:nameoption ne recherchera exactement que le nom spécifié: https://sourceware.org/binutils/docs/ld/Options.html

-l namespec
--library=namespec

Ajoutez l'archive ou le fichier objet spécifié par namespecà la liste des fichiers à lier. Cette option peut être utilisée un nombre illimité de fois. S'il namespecest de la forme :filename, ld recherchera dans le chemin de la bibliothèque un fichier appelé filename, sinon il recherchera le chemin de la bibliothèque pour un fichier appelé libnamespec.a.

Sur les systèmes prenant en charge les bibliothèques partagées, ld peut également rechercher des fichiers autres que libnamespec.a. Plus précisément, sur les systèmes ELF et SunOS, ld recherchera dans un répertoire une bibliothèque appelée libnamespec.soavant d'en rechercher une appelée libnamespec.a. (Par convention, une .soextension indique une bibliothèque partagée.) Notez que ce comportement ne s'applique pas à :filename, qui spécifie toujours un fichier appelé filename.

L'éditeur de liens ne recherchera une archive qu'une seule fois, à l'emplacement où il est spécifié sur la ligne de commande. Si l'archive définit un symbole qui n'a pas été défini dans un objet apparaissant avant l'archive sur la ligne de commande, l'éditeur de liens inclura le (s) fichier (s) approprié (s) de l'archive. Cependant, un symbole non défini dans un objet apparaissant plus tard sur la ligne de commande n'entraînera pas l'éditeur de liens pour rechercher à nouveau l'archive.

Voir l' -(option pour un moyen de forcer l'éditeur de liens à rechercher des archives plusieurs fois.

Vous pouvez lister la même archive plusieurs fois sur la ligne de commande.

Ce type de recherche d'archive est standard pour les éditeurs de liens Unix. Cependant, si vous utilisez ld sous AIX, notez qu'il est différent du comportement de l'éditeur de liens AIX.

La variante -l:namespecest documentée depuis la version 2.18 de binutils (2007): https://sourceware.org/binutils/docs-2.18/ld/Options.html

osgx
la source
Cette option semble fonctionner là où tout le reste échoue. Nous venons de tomber sur un cas où nous devions relier statiquement libjsoncpp.a, car nos machines de construction produiraient des binaires liés à libjsocpp.so.0, alors que le système d'exploitation cible ne fournit que libjsoncpp.so.1. Jusqu'à ce que nous puissions éclaircir cette différence, c'était la seule solution qui a donné des résultats corrects dans notre cas.
Tomasz W
4

Certains chargeurs (linkers) fournissent des commutateurs pour activer et désactiver le chargement dynamique. Si GCC fonctionne sur un tel système (Solaris - et éventuellement d'autres), vous pouvez utiliser l'option appropriée.

Si vous savez quelles bibliothèques vous souhaitez lier statiquement, vous pouvez simplement spécifier le fichier de bibliothèque statique dans la ligne de lien - par chemin complet.

Jonathan Leffler
la source
6
Même si cette réponse a été acceptée, elle ne résout pas complètement le problème. Comme @peoro l'a expliqué, le problème qu'il essaie de résoudre est qu'il ne dispose pas de versions statiques de toutes les bibliothèques, ce qui implique qu'il aimerait relier le plus de bibliothèques statiquement possible. Voyez ma réponse.
jcoffland
2

à dynamique de la liaison et la bibliothèque statique dans une ligne, vous devez mettre libs statiques après libs dynamiques et les fichiers d'objets, comme ceci:

gcc -lssl main.o -lFooLib -o main

sinon, cela ne fonctionnera pas. il me faut un certain temps pour le comprendre.

Vincent
la source