如何在没有硬编码完整依赖路径的情况下构建共享库(.so)?

时间:2011-10-27 04:35:54

标签: c++ gcc linker shared-libraries

我需要构建两个第三方共享库,因此其他项目将重用其.so文件。但是,在构建之后,这些库中的一个包含到另一个库的硬编码路径。此路径在其他计算机上无效并导致链接器警告。如何防止将完整路径嵌入到生成的.so文件中?

详细说明:

第一个图书馆来源:~/dev/A
第二个图书馆来源:~/dev/B

他们都有configure脚本来生成make文件。图书馆B取决于A。所以,首先我构建A

$ ~/dev/A/configure --prefix=~/dev/A-install
$ make && make install

然后我建立B

$ ~/dev/B/configure --prefix=~/dev/B-install --with-A=~/dev/A-install
$ make && make install

然后我想将~/dev/A-install~/dev/B-install的内容上传到我们的文件服务器,以便其他团队和构建机器可以使用二进制文件。但是当他们尝试使用B时,他们会收到链接器警告:

/usr/bin/ld: warning: libA.so.2, needed by /.../deps/B/lib/libB.so, not found (try using -rpath or -rpath-link)

当我运行ldd libB.so时,它会给出:

...
libA.so.2 => /home/alex/dev/A-install/lib/libA.so.2

显然这条路径只存在于我的机器上,在其他机器上找不到。

如何从libB.so删除完整的硬编码路径?

感谢。

6 个答案:

答案 0 :(得分:4)

对于两个软件包,您必须使用在运行时环境中有效的--prefix值!

在安装时,覆盖prefixDESTDIRprefix替换前缀,DESTDIR前置,但工作更可靠)。 。像:

~/dev/A$ ./configure
~/dev/A$ make 
~/dev/A$ make install prefix=~/dev/A-install
~/dev/B$ ./configure --with-A=~/dev/A-install
~/dev/B$ make
~/dev/B$ make install prefix=~/dev/B-install

或(这是首选,所有包构建工具都使用它):

~/dev/A$ ./configure
~/dev/A$ make 
~/dev/A$ make install DESTDIR=~/dev/A-install
~/dev/B$ ./configure --with-A=~/dev/A-install/usr/local
~/dev/B$ make
~/dev/B$ make install prefix=~/dev/B-install

因为这种方式安装到~/dev/A-install/$prefix,所以使用默认前缀~/dev/A-install/usr/local。后一个选项的优点是,如果您重新定义某些特定的安装路径而不引用前缀(例如--sysconfdir=/etc),DESTDIR仍然会被添加到前面,而它不会受{ {1}}。

答案 1 :(得分:2)

-Wl,-rpath,~/deps/A/lib:~/deps/B/lib:~/dev/MyApp/bin

此链接器选项负责保存库中的路径。你需要以某种方式删除它。

请参阅./configure --help是否有可能影响此选项的选项。另一种选择是手动编辑makefile并删除此选项。

== edit2 == 还有一件事

-L~/deps/A/lib -L~/deps/B/lib ~/deps/A/lib/libA.so ~/deps/B/lib/libB.so

如果libA.so和libB.so没有SONAME,将它们链接为“〜/ deps / A / lib / libA.so”也会导致保存路径。在构建共享库时,使用-Wl,-soname,<soname>链接器选项设置Soname。

如果在共享库中设置了soname,则使用“~/deps/A/lib/libA.so”表单链接它是正常的。

与评论中提到的Jan一样,更好的方法是在没有-Llibrary/path -llibrary_name的情况下使用“rpath”。

-L~/deps/A/lib -L~/deps/B/lib -lA -lB

答案 2 :(得分:1)

  

当我运行ldd libB.so时,它会给出:

     

libA.so.2 =&gt; /home/alex/dev/A-install/lib/libA.so.2

此问题的低级解决方案是在链接libA.so库时使用选项“-soname = libA.so”。通过为共享对象定义SONAME,链接器在链接该共享对象时不会嵌入绝对路径。

OP正在使用“configure”,因此这不是一个简单的实现方案,除非他愿意进入configure脚本生成的Makefile的内容。

答案 3 :(得分:0)

我只是觉得我有同样的问题。 上述答案都没有帮助我。 每当我尝试

ldd libB.so 

我会进入输出:

libA.so.1 => /home/me/localpath/lib/libA.so.1.0

因此我认为我有一条硬编码路径。事实证明我忘了我已经为本地测试设置了LD_LIBRARY_PATH。清除LD_LIBRARY_PATH意味着ldd在/ usr / lib /

中找到了正确的已安装库

答案 4 :(得分:0)

除了操作系统配置中的列表之外,共享库和可执行文件还具有用于查找共享库的目录列表。 RPATH用于添加在运行时使用的共享库搜索路径。 如果要在RPATH中使用相对路径,则大多数Linux / UNIX(而不是AIX)系统支持一种特殊的语法-$ORIGIN${ORIGIN}
$ORIGIN将在运行时扩展到二进制文件所在的目录-库或可执行文件。

因此,如果将可执行二进制文件放在prefix / bin /中,并将共享库放在prefix/lib/中,则可以将条目添加到${ORIGIN}/../lib之类的RPATH中,并将在运行时扩展到prefix/bin/../lib/

在Makefile中获取正确的语法通常是一个技巧,因为您必须转义ORIGIN中的$,以使其无法扩展。通常在Makefile中执行此操作:

 g++  -Wl,-rpath=\$${ORIGIN}/../lib

Make和Shell都希望在您的环境中查找名为ORIGIN的变量-因此需要对其进行两次转义。

答案 5 :(得分:-1)

使用 ld -rpath-soname选项可能有所帮助(假设最近的Linux系统上ld的binutils或binutils.gold包) ?