В чем разница между `-rpath-link` и` -L`?

Человек для gold заявляет:

  -L DIR, --library-path DIR
          Add directory to search path

  --rpath-link DIR
          Add DIR to link time shared library search path

Человек для bfd ld делает вид, будто -rpath-link используется для рекурсивно включаемых sos.

ld.lld даже не перечисляет это как аргумент.

Может ли кто-нибудь прояснить мне эту ситуацию?


person lanza    schedule 06.03.2018    source источник


Ответы (2)


Вот демонстрация для 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
comment
Это отличный ответ, позвольте мне просто добавить к этому, что в «современных» версиях GCC будет использоваться RUNPATH. По умолчанию только старые компиляторы имеют RPATH. RUNPATH не заставляет зависимости наследовать путь поиска, он применяется только к пути поиска текущего двоичного файла. В приведенном выше примере: libfoobar.so будет найден во время выполнения, но libfoo.so и libbar.so - нет. Вам также нужно будет указать -Wl,-rpath=$(pwd) для libfoobar.so :) или вам нужно будет сделать -Wl,--disable-new-dtags, чтобы получить RPATHS, и тогда пример Майка все еще действителен. - person Harmen; 03.09.2020
comment
Что мне до сих пор неясно: почему бы не позволить динамическим зависимостям также смотреть в путь, указанный -L? Или, в качестве альтернативы, почему бы не искать библиотеки по пути, указанному -rpath-link? В чем причина существования -L и -rpath-link? - person Bart; 02.07.2021
comment
Спасибо за ОЧЕНЬ обстоятельный и поучительный ответ. - person nic; 21.07.2021

Параметр --rpath-link используется bfd ld для добавления к пути поиска, используемому для поиска разделяемых библиотек DT_NEEDED при разрешении символов во время компоновки. По сути, он сообщает компоновщику, что использовать в качестве пути поиска во время выполнения при попытке имитировать то, что динамический компоновщик будет делать при разрешении символов (как установлено параметрами --rpath или переменной среды LD_LIBRARY_PATH).

Gold не следует за записями DT_NEEDED при разрешении символов в разделяемых библиотеках, поэтому параметр --rpath-link игнорируется. Это было продуманное дизайнерское решение; косвенные зависимости не обязательно должны присутствовать или находиться в местах их выполнения во время процесса связывания.

person Cary Coutant    schedule 07.03.2018
comment
Спасибо! Мы получали ошибки компоновщика и много раз пытались использовать -rpath или -rpath-link, и оказалось, что мы случайно переключились с gold на bfd. - person A_K; 12.09.2018