“-rpath-link”和“-L”的区别是什么?

54

这位关于黄金的男士说:

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

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

这位关于 bfd 的男士说起 -rpath-link 声音有点像它是用于递归包含 sos 的。

ld.lld 甚至没有将其列为参数。

请问有人能为我澄清这种情况吗?

2个回答

151
这是一个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.solibbar.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请求的动态依赖项,但无法满足它们。让我们暂时忽略它的建议- 尝试使用-rpath或-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) 的意思是 "Print Working Directory",表示“打印当前工作目录”,只是“复制”了当前路径。)

-rpath-link=dir 选项告诉链接器,当它遇到请求动态依赖项(例如libfoobar.so)的输入文件时, 应该搜索目录dir来解决它们。因此,我们不需要使用-lfoo -lbar来指定这些依赖关系,甚至不需要知道它们是什么。 它们已经在libfoobar.so的动态部分中写好了信息:-

$ 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.solibbar.so - 甚至是 libfoobar.so - 可能不在它们现在所在的位置 - $(pwd) - 但是加载器可能能够通过其他方式定位它们:通过ldconfig缓存或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配置的目录以及/lib/usr/lib)之前搜索。


1
这是一个很好的回答,让我来补充一下,在“现代”版本的GCC中将使用RUNPATH。只有旧的编译器默认使用RPATH。RUNPATH不会使依赖项继承搜索路径,它仅适用于当前二进制文件的搜索路径。在上面的例子中:libfoobar.so将在运行时找到,但libfoo.solibbar.so将不会找到。您还需要为 libfoobar.so指定 -Wl,-rpath=$(pwd) :) 或者要获取RPATHS,则必须执行 -Wl,--disable-new-dtags,然后Mike的示例仍然有效。 - Harmen
2
对我来说仍然不清楚的是:为什么不让动态依赖项也在-L提供的路径中查找?或者,为什么不在-rpath-link指定的路径中搜索库?为什么同时存在-L和-rpath-link的原理是什么? - Bart

11

--rpath-link选项是bfd ld用于添加搜索路径的选项,以便在链接时解析符号时找到DT_NEEDED共享库。当尝试模拟动态链接器解析符号时(由--rpath选项或LD_LIBRARY_PATH环境变量设置),它基本上告诉链接器要使用什么作为运行时搜索路径。

Gold在解析共享库中的符号时不遵循DT_NEEDED条目,因此--rpath-link选项会被忽略。这是一个有意设计的决定;在链接过程中,间接依赖项不需要出现或位于其运行时位置。


谢谢!我们遇到了链接器错误和大量的“尝试使用-rpath或-rpath-link”的问题,结果发现我们不小心从gold切换到了bfd。 - A_K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接