注意:现在完整的工作示例如下。原始问题如下:
我在使用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:
,这需要完整的库文件名(除其他原因外,当所有函数采用相同的名称格式时,使用变量编写脚本更容易)。
有谁知道为什么这不起作用?
或者,是否有人拥有或知道完整的工作示例?
答案 0 :(得分:6)
(我宁愿
[foo.sh]
而不是[lib/foo.sh]
但我稍后会解决这个问题。
你的大多数问题都是:名称中的/
会阻止动态链接器执行rpath魔术。
(你的rpath也是错的。想想看:从shell开始,如果你当前在你的可执行文件所在的目录中,你将如何到达你的库所在的目录?在这里,你需要{ {1}}。所以你的rpath应该是cd ../lib
。)
如果您将对象构建为$ORIGIN/../lib
并与libfoo.so
链接,则链接器将确定您的意图,并做正确的事情。但是,如果您要使用不寻常的命名约定,则必须提供帮助:
更改库的链接行,以便将库的SONAME显式设置为-Llib -lfoo
:
foo.sh
修复rpath:
g++ -shared -Wl,-soname,foo.sh -o lib/foo.sh obj/foo.o
运行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
)搜索路径都是如此。
因此,如果您有两个名称相同但位置不同的库,则无法将它们都链接起来。 (我希望我错了或者这个问题得到解决)。