Рассмотрим следующий сценарий:
- Общая библиотека libA.so, без зависимостей.
- Общая библиотека libB.so с libA.so в качестве зависимости.
Я хочу скомпилировать двоичный файл, который связан с libB. Должен ли я связать двоичный файл только с libB или с libA?
Есть ли способ связать только с прямыми зависимостями, позволяя разрешать неразрешенные символы из зависимостей во время выполнения?
Меня беспокоит тот факт, что реализация библиотеки libB может измениться в будущем, введя другие зависимости (например, libC, libD, libE). У меня будут с этим проблемы?
Другими словами:
- Файлы libA: a.cpp ах
- файлы libB: b.cpp bh
- основные программные файлы: main.cpp
Конечно, b.cpp включает ah, а main.cpp включает bh
Команды компиляции:
g++ -fPIC a.cpp -c
g++ -shared -o libA.so a.o
g++ -fPIC b.cpp -c -I.
g++ -shared -o libB.so b.o -L. -lA
Какой из приведенных ниже вариантов мне следует использовать?
g++ main.cpp -o main -I. -L. -lB
или же
g++ main.cpp -o main -I. -L. -lB -lA
Я не мог использовать первый вариант. Компоновщик жалуется на неразрешенные символы из библиотеки libA. Но для меня это звучит немного странно.
Спасибо большое.
- Обновлены комментарии:
Когда я связываю двоичный файл, компоновщик попытается разрешить все символы из файла main и libB. Однако в libB есть неопределенные символы из libA. Вот почему компоновщик жалуется на это.
Вот почему мне тоже нужно связать с libA. Однако я нашел способ игнорировать неразрешенные символы из общих библиотек. Похоже, для этого мне нужно использовать следующую командную строку:
g++ main.cpp -o main -I. -L. -lB -Wl,-unresolved-symbols=ignore-in-shared-libs
Похоже, что все еще можно использовать -rpath
опцию . Однако мне нужно понять это немного лучше.
Кто-нибудь знает возможные подводные камни при использовании -Wl,-unresolved-symbols=ignore-in-shared-libs
опции?
- Обновлены комментарии 2:
-rpath
не следует использовать для этой цели. Полезно заставить библиотеку быть найденной в данном каталоге. Такой -unresolved-symbol
подход выглядит намного лучше.
Еще раз спасибо.
источник
Ответы:
Похоже, вы уже прошли большую часть пути. Отличное расследование. Посмотрим, смогу ли я помочь прояснить «почему» за этим.
Вот что делает компоновщик. Когда вы связываете свой исполняемый файл («main» выше), он содержит некоторые символы (функции и другие вещи), которые не разрешены. Он будет просматривать список следующих библиотек, пытаясь разрешить неразрешенные символы. Попутно он обнаруживает, что некоторые символы предоставляются libB.so, поэтому отмечает, что теперь они разрешаются этой библиотекой.
Однако он также обнаруживает, что некоторые из этих символов используют другие символы, которые еще не разрешены в вашем исполняемом файле, поэтому теперь ему необходимо разрешить и их. Без связи с libA.so ваше приложение будет неполным. Как только он связывается с libA.so, все символы разрешаются, и связывание завершается.
Как вы видели, использование
-unresolved-symbols-in-shared-libs
не решает проблему. Он просто откладывает его, чтобы эти символы разрешались во время выполнения. Вот для чего-rpath
: указать библиотеки, в которых будет выполняться поиск во время выполнения. Если эти символы не могут быть разрешены, ваше приложение не запустится.Выявить зависимости библиотеки непросто, потому что символ может быть предоставлен более чем одной библиотекой и удовлетворяться путем связывания с любой из них.
Здесь есть еще одно описание этого процесса: Почему порядок, в котором библиотеки связаны, иногда вызывает ошибки в GCC?
источник
Для динамической компоновки только с прямыми зависимостями вы можете использовать
-Wl,--as-needed
добавление библиотек после-Wl,--as-needed
:Для проверки прямых зависимостей вы должны использовать readelf вместо ldd, потому что ldd также показывает косвенные зависимости.
$ readelf -d main | grep library 0x0000000000000001 (NEEDED) Shared library: [libB.so] 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
ldd также показывает косвенные зависимости:
$ LD_LIBRARY_PATH=. ldd ./main linux-vdso.so.1 (0x00007fff13717000) libB.so => ./libB.so (0x00007fb6738ed000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb6734ea000) libA.so => ./libA.so (0x00007fb6732e8000) /lib64/ld-linux-x86-64.so.2 (0x00007fb673af0000)
Если вы используете cmake , вы можете добавить следующие строки, чтобы включить только прямые зависимости:
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_SHARED_LINKER_FLAGS}")
источник
Другой вариант - использовать
libtool
Если вы измените
g++
вызов наlibtool --mode=compile g++
компиляцию исходного кода, а затемlibtool --mode=link g++
на создание приложенияlibB
, то связьlibA
будет выполнена автоматически.источник
Это интересный пост - я тоже бился головой, но думаю, вы упускаете здесь один момент ..
Идея такая, правда?
Давайте дальше считать, что ..
Теперь libB.so и libA.so скомпилированы, как вы описали выше. После этого должен работать ваш первый вариант , а именно:
Я предполагаю, что ваша проблема связана с тем, что
в main.cpp вы также ссылаетесь на symA
Я прав?
Если вы используете символ в своем коде, то этот символ должен быть найден в файле .so.
Вся идея создания взаимных ссылок на разделяемые библиотеки (т.е. создание API-интерфейсов) состоит в том, что символы на более глубоких уровнях скрыты (подумайте о чистке лука) и не используются. .. т.е. не ссылайтесь на symA в вашем main.cpp, а только на symB (и позвольте symB ссылаться только на symA).
источник