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

108

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

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

Пеоро
источник

Ответы:

112

gcc -lsome_dynamic_lib code.c some_static_lib.a

Шимон Тот
источник
5
Связывайте библиотеки после объектных файлов - особенно статические библиотеки. В древних и современных версиях среды ссылок (я не уверен в статус-кво для умеренно старых версий по состоянию на ноябрь 2010 г.) перечисление статической библиотеки перед code.cфайлом гарантирует, что символы в ней будут проигнорированы, если только не произойдет main()функции в одном из библиотеки объектных файлов.
Джонатан Леффлер,
44
Не могли бы вы рассказать, как это работает? Ответы только на код бесполезны для новичков.
jb.
8
@jb по умолчанию, gcc ссылается динамически. Когда вы используете -lsome_dynamic_lib, он динамически связывается, как и ожидалось. Но когда gcc явно предоставляется статическая библиотека, он всегда будет пытаться связать ее статически. Однако есть некоторые хитрые детали о порядке, в котором разрешаются символы; Я не совсем понимаю, как это работает. Я узнал, что в случае сомнений попробуйте изменить порядок флагов библиотеки :-)
bchurchill
4
существует проблема лицензирования, если вы статически связываете, например, библиотеку GPL
HiB,
1
@HiB GPL применяет тот же способ к статической и динамической компоновке
Osvein
50

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

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

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

Дмитрий Юдаков
источник
19
-Wl, -Bdynamic требует GNU ld, поэтому это решение не работает в системах, где gcc использует системный ld (например, Mac OS X).
пц
33
gcc objectfiles -o program -Wl,-Bstatic -ls1 -ls2 -Wl,-Bdynamic -ld1 -ld2

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

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

Wgodoy
источник
1
По крайней мере, это решение работает со статической ссылкой на libgomp!
Жером
Это хорошо работает для меня, хотя использование -staticгде-то в команде не работает (я предполагаю, что он пытается статически связать больше вещей, чем только библиотеки, которые мне нужны).
nh2
4
NB. Порядок -Wl,-Bstaticи -Wl,-Bdynamicважен.
Павел Власов
27

На странице руководства 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файл всегда связан динамически, даже если он указан с полным именем файла / путем.

ypnos
источник
Какая связь между libX11.a и выводом ldd a.out?
Раффи Хачадурян
1
Ах я вижу. lddвыводит необходимые разделяемые библиотеки, а libX11 не появляется в этом списке.
Раффи Хачадурян
это не ясно. вы говорите «этот вариант» и «тот вариант». какой вариант?
Octopus
19

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

Другой вариант, о котором я несколько раз мечтал иметь gcc , - это то, что я называю -mostly-static и по сути является противоположностью -dynamic (по умолчанию). -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"
Jcoffland
источник
7

Существует также -l:libstatic1.a(без двоеточия) вариант параметра -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

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

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

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

Джонатан Леффлер
источник
6
Несмотря на то, что этот ответ был принят, он не решает проблему полностью. Как объяснил @peoro, проблема, которую он пытается решить, заключается в том, что у него нет статических версий всех библиотек, что означает, что он хотел бы связать как можно больше библиотек статически. Смотрите мой ответ.
jcoffland 06
2

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

gcc -lssl main.o -lFooLib -o main

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

Винсент
источник