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

Ld: Использование -rpath, $ORIGIN внутри общей библиотеки (рекурсивный)

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

Структура файла во время выполнения:

  • проект /
    • Библиотека /
      • реж /
        • к югу /
          • bar.so
        • foo.so
    • бежать /
      • 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.

Я собираю 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: предупреждение: bar.so, необходимый для lib/dir/foo.so, не найден (попробуйте использовать чернила -rpath или -rpath -l)

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

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

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

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

4b9b3361

Ответ 1

Хорошо, у меня что-то работает. Но я не понимаю, почему это работает. Это кажется ошибкой для меня.

Я выполнил 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.so" , а не только "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,. вставлен.

[добавление]

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

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

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

Если вы переименуете bar.so в libbar.so и ссылку foo.so, чтобы использовать -lbar вместо -l:bar.so, тот же самый readelf показывает, что foo.so теперь зависит от "libbar.so" (без пути компонент). С помощью этого 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).

Ответ 2

Из ld-linux (8) manpage:

$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

Ответ 3

Во-первых, есть проблемы с расширением $sign, которые могут вызвать проблемы. Я создаю 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 в своем двоичном формате. Я нашел ваш вопрос во время поиска в googling rpath recursive, чтобы попытаться решить мою проблему создания дистрибутивного независимого пакета.

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

Ответ 4

Я тоже изучал это, и, насколько я могу судить, вам нужно использовать -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).

Ответ 5

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

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

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

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

Ответ 6

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

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

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

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

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