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

C++ 如何在没有硬编码完全依赖路径的情况下构建共享库(.so)?,c++,gcc,linker,shared-libraries,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 i

我需要建立两个第三方共享库,所以他们的.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
/usr/bin/ld: warning: libA.so.2, needed by /.../deps/B/lib/libB.so, not found (try using -rpath or -rpath-link)
然后我想将
~/dev/A-install
~/dev/B-install
的内容上传到我们的文件服务器,以便其他团队和构建机器可以使用二进制文件。但当他们尝试使用
B
时,会收到链接器警告:

$ ~/dev/B/configure --prefix=~/dev/B-install --with-A=~/dev/A-install
$ make && make install
/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
中删除完整的硬编码路径


谢谢。

也许在ld中使用
-rpath
-soname
选项会有所帮助(假设在最近的Linux系统上为
ld
使用binutils或binutils.gold包)

此链接器选项负责保存库中的路径。你需要想办法把它去掉

如果有可能影响此操作的选项,请参阅使用
/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,则可以使用“
~/deps/A/lib/libA.so
”表单链接它

正如Jan在评论中提到的,更好的方法是使用“
-Llibrary/path-Llibrary\u name
”而不使用
rpath

-L~/deps/A/lib -L~/deps/B/lib -lA -lB
您必须使用
--prefix
值,该值在运行时环境中对这两个包都有效

然后在安装时覆盖make命令行上的
prefix
DESTDIR
prefix
替换前缀,
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-install/$prefix
,所以使用默认前缀
~/dev/A-install/usr/local
。后一个选项的优点是,如果您重新定义一些特定的安装路径而不引用前缀(比如说
--sysconfdir=/etc
),那么
DESTDIR
仍然会被预先添加到它,而它不会受到
前缀

的影响,我只是觉得自己也有同样的问题。 上面的答案对我都没有帮助。 每当我尝试

ldd libB.so 
我将得到输出:

libA.so.1 => /home/me/localpath/lib/libA.so.1.0
所以我认为我有一个硬编码的路径。结果我忘了我已经为本地测试设置了LD_LIBRARY_路径。清除LD_LIBRARY_PATH意味着ldd在/usr/lib中找到了正确的已安装库/

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

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

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


OP使用“configure”,因此这不是一个容易实现的解决方案,除非他愿意深入到由configure脚本生成的Makefile的内部。

除了操作系统配置中的列表外,共享库和可执行文件还有一个目录列表来查找共享库。RPATH用于添加运行时使用的共享库搜索路径。 如果希望在RPATH中使用相对路径,则大多数Linux/UNIX(但不是AIX)系统都支持一种特殊语法-
$ORIGIN
${ORIGIN}

$ORIGIN
将在运行时扩展到二进制文件所在的目录-库或可执行文件

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

在Makefile中获得正确的语法通常是一个小技巧,因为您必须在ORIGIN中转义
$
,这样它就不会被扩展。通常在生成文件中执行此操作:

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

Make和shell都希望在您的环境中查找名为
ORIGIN
的变量,因此它需要双转义。

如何链接?用libtool?在这种情况下,
patchelf
可能会帮助您摆脱编码路径(RPATH)。为什么不使用不依赖于您的环境的前缀,即
~
?为什么不使用像say
/opt
这样的前缀,它不依赖于环境变量,比如
$HOME
~
)?您可以尝试更改
configure
脚本以防止出现此问题。如果你想编辑这些库,我听说过关于elfsh()的好消息。@other.anon.coward,为了清晰起见,我在这里使用~prefix。实际上,它是指向某个目录的完整路径。但在任何情况下,此目录都是我的计算机所独有的,不一定存在于其他计算机上。
configure
脚本就是为在这种情况下使用而设计的。是的,在你仔细阅读之前,从文档中还不能完全看出这一点。你是想在我的应用程序中使用这些标志还是wit