statyczne łączenie tylko niektórych bibliotek

Odpowiedzi:

112

gcc -lsome_dynamic_lib code.c some_static_lib.a

Šimon Tóth
źródło
5
Połącz biblioteki po plikach obiektowych - zwłaszcza bibliotekach statycznych. W starożytnych i współczesnych wersjach środowiska link (nie jestem pewien status quo dla skromnie starych wersji na listopad 2010), umieszczenie biblioteki statycznej przed code.cplikiem gwarantuje, że symbole w niej będą ignorowane, chyba że main()funkcja w jednej z bibliotek plików obiektowych.
Jonathan Leffler
44
Czy mógłbyś wyjaśnić, jak to działa? Tylko kod odpowiedzi nie są pomocne dla początkujących.
jb.
8
@jb domyślnie, gcc łączy się dynamicznie. Kiedy używasz -lsome_dynamic_lib, zostaje on połączony dynamicznie zgodnie z oczekiwaniami. Ale kiedy gcc otrzymuje jawnie statyczną bibliotekę, zawsze będzie próbował łączyć ją statycznie. Istnieją jednak pewne skomplikowane szczegóły dotyczące kolejności, w jakiej symbole są rozpoznawane; Nie jestem pewien, jak to działa. Dowiedziałem się, że w razie wątpliwości spróbuj zmienić kolejność flag w bibliotece :-)
bchurchill
4
istnieje problem z lincense, jeśli łączysz statycznie na przykład bibliotekę GPL
HiB
1
@HiB GPL stosuje się w ten sam sposób do łączenia statycznego i dynamicznego
osvein
50

Możesz także użyć ldopcji-Bdynamic

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

Wszystkie biblioteki po nim (w tym biblioteki systemowe połączone automatycznie przez gcc) zostaną połączone dynamicznie.

Dmitry Yudakov
źródło
19
-Wl, -Bdynamic wymaga GNU ld, więc to rozwiązanie nie działa na systemach, w których gcc używa systemowego ld (np. Mac OS X).
pkt
33
gcc objectfiles -o program -Wl,-Bstatic -ls1 -ls2 -Wl,-Bdynamic -ld1 -ld2

możesz także użyć: -static-libgcc -static-libstdc++flagi dla bibliotek gcc

pamiętaj, że jeśli libs1.soi libs1.aoba istnieją, konsolidator wybierze, libs1.soczy jest przed, -Wl,-Bstaticczy po -Wl,-Bdynamic. Nie zapomnij przejść -L/libs1-library-location/przed zadzwonieniem -ls1.

wgodoy
źródło
1
Przynajmniej to rozwiązanie działa z linkami statycznymi przeciwko libgomp!
Jérôme
Działa to dobrze dla mnie, podczas gdy użycie -staticgdzieś w poleceniu kończy się niepowodzeniem (zakładam, że próbuje połączyć więcej rzeczy statycznie niż tylko wybrane biblioteki).
nh2
4
NB. Kolejność -Wl,-Bstatici -Wl,-Bdynamicjest ważna.
Pavel Vlasov
27

Ze strony podręcznika ld(to nie działa z gcc), odnosząc się do --staticopcji:

Możesz użyć tej opcji wiele razy w linii poleceń: wpływa to na wyszukiwanie w bibliotece opcji -l, które następują po niej.

Jednym z rozwiązań jest umieszczenie dynamicznych zależności przed --static opcją w wierszu poleceń.

Inną możliwością jest nieużywanie --static, ale podanie pełnej nazwy pliku / ścieżki do statycznego pliku obiektowego (tj. Nieużywanie opcji -l) do statycznego łączenia określonej biblioteki. Przykład:

# 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)

Jak widać w przykładzie, libX11nie ma go na liście dynamicznie połączonych bibliotek, ponieważ został połączony statycznie.

Uwaga: .soplik jest zawsze linkowany dynamicznie, nawet jeśli jest określony pełną nazwą pliku / ścieżką.

