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

Как создать общую библиотеку (.so) без жестко закодированных путей полной зависимости?

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

Подробнее:

Первый источник библиотеки: ~/dev/A
Второй источник библиотеки: ~/dev/B

Оба из них имеют configure script для создания файлов make. Библиотека B зависит от A. Итак, сначала я построю A:

$ ~/dev/A/configure --prefix=~/dev/A-install
$ make && make install

Затем я строю B:

$ ~/dev/B/configure --prefix=~/dev/B-install --with-A=~/dev/A-install
$ make && make install

Затем я хочу загрузить содержимое ~/dev/A-install и ~/dev/B-install на наш файловый сервер, чтобы другие команды и машины сборки могли использовать двоичные файлы. Но они получают предупреждения компоновщика, когда они пытаются использовать B:

/usr/bin/ld: warning: libA.so.2, needed by /.../deps/B/lib/libB.so, not found (try using -rpath or -rpath-link)

Когда я запускаю ldd libB.so, он дает:

...
libA.so.2 => /home/alex/dev/A-install/lib/libA.so.2

Очевидно, что этот путь существует только на моей машине и не может быть найден на других машинах.

Как удалить полный путь с жестким кодом из libB.so?

Спасибо.

4b9b3361

Ответ 1

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

Чем вы переопределяете prefix или DESTDIR (prefix заменяет префикс, DESTDIR добавляется к нему, но работает более надежно) в командной строке make при установке. Как:

~/dev/A$ ./configure
~/dev/A$ make 
~/dev/A$ make install prefix=~/dev/A-install
~/dev/B$ ./configure --with-A=~/dev/A-install
~/dev/B$ make
~/dev/B$ make install prefix=~/dev/B-install

или (что является предпочтительным и как все инструменты построения пакета используют его):

~/dev/A$ ./configure
~/dev/A$ make 
~/dev/A$ make install DESTDIR=~/dev/A-install
~/dev/B$ ./configure --with-A=~/dev/A-install/usr/local
~/dev/B$ make
~/dev/B$ make install prefix=~/dev/B-install

потому что таким образом вы устанавливаете на ~/dev/A-install/$prefix, поэтому с префиксом по умолчанию ~/dev/A-install/usr/local. Преимущество этого более позднего варианта заключается в том, что если вы переопределите некоторые конкретные пути установки, не ссылаясь на префикс (скажем --sysconfdir=/etc), DESTDIR все равно будет добавлен к нему, пока он не будет затронут prefix.

Ответ 2

-Wl,-rpath,~/deps/A/lib:~/deps/B/lib:~/dev/MyApp/bin

Этот вариант компоновщика отвечает за сохранение пути внутри библиотеки. Вам нужно как-то удалить это.

См. ./configure --help, если есть какой-то вариант, который может повлиять на это. Другой вариант - вручную отредактировать make файл и удалить эту опцию.

== edit2 == Еще одна вещь

-L~/deps/A/lib -L~/deps/B/lib ~/deps/A/lib/libA.so ~/deps/B/lib/libB.so

Если libA.so и libB.so не имеют SONAME, связывание их как "~/deps/A/lib/libA.so" также приведет к сохранению пути. Soname устанавливается с использованием опции -Wl,-soname,<soname> linker при создании общей библиотеки.

Если soname установлен в общей библиотеке, ссылка на него с помощью формы "~/deps/A/lib/libA.so" в порядке.

Как и Ян, упомянутый в комментариях, лучший способ - "-Llibrary/path -llibrary_name" без rpath.

-L~/deps/A/lib -L~/deps/B/lib -lA -lB

Ответ 3

Когда я запускаю ldd libB.so, он дает:

libA.so.2 = > /home/alex/dev/A-install/lib/libA.so.2

Низкоуровневое решение этой проблемы заключается в использовании опции "-soname = libA.so" при связывании библиотеки libA.so. Имея SONAME для общего объекта, компоновщик не будет вставлять абсолютные пути при связывании с этим общим объектом.

OP использует "configure", поэтому это не простое решение для реализации, если только он не захочет войти в недра Makefile, сгенерированного configure script.

Ответ 4

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

ldd libB.so 

Я бы получил на выходе:

libA.so.1 => /home/me/localpath/lib/libA.so.1.0

и поэтому я думал, что у меня есть жесткий путь. Оказывается, я забыл, что для локального тестирования был установлен LD_LIBRARY_PATH. Очистка LD_LIBRARY_PATH означает, что ldd нашел правильную установленную библиотеку в/usr/lib/

Ответ 5

Общие библиотеки и исполняемые файлы имеют список каталогов для поиска общих библиотек, в дополнение к списку в конфигурации операционной системы. RPATH используется для добавления общих путей поиска в библиотеке, используемых во время выполнения. Если вы хотите использовать относительный путь в RPATH, существует специальный синтаксис, который поддерживается большинством систем Linux/UNIX (но не AIX) - $ORIGIN или ${ORIGIN}.
$ORIGIN будет расширяться во время выполнения до каталога, в котором находится бинарный файл - библиотеки или исполняемого файла.

Поэтому, если вы поместите исполняемые двоичные файлы в префикс/bin/и общие библиотеки в prefix/lib/ вы можете добавить запись в RPATH, например, ${ORIGIN}/../lib и она будет расширена во время выполнения до prefix/bin/../lib/

Часто бывает сложно найти правильный синтаксис в Makefile, потому что вам нужно экранировать $ в ORIGIN, чтобы он не расширялся. Обычно это делается в Makefile:

 g++  -Wl,-rpath=\$${ORIGIN}/../lib

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

Ответ 6

Возможно, использование опций -rpath и -soname для ld может помочь (предполагая пакет binutils или binutils.gold для ld в последней системе Linux)?