статичне з'єднання лише деяких бібліотек


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
Виникла проблема з lincense, якщо ви
з'єднаєтесь

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++прапори для бібліотек gcc

майте на увазі, що якщо libs1.soі те і libs1.aінше існує, лінкер вибере, libs1.soякщо це до -Wl,-Bstaticабо після -Wl,-Bdynamic. Не забувайте проїхати, -L/libs1-library-location/перш ніж дзвонити -ls1.


1
Принаймні, це рішення працює на статичну зв'язок проти libgomp!
Jérôme

Це добре працює для мене, хоча використання -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файл завжди пов'язується динамічно, навіть якщо вказано повне ім’я файлу / шлях.


Який взаємозв'язок між libX11.a та виходом ldd a.out?
Раффі Хатчадурян

1
А, бачу. lddвиводить потрібні бібліотеки, і libX11 не відображається у цьому списку.
Раффі Хатчадуріан

це не ясно. ви говорите "цей варіант" і "цей варіант". який варіант?
Восьминіг

19

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

Інший варіант, який я декілька разів хотів, щоб gcc мав, - це те, що я називаю - переважно статичним і по суті є протилежним до -динамічного (за замовчуванням). Найбільш-статичні , якби вони існували, воліли б пов'язувати статичні бібліотеки, але повернулися б до динамічних бібліотек.

Ця опція не існує, але її можна емулювати за допомогою наступного алгоритму:

  1. Побудова командного рядка посилання з out, включаючи -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"

7

Існує також -l:libstatic1.a(мінус l двокрапка) варіант -l в gcc, який можна використовувати для зв’язку статичної бібліотеки (Завдяки https://stackoverflow.com/a/20728782 ). Це документально підтверджено? Не в офіційній документації 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задокументований з версії binutils 2.18 (2007): https://sourceware.org/binutils/docs-2.18/ld/Options.html


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

4

Деякі навантажувачі (лінкери) надають вимикачі для включення та вимкнення динамічного навантаження. Якщо GCC працює в такій системі (Solaris - і, можливо, інші), то ви можете використовувати відповідний варіант.

Якщо ви знаєте, з якими бібліотеками ви хочете статично зв’язати, ви можете просто вказати статичний файл бібліотеки у рядку посилань - повним шляхом.


6
Незважаючи на те, що ця відповідь була прийнята, вона не вирішує проблему повною мірою. Як пояснив @peoro, проблемою, яку він намагається вирішити, є те, що він не має статичних версій усіх бібліотек, що означає, що він хотів би статично пов'язати якомога більше бібліотек. Дивіться мою відповідь.
jcoffland

2

щоб з'єднати динамічну та статичну бібліотеку в одному рядку, ви повинні поставити статичні лібри після динамічних файлів і файлів об'єктів, наприклад:

gcc -lssl main.o -lFooLib -o main

інакше це не спрацює. мені потрібно колись зрозуміти це.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.