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

79

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

  • Общая библиотека 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

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

Какой из приведенных ниже вариантов мне следует использовать?

или же

Я не мог использовать первый вариант. Компоновщик жалуется на неразрешенные символы из библиотеки libA. Но для меня это звучит немного странно.

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

- Обновлены комментарии:

Когда я связываю двоичный файл, компоновщик попытается разрешить все символы из файла main и libB. Однако в libB есть неопределенные символы из libA. Вот почему компоновщик жалуется на это.

Вот почему мне тоже нужно связать с libA. Однако я нашел способ игнорировать неразрешенные символы из общих библиотек. Похоже, для этого мне нужно использовать следующую командную строку:

Похоже, что все еще можно использовать -rpath опцию . Однако мне нужно понять это немного лучше.

Кто-нибудь знает возможные подводные камни при использовании -Wl,-unresolved-symbols=ignore-in-shared-libsопции?

- Обновлены комментарии 2:

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

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

Маркус
источник
Вот работоспособный минималистичный пример для тех, кто хочет протестировать такие вещи: github.com/cirosantilli/cpp-cheat/tree/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Ответы:

42

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

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

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

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

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

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

кифрил
источник
2
Я знаю, что это было давно, забыл отметить, что решено. Большое спасибо за ваш ответ.]
Маркус
Спасибо, ребята, за прекрасный вопрос и подходящий ответ! У меня почти точно такой же сценарий. Я пытаюсь обернуть общие объекты 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 ?? Я был бы рад, если бы кто-нибудь пролил свет на это. Спасибо! :)
Ленни Линус
2
Почему компоновщик не разрешает все необходимые символы libA.so при создании libB.so? Основное приложение не должно связываться с libA, так как оно должно быть прозрачным, например, косвенно защищенным libB?
Уилсон
Это неверно. Компоновщик не использует libA.so, кроме как для уведомления пользователя о неполной цепочке зависимостей. Не предоставляя libA.so, но вместо этого передавая -unresolved-symbols = ignore-in-shared-libs вы получите точно такой же исполняемый файл.
listerreg
21

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

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

ldd также показывает косвенные зависимости:

Если вы используете cmake , вы можете добавить следующие строки, чтобы включить только прямые зависимости:

user1047271
источник
Извините, я сделал ваш пример как программу c вместо c ++. Поэтому я использовал компилятор gcc. Но он также работает с g ++.
user1047271
1
Это действительно полезная информация. Про использование readelf вместо ldd я не знал. Спасибо большое.
Маркус
1

Другой вариант - использовать libtool

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

Mattmilten
источник
0

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

Идея такая, правда?

Давайте дальше считать, что ..

  • В a.cpp (и только там) вы определяете класс / переменную, назовем ее "symA"
  • В b.cpp (и только там) вы определяете класс / переменную, назовем ее «symB».
  • symB использует symA
  • main.cpp использует symB

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

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

в main.cpp вы также ссылаетесь на symA

Я прав?

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

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

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