Статическое связывание только некоторых библиотек
Как я могу статически связать только некоторые конкретные библиотеки с моим двоичным файлом при соединении с GCC?
gcc ... -static ...
пытается статически связать все связанные библиотеки, но у меня нет статической версии некоторых из них (например, libX11).
8 ответов
Вы также можете использовать ld
вариант -Bdynamic
gcc <objectfiles> -static -lstatic1 -lstatic2 -Wl,-Bdynamic -ldynamic1 -ldynamic2
Все библиотеки после него (включая системные, связанные с gcc автоматически) будут связаны динамически.
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
,
Из справочной страницы 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
Файл всегда связан динамически, даже если указан с полным именем файла / пути.
Проблема, насколько я понимаю, заключается в следующем. У вас есть несколько библиотек, некоторые статические, некоторые динамические, а некоторые статические и динамические. Поведение gcc по умолчанию состоит в том, чтобы связать "в основном динамический". То есть gcc связывается с динамическими библиотеками, когда это возможно, но в противном случае возвращается к статическим библиотекам. Когда вы используете опцию -static для gcc, поведение состоит в том, чтобы связывать только статические библиотеки и завершать работу с ошибкой, если статическая библиотека не может быть найдена, даже если есть соответствующая динамическая библиотека.
Другой вариант, который я несколько раз хотел, чтобы gcc имел, - это то, что я называю -мостатически статичным, и, по сути, является противоположностью -dynamic (по умолчанию). -mly-static, если бы он существовал, предпочел бы связываться со статическими библиотеками, но использовал бы динамические библиотеки.
Эта опция не существует, но ее можно эмулировать с помощью следующего алгоритма:
Построение командной строки ссылки без включения -static.
Переберите параметры динамической ссылки.
Накапливать пути к библиотекам, то есть эти параметры формы -L
в переменной Для каждого параметра динамической ссылки, т. Е. В форме -l <имя_библиотеки>, выполните команду gcc
-print-file-name = lib <имя_библиотеки>.a и запишите выходные данные.Если команда напечатает что-то отличное от того, что вы передали, это будет полный путь к статической библиотеке. Замените параметр динамической библиотеки на полный путь к статической библиотеке.
Промойте и повторяйте, пока не обработаете всю командную строку ссылки. При желании сценарий может также взять список имен библиотек для исключения из статического связывания.
Следующий скрипт 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"
Существует также -l:libstatic1.a
(минус l двоеточие) вариант опции -l в gcc, который можно использовать для связывания статической библиотеки (спасибо /questions/42927298/ukazanie-gcc-napryamuyu-svyazat-biblioteku-staticheski/42927306#42927306). Это задокументировано? Не в официальной документации gcc (которая не является точной для общих библиотек): https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html
-llibrary -l library
Поиск библиотеки по имени библиотеки при связывании. (Второй вариант с библиотекой в качестве отдельного аргумента предназначен только для соответствия POSIX и не рекомендуется.) ... Единственная разница между использованием параметра -l и указанием имени файла заключается в том, что -l окружает библиотеку с помощью 'lib' и '.a' и ищет несколько каталогов.
Binutils ld doc описывает это. -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
документировано начиная с версии 2.18 binutils (2007): https://sourceware.org/binutils/docs-2.18/ld/Options.html
Некоторые загрузчики (линкеры) предоставляют переключатели для включения и выключения динамической загрузки. Если GCC работает в такой системе (Solaris - и, возможно, в других), то вы можете использовать соответствующую опцию.
Если вы знаете, какие библиотеки вы хотите связать статически, вы можете просто указать файл статической библиотеки в строке ссылки - по полному пути.
Чтобы связать динамическую и статическую библиотеку в одну строку, вы должны поместить статические библиотеки после динамических библиотек и объектных файлов, например:
gcc -lssl main.o -lFooLib -o main
в противном случае это не сработает. мне нужно время, чтобы понять это.