статическая компоновка только некоторых библиотек

Как я могу статически связать только некоторые определенные библиотеки с моим двоичным файлом при связывании с GCC?

gcc ... -static ... пытается статически связать все связанные библиотеки, но у меня нет статической версии некоторых из них (например: libX11).


person peoro    schedule 11.11.2010    source источник


Ответы (8)


Вы также можете использовать ld параметр -Bdynamic

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

Все библиотеки после него (включая системные библиотеки, автоматически подключаемые gcc) будут связаны динамически.

person Dmitry Yudakov    schedule 15.11.2010
comment
-Wl, -Bdynamic требует GNU ld, поэтому это решение не работает в системах, где gcc использует системный ld (например, Mac OS X). - person pts; 19.07.2012

gcc objectfiles -o program -Wl,-Bstatic -ls1 -ls2 -Wl,-Bdynamic -ld1 -ld2

вы также можете использовать: -static-libgcc -static-libstdc++ флаги для библиотек gcc

имейте в виду, что если существуют и libs1.so, и libs1.a, компоновщик выберет libs1.so, если он находится до -Wl,-Bstatic или после -Wl,-Bdynamic. Не забудьте пройти -L/libs1-library-location/ перед вызовом -ls1.

person wgodoy    schedule 18.07.2013
comment
По крайней мере, это решение работает со статической ссылкой на libgomp! - person Jérôme; 24.06.2014
comment
У меня это работает хорошо, тогда как использование -static где-то в команде не работает (я предполагаю, что он пытается связать больше вещей статически, чем только библиотеки, которые мне нужны). - person nh2; 28.09.2016
comment
NB. Порядок -Wl,-Bstatic и -Wl,-Bdynamic важен. - person Pavel Vlasov; 22.06.2017

На странице руководства ld (это не работает с gcc), ссылаясь на параметр --static:

Вы можете использовать эту опцию несколько раз в командной строке: она влияет на поиск в библиотеке опций -l, следующих за ней.

Одно из решений - поместить динамические зависимости перед параметром --static в командной строке.

Другая возможность - не использовать --static, а вместо этого указать полное имя файла / путь к статическому объектному файлу (т. Е. Не использовать параметр -l) для статического связывания конкретной библиотеки. Пример:

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

Как вы можете видеть в примере, libX11 отсутствует в списке динамически подключаемых библиотек, поскольку он был связан статически.

Осторожно: файл .so всегда связан динамически, даже если он указан с полным именем файла / путем.

person ypnos    schedule 11.11.2010
comment
Какая связь между libX11.a и выводом ldd a.out? - person Raffi Khatchadourian; 21.05.2013
comment
Ах я вижу. ldd выводит требуемые разделяемые библиотеки, а libX11 не отображается в этом списке. - person Raffi Khatchadourian; 21.05.2013
comment
это не ясно. вы говорите «этот вариант» и «тот вариант». какой вариант? - person Octopus; 25.10.2013

Проблема, как я понимаю, в следующем. У вас есть несколько библиотек, некоторые статические, некоторые динамические, а некоторые статические и динамические. По умолчанию gcc связывает "в основном динамические". То есть gcc по возможности ссылается на динамические библиотеки, но в остальном возвращается к статическим библиотекам. Когда вы используете параметр -static для gcc, поведение заключается в связывании только статических библиотек и выходе с ошибкой, если статическая библиотека не может быть найдена, даже если есть подходящая динамическая библиотека.

Другой вариант, который мне несколько раз хотелось, чтобы у gcc был, - это то, что я называю -в основном-статическим, и по сути он противоположен -динамическому. (по умолчанию). -mostly-static, если бы он существовал, предпочел бы связываться со статическими библиотеками, но вернулся бы к динамическим библиотекам.

Этой опции не существует, но ее можно эмулировать с помощью следующего алгоритма:

  1. Создание командной строки ссылки без включения -static.

  2. Перебирайте параметры динамической ссылки.

  3. Накапливайте пути к библиотекам, то есть параметры формы -L ‹lib_dir› в переменной ‹lib_path›.

  4. Для каждого параметра динамической ссылки, т.е. имеющего форму -l ‹lib_name›, выполните команду gcc ‹lib_path› -print-file-name = lib ‹lib_name› .a и захватить выход.

  5. Если команда выводит что-то отличное от того, что вы передали, это будет полный путь к статической библиотеке. Замените параметр динамической библиотеки полным путем к статической библиотеке.

Промойте и повторяйте, пока не обработаете всю командную строку ссылки. При желании сценарий также может принимать список имен библиотек для исключения из статической компоновки.

Следующий сценарий bash, похоже, помогает:

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

Например:

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

в моей системе возвращается:

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"

или с исключением:

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

Тогда я получаю:

gcc -o test test.c -ldl "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a"
person jcoffland    schedule 06.12.2012