ypnos
źródło
Jaka jest relacja między libX11.a i wyjściem ldd a.out?
Raffi Khatchadourian
1
O, rozumiem. lddwyświetla wymagane biblioteki współdzielone, a libX11 nie pojawia się na tej liście.
Raffi Khatchadourian
to nie jest jasne. mówisz „ta opcja” i „ta opcja”. jaka opcja?
Octopus
19

Jak rozumiem, problem jest następujący. Masz kilka bibliotek, niektóre statyczne, niektóre dynamiczne, a niektóre zarówno statyczne, jak i dynamiczne. Domyślnym zachowaniem gcc jest linkowanie „głównie dynamiczne”. Oznacza to, że gcc łączy się z bibliotekami dynamicznymi, jeśli jest to możliwe, ale w przeciwnym razie wraca do bibliotek statycznych. Kiedy używasz opcji -static do gcc zachowanie polega na połączeniu tylko bibliotek statycznych i zakończeniu z błędem, jeśli nie można znaleźć żadnej biblioteki statycznej, nawet jeśli istnieje odpowiednia biblioteka dynamiczna.

Inną opcją, którą przy kilku okazjach życzyłem sobie, by gcc był, jest to, co nazywam - głównie statyczne i zasadniczo jest przeciwieństwem opcji -dynamic (domyślna). -mostly-static wolałoby, gdyby istniało, łączyć się z bibliotekami statycznymi, ale wracałoby do bibliotek dynamicznych.

Ta opcja nie istnieje, ale można ją emulować za pomocą następującego algorytmu:

  1. Konstruowanie wiersza poleceń łącza bez włączania -static .

  2. Powtarzaj opcje łącza dynamicznego.

  3. Gromadzenie ścieżek bibliotek, tj. Tych opcji postaci -L <katalog_lib> w zmiennej <ścieżka_lib>

  4. Dla każdej opcji dowiązania dynamicznego, tj. Tych w postaci -l <nazwa_biblioteki> , uruchom komendę gcc <ścieżka_lib> -print-file-name = lib <nazwa_biblioteki> .a i przechwyć wynik.

  5. Jeśli polecenie wydrukuje coś innego niż to, co przekazałeś, będzie to pełna ścieżka do biblioteki statycznej. Zastąp opcję biblioteki dynamicznej pełną ścieżką do biblioteki statycznej.

Płucz i powtarzaj, aż przetworzysz cały wiersz poleceń łącza. Opcjonalnie skrypt może również pobrać listę nazw bibliotek, które mają być wyłączone z łączenia statycznego.

Poniższy skrypt bash wydaje się działać:

#!/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

Na przykład:

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

w moim systemie zwraca:

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"

lub z wykluczeniem:

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

Otrzymuję wtedy:

gcc -o test test.c -ldl "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a"
jcoffland
źródło
7

