Вот демонстрация для GNU ld
разницы между -L
и -rpath-link
и, для хорошей оценки, разницы между -rpath-link
и -rpath
.
foo.c
#include <stdio.h>
void foo(void)
{
puts(__func__);
}
bar.c
#include <stdio.h>
void bar(void)
{
puts(__func__);
}
foobar.c
extern void foo(void);
extern void bar(void);
void foobar(void)
{
foo();
bar();
}
main.c
extern void foobar(void);
int main(void)
{
foobar();
return 0;
}
Создайте две общие библиотеки, libfoo.so
и libbar.so
:
$ gcc -c -Wall -fPIC foo.c bar.c
$ gcc -shared -o libfoo.so foo.o
$ gcc -shared -o libbar.so bar.o
Создайте третью разделяемую библиотеку, libfoobar.so
, которая зависит от первых двух;
$ gcc -c -Wall -fPIC foobar.c
$ gcc -shared -o libfoobar.so foobar.o -lfoo -lbar
/usr/bin/ld: cannot find -lfoo
/usr/bin/ld: cannot find -lbar
collect2: error: ld returned 1 exit status
Ой. Компоновщик не знает, где искать, чтобы разрешить -lfoo
или -lbar
.
Опция -L
исправляет это.
$ gcc -shared -o libfoobar.so foobar.o -L. -lfoo -lbar
Параметр -Ldir
сообщает компоновщику, что dir
является одним из каталогов для поиска библиотек, разрешающих заданные -lname
параметры. Сначала он ищет -L
каталоги в порядке их командной строки; затем он ищет свои настроенные каталоги по умолчанию в их настроенном порядке.
Теперь создайте программу, которая зависит от libfoobar.so
:
$ gcc -c -Wall main.c
$ gcc -o prog main.o -L. -lfoobar
/usr/bin/ld: warning: libfoo.so, needed by ./libfoobar.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: warning: libbar.so, needed by ./libfoobar.so, not found (try using -rpath or -rpath-link)
./libfoobar.so: undefined reference to `bar'
./libfoobar.so: undefined reference to `foo'
collect2: error: ld returned 1 exit status
Опять-таки. Компоновщик обнаруживает динамические зависимости, запрошенные libfoobar.so
, но не может их удовлетворить. Давайте немного воздержимся от его совета - try using -rpath or -rpath-link
- и посмотрим, что мы можем сделать с -L
и -l
:
$ gcc -o prog main.o -L. -lfoobar -lfoo -lbar
Все идет нормально. Но:
$ ./prog
./prog: error while loading shared libraries: libfoobar.so: cannot open shared object file: No such file or directory
во время выполнения загрузчик не может найти libfoobar.so
.
А как насчет совета линкера? С -rpath-link
мы можем:
$ gcc -o prog main.o -L. -lfoobar -Wl,-rpath-link=$(pwd)
и эта связь также успешна. ($(pwd)
означает P rint W orking D каталог и просто копирует текущий путь.)
Параметр -rpath-link=dir
сообщает компоновщику, что, когда он встречает входной файл, который запрашивает динамические зависимости - например, libfoobar.so
, - он должен искать каталог dir
, чтобы разрешить их. Поэтому нам не нужно указывать эти зависимости с помощью -lfoo -lbar
и даже не нужно знать, что это такое. Это информация, уже записанная в динамическом разделе _39 _: -
$ readelf -d libfoobar.so
Dynamic section at offset 0xdf8 contains 26 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libfoo.so]
0x0000000000000001 (NEEDED) Shared library: [libbar.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
...
...
Нам просто нужно знать каталог, в котором они могут быть найдены, какими бы они ни были.
Но дает ли это нам работоспособный prog
?
$ ./prog
./prog: error while loading shared libraries: libfoobar.so: cannot open shared object file: No such file or directory
Нет. То же, что и раньше. Это потому, что -rpath-link=dir
дает компоновщику информацию, которая может понадобиться загрузчику для разрешения некоторых динамических зависимостей prog
во время выполнения - при условии, что это остается верным во время выполнения, - но он не записывает эту информацию в динамический раздел prog
. Это просто позволяет соединению быть успешным, без необходимости описывать все рекурсивные динамические зависимости связывания с -l
параметрами.
Во время выполнения libfoo.so
, libbar.so
- и действительно libfoobar.so
- вполне могут быть не там, где они сейчас - $(pwd)
, но загрузчик может найти их другими способами: через _ 51_ кеш или значение переменной среды LD_LIBRARY_PATH
, например:
$ export LD_LIBRARY_PATH=.; ./prog
foo
bar
rpath=dir
предоставляет компоновщику ту же информацию, что и rpath-link=dir
, и инструктирует компоновщик записать эту информацию в динамический раздел выходного файла. Попробуем это:
$ export LD_LIBRARY_PATH=
$ gcc -o prog main.o -L. -lfoobar -Wl,-rpath=$(pwd)
$ ./prog
foo
bar
Все хорошо. Потому что теперь prog
содержит информацию о том, что $(pwd)
- это путь поиска времени выполнения для разделяемых библиотек, от которого он зависит, как мы видим:
$ readelf -d prog
Dynamic section at offset 0xe08 contains 26 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libfoobar.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000f (RPATH) Library rpath: [/home/imk/develop/so/scrap]
... ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
Этот путь поиска будет испробован после каталогов, перечисленных в LD_LIBRARY_PATH
, если они установлены, и перед системными значениями по умолчанию - каталогами ldconfig
-ed, а также /lib
и /usr/lib
.
person
Mike Kinghan
schedule
08.03.2018