ld: Использование -rpath, $ ORIGIN внутри разделяемой библиотеки (рекурсивно) - PullRequest
28 голосов
/ 12 июня 2011

Я только что сделал базовый пример использования опции ld's -rpath с $ORIGIN здесь (см. 2-й ответ для рабочей версии).Я пытаюсь создать пример, где main.run ссылается на foo.so, который, в свою очередь, ссылается на bar.so, все используют rpath и $ORIGIN.

Файловая структура времени выполненияэто:

  • проект /
    • lib /
      • dir /
        • sub /
          • бар.so
        • foo.so
    • run /
      • main.run (не удается собрать)

Я собираю foo.so, используя:

g++ -c -o obj/foo.o src/foo.cpp -fPIC
g++ -shared -o lib/dir/foo.so obj/foo.o -Wl,-soname,foo.so -Wl,-rpath,'$ORIGIN/sub' -Llib/dir/sub -l:bar.so

Который прекрасно строит.ldd lib/dir/foo.so может даже найти bar.so.

Однако, когда я пытаюсь связать main.run с foo.so, foo.so не может найти bar.so.

I 'm building main.so используя:

g++ -c -o obj/main.o src/main.cpp
g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../lib/dir' -Llib/dir -l:foo.so

Это прекрасно работает, если используется другая версия foo.so, которая не рекурсивно связывает.(Раскомментируйте строки в make.sh, в проекте ниже, чтобы протестировать).

Однако, используя обычный foo.so Я получаю эту ошибку при сборке main.run:

/ usr / bin / ld: warning: bar.so, необходим дляlib / dir / foo.so, не найден (попробуйте использовать -rpath или -rpath-link)

Итак, мои вопросы:

  1. Имеет ли $ORIGIN внутри foo.so разрешить project/lib/dir (где foo.so) или project/run (где main.run (исполняющий файл, связывающий его))?
    ldd может показывать, что это project/lib/dir, что может показатьсябыть лучшим способом (хотя я пытался предположить и то и другое).
  2. Как получить их для связи (при сохранении возможности перемещения) - желательно без использования -rpath-link.

Вы можете скачатьпроект здесь .Это так просто, как я могу это сделать.4 коротких источника и скрипт.
После распаковки просто запустите ./make.sh из project/.

Примечание: Я использую -l:.Это не должно ничего менять, за исключением того, что библиотеки названы как foo.so вместо libfoo.so и lunk с -l:foo.so вместо -lfoo.

Ответы [ 6 ]

7 голосов
/ 12 июня 2011

Ну, у меня что-то работает.Но я не очень понимаю, почему это работает.Для меня это похоже на ошибку в ld.

Я запустил strace -f -o /var/tmp/strace.out -- g++ ... для компиляции main.run.Статический компоновщик на самом деле пытается открыть файлы, буквальное имя которых выглядит как «$ ORIGIN / lib / dir / sub / bar.so», среди 20-30 других вещей.(Другими словами, он ищет фактический каталог с именем $ORIGIN. Серьезно.)

Он также ищет путь -rpath-link для имени "lib / dir / sub / bar.так ", а не просто" bar.so ".Понятия не имею, почему.

В любом случае, это ссылка на main.run, которая работает на меня:

g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../lib/dir' -Wl,-rpath-link,. -Llib/dir -l:foo.so

Она идентична вашей, но со вставленным -Wl,-rpath-link,..

[приложение]

Хорошо, я думаю, что вижу, что происходит.Во-первых, статический компоновщик (GNU ld) просто не учитывает $ ORIGIN в библиотеках, с которыми он связывается.

Во-вторых, поведение при использовании -lbar по сравнению с -l:bar.so очень отличается.

Выполнить readelf -a на foo.so.В вашей сборке он показывает зависимость от "lib / dir / sub / bar.so".Вот почему установка ссылки rpath на "."исправляет сборку main.run;это заставляет статический компоновщик искать "."для "lib / dir / sub / bar.so", который он находит.

Если вы переименуете bar.so в libbar.so и создадите ссылку foo.so, чтобы использовать -lbar вместо -l:bar.so,та же самая readelf показывает, что foo.so теперь зависит от "libbar.so" (без компонента path).С этим foo.so вы можете заставить работать ссылку main.run, используя -Wl,-rpath-link,lib/dir/sub, как и следовало ожидать, если бы вы знали, что статический компоновщик просто не соблюдает $ ORIGIN.

Кстати, яне вижу синтаксиса -l:bar.so, документированного где-либо в руководстве GNU ld.Из любопытства, как у вас это получилось?

Если предположить, что это поддерживаемая функция, это выглядит как ошибка (-l: bar.so создаёт зависимость от lib / dir / sub / bar.so вместо просто bar.so).Вы можете устранить эту ошибку, установив для rpath-link значение '.'для main.run, или вы можете переименовать материал обычным способом (libxxx.so).

6 голосов
/ 14 июля 2014

Из справочной страницы ld-linux (8) :

$ ORIGIN и rpath

ld.so понимает строку $ ORIGIN (или эквивалентно $ {ORIGIN}) в Спецификация rpath (DT_RPATH или DT_RUNPATH) означает каталог содержащий исполняемый файл приложения. Таким образом, приложение, расположенное в somedir / app может быть скомпилировано с помощью gcc -Wl, -rpath, '$ ORIGIN /../ lib', поэтому что он находит связанную разделяемую библиотеку в somedir / lib независимо от того, где somedir находится в иерархии каталогов. Это облегчает создание приложений «под ключ», которые не нужно устанавливается в специальные каталоги, но вместо этого может быть распакован в любой каталог и до сих пор найти свои собственные общие библиотеки.

