build立一个简单的(hello-world-esque)例子,用$ ORIGIN使用ld的选项-rpath

注意:下面是完整的工作示例。 原来的问题如下:

使用$ORIGIN使用ld的-rpath参数时遇到问题。
由于我找不到一个完整的例子,我想我会自己写一个,以便我和其他人以后可以使用它。 一旦我得到它的工作,我会整理它。

之前我问过这个问题 ,但是我觉得我的post有点混乱。

示例项目构build一个共享库和一个链接到所述库的可执行文件。
这是非常小的(3个文件,22行,包括buildscript)。
您可以从这里下载该项目


文件结构(build设之前):

  • 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 (确保它是可执行的)。


文件结构(build立后):

  • 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

问题

目前,这只是部分工作。
这些文件编译并链接正常,但从project以外的任何目录(这是练习的要点)运行时,链接失败。

使用readelf -d显示检查main.run
0x0000000000000001 (NEEDED) Shared library: [lib/foo.sh]
0x000000000000000f (RPATH) Library rpath: [$ORIGIN/../../lib]看起来很接近(我宁愿[foo.sh][lib/foo.sh]但我会稍后解决)。

AFIGIC $ORIGIN in -Wl,-rpath,'$ORIGIN/../../lib'表示project/run/main.run所以这个rpath应该成为project/lib

我试过$ORIGIN/..$ORIGIN/../lib$ORIGIN/../..$ORIGIN/../../lib无济于事。

注:我正在使用-l:这需要完整的库文件名(其他原因,当所有函数采用相同的名称格式时,使用variables进行脚本更容易)。

有谁知道为什么这不起作用?
或者,有没有人有或知道一个完整的工作示例?

(我宁愿有[foo.sh][lib/foo.sh]但我会解决这个问题)。

你的问题最多的是: /这个名字阻止了动态链接器执行rpath的魔法。

(你的路径也是错的,想一下:从shell中,如果你当前在你的可执行文件所在的目录下,你将如何到达你的库所在的目录?在这里,你需要cd ../lib因此你的rpath应该是$ORIGIN/../lib 。)

如果您将对象构建为libfoo.so并链接到-Llib -lfoo ,则链接器将解决您的意图,并做正确的事情。 但是如果你打算使用不寻常的命名约定,你将不得不提供帮助:

  1. 更改库的链接线,以显式地将库的SONAME设置为foo.sh

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

  2. 修复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) 

这是一个通过在rpath使用$ORIGIN来相对路径链接(使用ld)的例子。

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>-Wl,-soname,<NAME>嵌入到生成的库中。 这应该与链接到此库时为-l-l:选项提供的名称匹配。
  • -Wl,-rpath,'<PATH>'<PATH>作为运行时库搜索路径(或rpath – 见上)嵌入到生成的库中。
  • -L将路径添加到构建时库搜索路径列表。 (注意:在构建时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:这需要完整的库文件名(其他原因,当所有函数采用相同的名称格式时,使用变量进行脚本更容易)。
使用-l更常见,其中-l<NAME>表示lib.so.

限制

据我所知(纠正我,如果我错了)没有办法在搜索路径内的子目录中添加一个库(除了添加该目录作为子路径)。 对于构建时( -L )和运行时( -rpath )搜索路径都是如此。

所以如果你有两个名字相同但位置不同的库,你将无法将它们链接起来。 (我希望我错了,或者这是固定的)。