Подтвердить что ты не робот

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

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

  • Общая библиотека libA.so без зависимостей.
  • Общая библиотека libB.so, с libA.so как ее зависимость.

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

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

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

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

  • Файлы libA: a.cpp a.h
  • Файлы libB: b.cpp b.h
  • основные файлы программы: 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. Но это звучит немного странно для меня.

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

Ответ 2

Для динамического связывания только с прямыми зависимостями вы можете использовать -Wl,--as-needed с добавлением libs после -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}")

Ответ 3

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

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

Ответ 4

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

Идея такова, верно?

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

Далее рассмотрим, что..

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

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

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

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

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

Правильно ли я?

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

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