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

Создание простого (hello-world-esque) примера использования ld-опции -rpath с $ORIGIN

Примечание. Полный рабочий пример ниже. Исходный вопрос следует:

У меня возникают проблемы с использованием параметра ld -rpath с $ORIGIN.
Поскольку я не мог найти полный пример, я думал, что попытаюсь написать его сам, чтобы я и другие могли использовать его позже. Как только я получу его работу, я приправлю его.

I спросил об этом раньше, но я думаю, что мой пост был немного запутанным.

Пример проекта создает одну общую библиотеку и один исполняемый файл, который ссылается на указанную библиотеку.
Он очень маленький (3 файла, 22 строки, включая buildscript).
Вы можете загрузить проект из здесь


Структура файла (до сборки):

  • project/
    • src/
      • foo.cpp
      • main.cpp
    • make.sh

project/src/foo.cpp


int foo()
  { return 3; }

project/src/main.cpp


int foo();

#include <iostream>
int main()
  {
    std::cout << foo() << std::endl;
    return 0;
  }

project/make.sh


# Make directories:
mkdir -p -v obj
mkdir -p -v lib
mkdir -p -v run

# Build the library:
g++ -c -o obj/foo.o src/foo.cpp -fPIC
g++ -shared -o lib/foo.sh obj/foo.o

# Build the executable:
g++ -c -o obj/main.o src/main.cpp
g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../../lib' -Llib -l:foo.sh

В каталоге project запустите make.sh (убедитесь, что он выполним).


Структура файла (после сборки):

  • project/
    • src/
      • foo.cpp
      • main.cpp
    • obj/
      • foo.o
      • main.o
    • lib/
      • foo.so
    • run/
      • main.run
    • make.sh

run/main.run теперь должен загружать lib/foo.sh при выполнении из любой точки.


Проблемы

В настоящее время это работает только частично. Файлы компилируются и имеют ссылку OK, но при запуске из любого каталога он не может связываться, кроме project (который является точкой упражнения).

Проверка main.run на readelf -d показывает:
0x0000000000000001 (NEEDED) Shared library: [lib/foo.sh]
0x000000000000000f (RPATH) Library rpath: [$ORIGIN/../../lib] Что выглядит близко (я бы предпочел [foo.sh], чем [lib/foo.sh], но я исправлю это позже).

AFAICT $ORIGIN в -Wl,-rpath,'$ORIGIN/../../lib' означает project/run/main.run, поэтому этот путь rpath должен стать project/lib.

Я пробовал $ORIGIN/.., $ORIGIN/../lib, $ORIGIN/../.., $ORIGIN/../../lib безрезультатно.

Примечание. Я использую -l:, для которого требуется полное имя файла библиотеки (среди других причин проще script с переменными, когда все функции принимают одинаковый формат имени).

Кто-нибудь знает, почему это не работает?
Или поочередно, есть ли кто-либо или знаете полный рабочий пример?

4b9b3361

Ответ 1

(я бы предпочел [foo.sh] чем [lib/foo.sh], но я исправлю это позже).

В большинстве случаев ваша проблема: / в имени запрещает динамический компоновщик делать магию rpath.

(Ваш rpath тоже не прав. Подумайте об этом: из оболочки, если бы вы были в каталоге, где находится ваш исполняемый файл, как бы вы попали в каталог, где находится ваша библиотека? Здесь вам нужно будет cd ../lib. Итак, ваш rpath должен быть $ORIGIN/../lib.)

Если вы построили свой объект как libfoo.so и связали его с -Llib -lfoo, компоновщик разработал то, что вы намеревались, и делайте правильные вещи. Но если вы собираетесь использовать необычные соглашения об именах, вам нужно будет помочь:

  • Измените линию ссылок для библиотеки, чтобы явным образом установить SONAME для вашей библиотеки только foo.sh:

    g++ -shared -Wl,-soname,foo.sh -o lib/foo.sh obj/foo.o

  • Исправить rpath:

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

Полезно запустить ldd main/main.run, чтобы узнать, что происходит. В вашем исходном случае с ошибкой вы увидите что-то вроде:

    lib/foo.sh (0xNNNNNNNN)

(отсутствие какого-либо => /some/resolved/path, показывающего, что он не сделал никакого разрешения пути). В фиксированном случае вы увидите что-то вроде:

    foo.sh => /your/path/to/run/../lib/foo.sh (0xNNNNNNNN)

Ответ 2

Это пример привязки относительного пути (с ld) с помощью $ORIGIN в rpath.

rpath - это путь (или набор путей), встроенный в двоичные файлы (разделяемые библиотеки (.so) и исполняемые файлы).
Эти пути являются основными путями поиска для общих библиотек, с которыми двоичный файл должен быть связан во время выполнения.

$ORIGIN - это потенциальный каталог запуска для пути rpath. Он разрешает каталог, содержащий исполняемый файл. (например: $ORIGIN/lib)

Пример проекта создает одну общую библиотеку и один исполняемый файл, который ссылается на указанную библиотеку с помощью rpath и $ORIGIN.
Вы можете загрузить проект из здесь.


Структура файла (до сборки):

  • project/
    • src/
      • foo.cpp
      • main.cpp
    • make.sh

project/src/foo.cpp


int foo()
  { return 3; }

project/src/main.cpp


int foo();

#include <iostream>
int main()
  {
    std::cout << foo() << std::endl;
    return 0;
  }

project/make.sh


# Make directories:
mkdir -p -v obj
mkdir -p -v lib/dir
mkdir -p -v run

# Build the library:
g++ -c -o obj/foo.o src/foo.cpp -fPIC
g++ -shared -o lib/dir/foo.so -Wl,-soname,foo.so obj/foo.o

# Build the executable:
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

В каталоге project запустите make.sh (если он не будет запущен, убедитесь, что make.sh имеет разрешения на выполнение).

Если все прошло нормально, main.run теперь должен загружать lib/dir/foo.so при выполнении, независимо от абсолютного пути к project (вы можете переместить его в любом месте) и независимо от текущего рабочего каталога (вы можете запустить его из любого места).


Примечания:

  • -fPIC инструктирует компилятор для создания перемещаемых объектных файлов (файлы объектов, встроенные в разделяемые библиотеки, должны быть перемещены).
  • -Wl,-soname,<NAME> внедряет <NAME> в сгенерированную библиотеку. Это должно соответствовать имени, которое вы указываете для опций -l или -l: при подключении к этой библиотеке.
  • -Wl,-rpath,'<PATH>' внедряет <PATH> в сгенерированную библиотеку как путь поиска библиотеки времени выполнения (или rpath - см. выше).
  • -l добавляет путь к списку путей поиска библиотеки build-time. (Примечание: rpath не имеет значения во время сборки, -l не имеет значения во время выполнения).
  • -l: добавляет имя файла (без пути) библиотеки для ссылки. (Подобно -l, кроме -l: требуется полное имя файла.

Структура файла (после сборки):

  • project/
    • src/
      • foo.cpp
      • main.cpp
    • obj/
      • foo.o
      • main.o
    • lib/
      • dir/
        • foo.so
    • run/
      • main.run
    • make.sh

Примечание. Я использую -l:, для которого требуется полное имя файла библиотеки (среди других причин проще script с переменными, когда все функции имеют одинаковый формат имени).
Чаще всего используется -l, где -l<NAME> обозначает lib.so.

Ограничения

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

Итак, если у вас есть две библиотеки с одинаковым именем, но в разных местах, вы не сможете их связать. (Надеюсь, я ошибаюсь или это исправляется).