构建一个简单的(hello-world-esque)示例,使用带有$ ORIGIN的ld选项-rpath

时间:2011-06-10 19:11:02

标签: linux gcc linker rpath

注意:现在完整的工作示例如下。原始问题如下:

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

asked about this before,但我认为我的帖子有点令人困惑。

示例项目构建一个共享库和一个链接到所述库的可执行文件 它非常小(3个文件,包含buildcript的22行) 您可以从here

下载项目

文件结构(在构建之前):

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


文件结构(构建后):

  • 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


问题

目前,这只是部分有效 文件编译并链接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:,这需要完整的库文件名(除其他原因外,当所有函数采用相同的名称格式时,使用变量编写脚本更容易)。

有谁知道为什么这不起作用?
或者,是否有人拥有或知道完整的工作示例?

2 个答案:

答案 0 :(得分:6)

  

(我宁愿[foo.sh]而不是[lib/foo.sh]但我稍后会解决这个问题。

你的大多数问题都是:名称中的/会阻止动态链接器执行rpath魔术。

(你的rpath也是错的。想想看:从shell开始,如果你当前在你的可执行文件所在的目录中,你将如何到达你的库所在的目录?在这里,你需要{ {1}}。所以你的rpath应该是cd ../lib。)

如果您将对象构建为$ORIGIN/../lib并与libfoo.so链接,则链接器将确定您的意图,并做正确的事情。但是,如果您要使用不寻常的命名约定,则必须提供帮助:

  1. 更改库的链接行,以便将库的SONAME显式设置为-Llib -lfoo

    foo.sh

  2. 修复rpath:

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

  3. 运行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

答案 1 :(得分:5)

这是使用$ORIGIN中的rpath进行相对路径链接(使用ld)的示例。

rpath 是嵌入在二进制文件(共享库(.so)和可执行文件)中的路径(或一组路径)。
这些路径是二进制文件必须在运行时链接的共享库的最重要的搜索路径。

$ ORIGIN 是rpath路径的潜在起始目录。 它解析为包含执行文件的目录。 (例如:$ORIGIN/lib

示例项目使用rpath$ORIGIN构建一个共享库和一个链接到所述库的可执行文件。
您可以从here下载项目。


文件结构(在构建之前):

  • 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><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)搜索路径都是如此。

因此,如果您有两个名称相同但位置不同的库,则无法将它们都链接起来。 (我希望我错了或者这个问题得到解决)。