Таким образом, в ответе на ваш первый вопрос есть только одно значение для $ORIGIN: project/run.

Поэтому, ответом на ваш второй вопрос должно быть использование следующей команды для связи foo.so:

g++ -shared -o lib/dir/foo.so obj/foo.o -Wl,-soname,foo.so -Wl,-rpath,'$ORIGIN/../lib/dir/sub' -Llib/dir/sub -l:bar.so
5 голосов
/ 14 июня 2011

Во-первых, существуют проблемы с расширением знака $, которые могут вызывать проблемы. Я собираю Python из исходного кода и делаю это:

export LDFLAGS='-Wl,-rpath,\$${ORIGIN}/../lib -Wl,-rpath,\$${ORIGIN}/../usr/lib -Wl,--enable-new-dtags'

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

Во-вторых, если вы запускаете objdump -x в двоичном файле или библиотеке, вы можете увидеть заголовок RPATH, который он на самом деле содержит. Когда я запускаю objdump -x path/to/python |grep RPATH, это показывает мне это. RPATH $ {ORIGIN} /../ lib: $ {ORIGIN} /../ usr / lib`

Я предлагаю вам проверить свои двоичные файлы, чтобы увидеть, что на самом деле находится в заголовке RPATH. К сожалению, я не думаю, что это решит вашу проблему. Это то, что я вижу, когда запускаю ldd path/to/python:

libpython2.7.so.1.0 => /data1/python27/bin/../lib/libpython2.7.so.1.0 (0x00002ad369d4f000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00002ad36a12f000)
libdl.so.2 => /lib/libdl.so.2 (0x00002ad36a34d000)
libutil.so.1 => /lib/libutil.so.1 (0x00002ad36a551000)
libm.so.6 => /lib/libm.so.6 (0x00002ad36a754000)
libc.so.6 => /lib/libc.so.6 (0x00002ad36a9d8000)
/lib64/ld-linux-x86-64.so.2 (0x00002ad369b2d000)

Как видите, зависимость первого уровня корректно обрабатывается rpath, но зависимости второго уровня, то есть зависимости libpython, возвращаются к системным библиотекам. И да, libpython имеет точно такой же заголовок RPATH в своем двоичном файле. Я нашел ваш вопрос, когда гуглял rpath recursive, чтобы попытаться решить мою проблему создания независимого от дистрибутива пакета.

Добавлено позже Заголовки rpath изменяют только ПЕРВЫЙ путь поиска библиотек. Если их там нет, то загрузчик продолжает поиск в обычных местах. ldd содержит только фактический путь к библиотеке, которая была найдена в результате поиска. Когда я скопировал эти библиотеки в каталог rpath, все заработало. В принципе, нет простого способа найти все зависимости и скопировать их, просто ldd -v path/to/python и некоторый анализ этого вывода.

1 голос
/ 30 августа 2016

Check моя модифицированная версия вашего скрипта make . По сути, следует использовать дополнительный -rpath-link без $ORIGIN, поскольку ld вообще не понимает $ORIGIN.

Что касается ваших вопросов.

  1. $ORIGIN работает только во время выполнения, и это w.r.t. каждая библиотека. Таким образом, разные общие библиотеки имеют разные $ORIGIN.
  2. Боюсь, лучший способ - добавить rpath-link, и это не повлияет на вашу переносимость, поскольку они являются относительными и не будут существовать в конечном исполняемом файле, как я показал в моей версии make.sh

Кроме того, это мое собственное понимание всего связующего материала . Надеюсь, это поможет.

1 голос
/ 03 июля 2012

Я также изучал это, и, насколько я могу судить, вам нужно использовать -rpath-link для любого пути, который будет использовать расширение ORIGIN.Например:

CC -shared (other flags) -R'$ORIGIN/../lib/' -o /buildpath/lib/libmylib1.so
CC -shared (other flags) -R'$ORIGIN/../lib/' -lmylib1 -o /buildpath/lib/libmylib2.so
# This fails to link 'somebinary'
CC (various flags) -R'$ORIGIN/../lib/' -lmylib2 -o /buildpath/bin/somebinary
# This works correctly
CC (various flags) -R'$ORIGIN/../lib/' -Wl,-rpath-link,/buildpath/lib/mylib1 -lmylib2 -o /buildpath/bin/somebinary
# The text above the carets to the right is a typo: ------------------^^^^^^
# I'm fairly sure it should read like this (though it has been awhile since I wrote this):
# (...) -Wl,-rpath-link,/buildpath/lib -lmylib1 (...)

ld не будет расширять $ORIGIN в пределах путей, указанных с помощью -rpath-link, или путей, которые он извлекает из RPATH зависимостей.В приведенном выше примере mylib2 зависит от mylib1;при связывании somebinary, ld пытается найти mylib1, используя буквенную / нерасширенную строку $ORIGIN/../lib/, встроенную в libmylib2.so.ld.so будет во время выполнения, но не ld.

Он также не будет использовать пути, указанные в -L, чтобы найти библиотеку зависимостей (y | ies).

0 голосов
/ 13 декабря 2018

Насколько я понимаю, это проблема в ld (то есть в binutils) и в том, как она разрешает "Вторичные зависимости"

AFAIK начался с binutils >= 2.30.rpath в зависимостях добавляется в поиск.то есть ld ... main.exe найти foo.so, затем прочитать RPATH в foo.so и найти bar.so

Здесь мой вопрос переполнения стека: Изменение вторичной зависимости Binutils

И вот мое исследование различных дистрибутивов (внутри контейнера докера) для тестирования различных версий binutils https://github.com/Mizux/SecondaryDependency

примечание: посмотрите журнал travis-CI ...

...