Istnieje również -l:libstatic1.a(bez l dwukropka) wariant opcji -l w gcc, który może być użyty do połączenia biblioteki statycznej (dzięki https://stackoverflow.com/a/20728782 ). Czy to jest udokumentowane? Nie ma w oficjalnej dokumentacji gcc (co nie jest dokładne w przypadku bibliotek współdzielonych): https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

-llibrary
-l library 

Podczas łączenia przeszukaj bibliotekę o nazwie Library. (Druga alternatywa z biblioteką jako oddzielnym argumentem jest tylko dla zgodności z POSIX i nie jest zalecana.) ... Jedyną różnicą między użyciem opcji -l a określeniem nazwy pliku jest to, że -l otacza bibliotekę 'lib' i „.a” i przeszukuje kilka katalogów.

Dokument binutils ld opisuje to. -lnameOpcja zrobi poszukiwania libname.sonastępnie przez libname.adodanie prefiksu lib i .so(jeśli jest włączona w tej chwili) lub .aprzyrostek. Ale -l:nameopcja wyszuka tylko dokładnie podaną nazwę: https://sourceware.org/binutils/docs/ld/Options.html

-l namespec
--library=namespec

Dodaj archiwum lub plik obiektowy określony przez namespecdo listy plików do połączenia. Ta opcja może być używana dowolną liczbę razy. Jeśli namespecma postać :filename, ld przeszuka ścieżkę do biblioteki dla pliku o nazwie filename, w przeciwnym razie przeszuka ścieżkę do biblioteki dla pliku o nazwie libnamespec.a.

W systemach, które obsługują biblioteki współdzielone, ld może również wyszukiwać pliki inne niż libnamespec.a. W szczególności w systemach ELF i SunOS ld przeszuka katalog w poszukiwaniu biblioteki o nazwie, libnamespec.sozanim wyszuka bibliotekę o nazwie libnamespec.a. (Zgodnie z konwencją .sorozszerzenie oznacza bibliotekę współdzieloną). Należy zauważyć, że to zachowanie nie ma zastosowania do :filename, co zawsze określa plik o nazwie filename.

Konsolidator przeszuka archiwum tylko raz, w miejscu, w którym jest ono określone w wierszu poleceń. Jeśli archiwum definiuje symbol, który był niezdefiniowany w jakimś obiekcie, który pojawił się przed archiwum w linii poleceń, konsolidator włączy odpowiedni plik (i) z archiwum. Jednak niezdefiniowany symbol w obiekcie pojawiający się później w wierszu poleceń nie spowoduje, że konsolidator ponownie przeszuka archiwum.

Zobacz -(opcję, aby wymusić na konsolidatorze wielokrotne przeszukiwanie archiwów.

Możesz wyświetlić to samo archiwum wiele razy w wierszu poleceń.

Ten typ przeszukiwania archiwów jest standardowy dla konsolidatorów Uniksa. Jeśli jednak używasz ld w systemie AIX, zwróć uwagę, że różni się on od zachowania konsolidatora AIX.

Wariant -l:namespecjest udokumentowany od wersji binutils 2.18 (2007): https://sourceware.org/binutils/docs-2.18/ld/Options.html

osgx
źródło
Ta opcja wydaje się działać tam, gdzie wszystko inne zawodzi. Po prostu natknęliśmy się na przypadek, w którym potrzebowaliśmy statycznego linku do libjsoncpp.a, ponieważ nasze maszyny kompilujące tworzyłyby pliki binarne połączone z libjsocpp.so.0, podczas gdy docelowy system operacyjny zapewnia tylko libjsoncpp.so.1. Dopóki nie uda nam się wyjaśnić tej różnicy, było to jedyne rozwiązanie, które w naszym przypadku przyniosło właściwe rezultaty.
Tomasz W
4

Niektóre programy ładujące (konsolidatory) zapewniają przełączniki do włączania i wyłączania dynamicznego ładowania. Jeśli GCC działa na takim systemie (Solaris - i być może inne), możesz użyć odpowiedniej opcji.

Jeśli wiesz, które biblioteki chcesz łączyć statycznie, możesz po prostu określić plik biblioteki statycznej w wierszu dowiązania - za pomocą pełnej ścieżki.

Jonathan Leffler
źródło
6
Chociaż ta odpowiedź została zaakceptowana, nie rozwiązuje ona w pełni problemu. Jak wyjaśnił @peoro, problem, który próbuje rozwiązać, polega na tym, że nie ma on statycznych wersji wszystkich bibliotek, co oznacza, że ​​chciałby łączyć jak najwięcej bibliotek statycznie. Zobacz moją odpowiedź.
jcoffland
2

aby połączyć bibliotekę dynamiczną i statyczną w jednym wierszu, należy umieścić biblioteki statyczne po bibliotekach dynamicznych i plikach obiektowych, na przykład:

gcc -lssl main.o -lFooLib -o main

w przeciwnym razie to nie zadziała. zajmuje mi trochę czasu, zanim to wymyślę.

Vincent
źródło