【问题标题】:How to build a shared library (.so) without hardcoded full dependency paths?如何在没有硬编码的完整依赖路径的情况下构建共享库(.so)?
【发布时间】:2011-12-16 05:45:43
【问题描述】:

我需要构建两个第三方共享库,因此它们的 .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 中删除完整的硬编码路径?

谢谢。

【问题讨论】:

  • 如何链接?使用 libtool?在这种情况下,patchelf 可能会帮助您摆脱编码路径(RPATH)。
  • 为什么不使用不依赖于您的环境的前缀,即~?为什么不使用像 /opt 这样的前缀,它不依赖于像 $HOME (~) 这样的环境变量?你可以试试LD_PRELOAD也许
  • 理想情况下,您可以更改configure 脚本以防止出现此问题。如果您想编辑库,我听说过有关 elfsh (eresi-project.org) 的好消息。
  • @another.anon.coward,为了清楚起见,我在这里使用 ~ 前缀。实际上,它是某个目录的完整路径。但无论如何,这个目录对我的机器来说是唯一的,不一定存在于其他机器上。
  • configure 脚本设计用于这种情况。是的,在您仔细阅读之前,从文档中并不完全清楚。

标签: c++ gcc linker shared-libraries


【解决方案1】:

您必须使用--prefix 值,这将在两个包的运行时环境中有效!

安装时在 make 命令行上覆盖 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 仍然会被添加到它的前面,而它不会受到prefix 的影响。

【讨论】:

  • 谢谢扬! DESTDIR 正是我所需要的。
【解决方案2】:
-Wl,-rpath,~/deps/A/lib:~/deps/B/lib:~/dev/MyApp/bin

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

如果有可能影响这一点的选项,请与./configure --help 联系。另一种选择是手动编辑 makefile 并删除此选项。

== 编辑2 == 还有一件事

-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”也会导致保存路径。 Soname 在构建共享库时使用-Wl,-soname,<soname> 链接器选项设置。

如果共享库中设置了soname,使用“~/deps/A/lib/libA.so”形式链接即可。

就像在 cmets 中提到的 Jan,更好的方法是使用“-Llibrary/path -llibrary_name”而不使用 rpath

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

【讨论】:

  • IIRC -Lpath -lname 确实编码完整路径,而path/name(没有-l;你不能写-lpath/name确实 对其进行编码。所以必须使用-L + -l 组合,而不能使用-Wl,-rpath,因为这会在使用-l 避免它之后添加路径。
  • 无论如何,是 libtool 添加了 rpath,它这样做是为了在您想在家中安装东西的更常见情况下工作。
  • @Jan Hudec 我没有说 -L/-l 是问题(rpath 是),只是提到在几个参数中存在一些不必要的库路径重复。无论如何,删除编辑以避免混淆。
【解决方案3】:

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

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

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

OP 正在使用“配置”,所以这不是一个容易实现的解决方案,除非他愿意深入了解配置脚本生成的 Makefile。

【讨论】:

    【解决方案4】:

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

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

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

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

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

    【讨论】:

      【解决方案5】:

      我刚刚发现我遇到了同样的问题。 以上答案都没有帮助我。 每当我尝试

      ldd libB.so 
      

      我会进入输出:

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

      所以我认为我有一个硬编码的路径。结果我忘记了我为本地测试设置了 LD_LIBRARY_PATH 。清除 LD_LIBRARY_PATH 意味着 ldd 在 /usr/lib/ 中找到了正确的安装库

      【讨论】:

      • 使用objdump -p libB.so | grep NEEDED查看,而不是ldd
      【解决方案6】:

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

      【讨论】:

      • 您的意思是在我的应用程序或B 库中使用这些标志?
      • 嗯,正好相反。他需要使用 -rpath 来STOP; rpath 正在被存储,但完全错误。
      • ... 并且 libtool 正在从它的 .la 可憎中添加 -rpath,所以他需要告诉 libtool 不要把它放在首位...
      猜你喜欢
      • 1970-01-01
      • 2016-02-13
      • 2017-02-15
      • 1970-01-01
      • 2014-04-19
      • 2019-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多