Создание простого (hello-world-esque) примера использования ld-опции -rpath с $ORIGIN
Примечание. Полный рабочий пример ниже. Исходный вопрос следует:
У меня возникают проблемы с использованием параметра ld -rpath
с $ORIGIN
.
Поскольку я не мог найти полный пример, я думал, что попытаюсь написать его сам, чтобы я и другие могли использовать его позже. Как только я получу его работу, я приправлю его.
I спросил об этом раньше, но я думаю, что мой пост был немного запутанным.
Пример проекта создает одну общую библиотеку и один исполняемый файл, который ссылается на указанную библиотеку.
Он очень маленький (3 файла, 22 строки, включая buildscript).
Вы можете загрузить проект из здесь
Структура файла (до сборки):
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/
-
obj/
-
lib/
-
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 с переменными, когда все функции принимают одинаковый формат имени).
Кто-нибудь знает, почему это не работает?
Или поочередно, есть ли кто-либо или знаете полный рабочий пример?
Ответы
Ответ 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
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/
-
obj/
-
lib/
-
run/
-
make.sh
Примечание. Я использую -l:
, для которого требуется полное имя файла библиотеки (среди других причин проще script с переменными, когда все функции имеют одинаковый формат имени).
Чаще всего используется -l
, где -l<NAME>
обозначает lib.so.
Ограничения
Насколько я знаю (исправьте меня, если я ошибаюсь) нет возможности добавить библиотеку внутри подкаталога в пути поиска (кроме добавления этого каталога в качестве подкаталога). Это верно для путей поиска времени сборки (-l
) и времени выполнения (-rpath
).
Итак, если у вас есть две библиотеки с одинаковым именем, но в разных местах, вы не сможете их связать. (Надеюсь, я ошибаюсь или это исправляется).