C++ 如何在没有硬编码完全依赖路径的情况下构建共享库(.so)?
我需要建立两个第三方共享库,所以他们的.so文件将被其他项目重用。但是,在构建之后,其中一个库包含到另一个库的硬编码路径。此路径在其他计算机上无效,并导致链接器警告。如何防止在生成的.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
~/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