Связывание с динамической библиотекой с зависимостями

Рассмотрим следующий сценарий:

Shared Library libA.so ,with no dependencies. Shared Library libB.so, with libA.so as its dependency.

Я хочу скомпилировать двоичный файл, который связан с libB. Должен ли я связать бинарный файл только с libB или с libA?

Есть ли способ связать только с прямыми зависимостями, разрешив разрешение неразрешенных символов из зависимостей для времени выполнения?

Меня беспокоит тот факт, что реализация библиотеки libB может измениться в будущем, введя другие зависимости (например, libC, libD, libE). У меня будут проблемы с этим?

Другими словами:

libA files: a.cpp a.h libB files: b.cpp b.h main program files: main.cpp

Конечно, b.cpp включает в себя a.h, а main.cpp включает в себя b.h.

Команды компиляции:

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. Но это звучит немного странно для меня.

Спасибо большое.

-- Updated comments:

Когда я связываю двоичный файл, компоновщик попытается разрешить все символы из основного и из 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 вариант?

-- Updated comments 2:

-rpath не должен использоваться для этой цели. Полезно принудительно найти библиотеку в данном каталоге.-unresolved-symbol подход выглядит намного лучше.

Еще раз спасибо.

Вот работающий минимальный пример для тех, кто хочет проверить подобные вещи:github.com/cirosantilli/cpp-cheat/tree/…

Ответы на вопрос(4)

Решение Вопроса

Похоже, вы уже прошли большую часть пути. Отлично с твоим расследованием. Давайте посмотрим, смогу ли я помочь прояснить, почему? за этим.

Вот что делает компоновщик. Когда вы связываете свой исполняемый файл («основной» выше), у него есть некоторые символы (функции и другие вещи), которые не разрешены. Он будет смотреть список библиотек, которые следуют, пытаясь найти неразрешенные символы. Попутно он обнаруживает, что некоторые символы предоставляются libB.so, поэтому он отмечает, что теперь они разрешаются этой библиотекой.

Тем не менее, он также обнаруживает, что некоторые из этих символов используют другие символы, которые еще не разрешены в вашем исполняемом файле, поэтому теперь необходимо также разрешить их. Без ссылки на libA.so ваше приложение будет неполным. Как только он ссылается на libA.so, все символы разрешаются, и соединение завершается.

Как вы видели, использование-unresolved-symbols-in-shared-libsне решает проблему. Он просто откладывает это так, что эти символы разрешаются во время выполнения. Вот что-rpath для: указать библиотеки для поиска во время выполнения. Если эти символы не могут быть разрешены, ваше приложение не запустится.

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

Здесь есть еще одно описание этого процесса:Почему порядок, в котором связаны библиотеки, иногда вызывает ошибки в GCC?

 02 нояб. 2017 г., 07:55
Почему компоновщик не разрешает все необходимые символы libA.so при создании libB.so? Основное приложение не должно ссылаться на libA, поскольку оно должно быть прозрачным, например косвенно экранируется libB?
 Marcus04 дек. 2013 г., 19:49
Я знаю, что это было давно, я забыл отметить это как решенное. Большое спасибо за ваш ответ.]
 16 нояб. 2016 г., 23:31
Спасибо, ребята, за удивительный вопрос и подходящий ответ! У меня почти точно такой же сценарий. Я пытаюсь обернуть общие объекты openCV в пользовательский общий объект. допустим, C.so - мой обычай, а CV.so - какой-то openCV. Я успешно связал C.so с CV.so и хочу связать main.cpp (вы понимаете!) С C.so, но дело не в использовании объектов из C.so, main.cpp использует объект "cv :: Mat" который определен в CV.so .. В этом случае для запуска main.cpp нужно ли ссылаться как на C.so, так и на CV.so ?? Мне бы очень понравилось, если бы кто-то пролил свет на это. Спасибо! :)

Другой вариант заключается в использованииlibtool

Если вы изменитеg++ позвонитьlibtool --mode=compile g++ скомпилировать исходный код, а затемlibtool --mode=link g++ создать приложение изlibB, затемlibA будет связан автоматически.

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

Идея заключается в следующем, верно?

main.cpp =(depends)=> libB.so =(depends)=> libA.so

Давайте далее посмотрим, что ..

In a.cpp (and only there) you define a class / variable, let's call it "symA" In b.cpp (and only there) you define a class / variable, let's call it "symB". symB uses symA main.cpp uses symB

Теперь libB.so и libA.so были скомпилированы, как вы описали выше. После этого ваш первый вариантshould работа, т. е.

g++ main.cpp -o main -I. -L. -lB

Я полагаю, что ваша проблема связана с тем, что

in main.cpp you also refer to symA

Я прав?

Если вы используете символ в своем коде, то этот символ должен быть найден в файле .so

Вся идея взаимных ссылок на разделяемые библиотеки (т.е. создание API) заключается в том, что символы в более глубоких слоях скрыты (например, очистка лука) и не используются. то есть не ссылаются на symA в вашем main.cpp, а только на symB (и пусть symB ссылается только на symA).

 Marcus17 авг. 2017 г., 18:24
Вы неправильно поняли это. Очевидно, вам нужно разрешить необходимые символы. Проблема связана с символами, которые могут потребоваться вашими зависимостями. Обычно при разработке библиотек для использования третьей стороной, когда пользователь библиотеки должен обеспечить реализацию функций, используемых вашей библиотекой. Вы разрабатываете с использованием заглушки без каких-либо зависимостей, и интегратор разработает библиотеку для замены заглушки, а затем свяжет все вместе. Загрузчик должен разрешить все зависимости. Однако ваши сценарии компиляции (и ваши пользователи) должны учитывать это.

Для динамического связывания только с прямыми зависимостями вы можете использовать-Wl,--as-needed с добавлением библиотекafter -Wl,--as-needed:

gcc main.c -o main -I. -L. -Wl,--as-needed -lB -lA

Для проверки прямых зависимостей вы должны использовать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}")
 Marcus24 мар. 2015 г., 13:49
Это действительно полезная информация. Я не знал об использовании readelf вместо ldd. Спасибо большое.
 23 мар. 2015 г., 12:24
Извините, я сделал ваш пример как программу c вместо c ++. Поэтому я использовал компилятор gcc. Но это также работает с g ++.

Ваш ответ на вопрос