Существует также вариант -l:libstatic1.a (минус l двоеточие) опции -l в gcc, который можно использовать для связывания статической библиотеки (благодаря https://stackoverflow.com/a/20728782). Это задокументировано? Нет в официальной документации gcc (что не совсем верно и для общих библиотек): https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

-llibrary
-l library 

При компоновке ищите библиотеку с именем library. (Вторая альтернатива с библиотекой в ​​качестве отдельного аргумента предназначена только для соответствия POSIX и не рекомендуется.) ... Единственная разница между использованием параметра -l и указанием имени файла заключается в том, что -l окружает библиотеку символами 'lib' и '.a' и выполняет поиск в нескольких каталогах.

Документ binutils ld описывает это. Параметр -lname будет искать libname.so, затем libname.a, добавляя префикс lib и .so (если он включен в данный момент) или суффикс .a. Но опция -l:name будет искать только точно указанное имя: https://sourceware.org/binutils/docs/ld/Options.html

-l namespec
--library=namespec

Добавьте архив или объектный файл, указанный namespec, в список файлов для ссылки. Эту опцию можно использовать любое количество раз. Если namespec имеет форму :filename, ld будет искать в пути к библиотеке файл с именем filename, в противном случае он будет искать в пути к библиотеке файл с именем libnamespec.a.

В системах, поддерживающих разделяемые библиотеки, ld может также искать файлы, отличные от libnamespec.a. В частности, в системах ELF и SunOS ld будет искать в каталоге библиотеку с именем libnamespec.so перед поиском библиотеки с именем libnamespec.a. (По соглашению расширение .so указывает на разделяемую библиотеку.) Обратите внимание, что это поведение не применяется к :filename, который всегда указывает файл с именем filename.

Компоновщик выполнит поиск в архиве только один раз в том месте, где он указан в командной строке. Если архив определяет символ, который не был определен в каком-либо объекте, который появился перед архивом в командной строке, компоновщик включит соответствующий файл (ы) из архива. Однако неопределенный символ в объекте, который появляется позже в командной строке, не заставит компоновщик снова искать в архиве.

См. Параметр -(, чтобы узнать, как заставить компоновщик выполнять поиск в архивах несколько раз.

Вы можете указать один и тот же архив несколько раз в командной строке.

Этот тип поиска в архивах является стандартным для линкеров Unix. Однако, если вы используете ld в AIX, обратите внимание, что он отличается от поведения компоновщика AIX.

Вариант -l:namespec задокументирован, начиная с версии binutils 2.18 (2007 г.): https://sourceware.org/binutils/docs-2.18/ld/Options.html.

person osgx    schedule 24.05.2017
comment
Этот вариант, кажется, работает там, где все остальное терпит неудачу. Мы только что наткнулись на случай, когда нам нужно было статически связать libjsoncpp.a, потому что наши машины сборки будут создавать двоичные файлы, связанные с libjsocpp.so.0, тогда как целевая ОС предоставляет только libjsoncpp.so.1. Пока мы не сможем устранить эту разницу, это было единственное решение, которое дало надлежащие результаты в нашем случае. - person Tomasz W; 14.08.2017

Некоторые загрузчики (компоновщики) предоставляют переключатели для включения и выключения динамической загрузки. Если GCC работает в такой системе (Solaris - и, возможно, в других), вы можете использовать соответствующую опцию.

Если вы знаете, какие библиотеки вы хотите связать статически, вы можете просто указать файл статической библиотеки в строке ссылки - по полному пути.

person Jonathan Leffler    schedule 11.11.2010
comment
Несмотря на то, что этот ответ был принят, он не решает проблему полностью. Как объяснил @peoro, проблема, которую он пытается решить, заключается в том, что у него нет статических версий всех библиотек, что означает, что он хотел бы связать как можно больше библиотек статически. Смотрите мой ответ. - person jcoffland; 06.12.2012

чтобы связать динамическую и статическую библиотеки в одной строке, вы должны поместить статические библиотеки после динамических библиотек и объектных файлов, например:

gcc -lssl main.o -lFooLib -o main

в противном случае это не сработает. мне действительно нужно время, чтобы понять это.

person Vincent    schedule 02.11.2017

person    schedule
comment
Связывайте библиотеки после объектных файлов - особенно статические библиотеки. В древних и современных версиях среды ссылок (я не уверен в статус-кво для умеренно старых версий по состоянию на ноябрь 2010 г.) перечисление статической библиотеки перед файлом code.c гарантирует, что символы в ней будут проигнорированы, если не произойдет быть main() функцией в одном из объектных файлов библиотеки. - person Jonathan Leffler; 21.04.2014
comment
Не могли бы вы рассказать, как это работает? Ответы только на код бесполезны для новичков. - person jb.; 14.06.2014
comment
@jb по умолчанию, gcc ссылается динамически. Когда вы используете -lsome_dynamic_lib, он динамически связывается, как и ожидалось. Но когда gcc явно предоставляется статическая библиотека, он всегда будет пытаться связать ее статически. Однако есть некоторые хитрые детали о порядке, в котором разрешаются символы; Я не совсем понимаю, как это работает. Я узнал, что в случае сомнений попробуйте изменить порядок флагов библиотеки :-) - person bchurchill; 06.08.2016
comment
существует проблема лицензирования, если вы статически связываете, например, библиотеку GPL - person laplasz; 12.10.2016
comment
@HiB GPL одинаково применяется к статической и динамической компоновке - person osvein; 29.06.2019
comment
Это не работает с g++, который не учитывает путь поиска библиотеки для получения .a статической библиотеки. - person vinc17; 01.07.2020
comment
@ vinc17 затем передайте его через -Wl,-l:staticlib.a ... параметр является параметром ld, а не одним из драйверов gcc / g ++. - person 0xC0000022L; 08.12.2020