【问题标题】:Can I force a dynamic library to link to a specific dynamic library dependency?我可以强制动态库链接到特定的动态库依赖项吗?
【发布时间】:2019-10-02 12:40:29
【问题描述】:

我正在构建一个动态库,libfoo.so,它依赖于libcrypto.so

在我的 autotools Makefile.am 文件中,我有这样一行:

libfoo_la_LIBADD += -L${OPENSSL_DIR}/lib -lcrypto

其中$OPENSSL_DIR 默认为/usr,但可以通过传递--with-openssl-dir=/whatever 来覆盖。

如何确保使用libfoo.so 的可执行文件使用${OPENSSL_DIR}/lib/libcrypto.so(仅限)构建或运行可执行文件的人必须使用rpath 或摆弄LD_LIBRARY_PATH

就目前而言,我可以构建 libfoo 并通过 --with-openssl-dir=/usr/local/openssl-special 并且它构建得很好。但是当我运行ldd libfoo.so 时,它只是指向/usr/lib 中的libcrypto.so

我能想到的唯一解决方案是将libcrypto.a 静态链接到libfoo.so。有没有其他可能的方法?

【问题讨论】:

  • 我不确定我是否理解。你已经排除了最有可能的可能性。除了“读懂我的想法”之外,您如何描述可接受的解决方案?
  • 此外,动态链接/加载细节因平台而异。您对哪个特定平台感兴趣?
  • @JohnBollinger 我可以接受“嗯……那是不可能的”的回答。我对主题领域不够熟悉,不知道我是否遗漏了其他东西。 W.r.t.平台,有问题的项目目前建立在 Linux 和 MacOS 上,所以一些可移植性会很好。我将把它添加到问题中。

标签: c shared-libraries static-linking autotools dynamic-linking


【解决方案1】:

运行时动态链接的细节因平台而异。 Autotools 可以在一定程度上使您与此隔离,但如果您关心细节,显然您会这样做,那么让 Autotools 为您选择可能是不够的。

话虽如此,但您似乎排除了所有种可能性:

  • 确保在运行时获得在构建时链接的特定实现的最可靠方法是静态链接。但你说你不想那样。

  • 如果您改为使用动态库,那么您依赖动态链接器在运行时将库实现与您的可执行文件相关联。在这种情况下,对于如何将 DL 定向到特定的库实现,有两种一般选择:

  1. 通过存储在程序/库二进制文件中的信息。您正在使用建议基于 ELF 的系统的术语,对于 ELF 共享对象,RPATH 和/或RUNPATH 传达有关在何处查找所需库的信息。没有与个别图书馆要求相关的路径信息;它们仅由 SONAME 识别。但是你说你不想使用RPATH*,所以我想也不是RUNPATH

  2. 通过动态链接器的静态或动态配置。这就是LD_LIBRARY_PATH 的用武之地,但你说你不想使用它。动态链接器通常还有一个或多个配置文件,例如/etc/ld.so.conf。在那里您可以指定要搜索的库目录,并且需要注意的是搜索它们的顺序。

然后,您可以通过更新动态链接器的配置文件使其首先搜索所需路径,从而将所需的库实现链接到您的应用程序。然而,这会影响整个系统,而且它很脆弱。

或者,根据依赖性质的详细信息,您可以为您想要的 libcrypto 版本提供一个独特的 SONAME。实际上,就静态和动态链接器而言,这将使其成为不同的对象(例如 libdjcrypto)。但这是有风险的,因为如果您的库对 libcrypto 有直接和间接依赖,或者如果使用您的库的程序通过另一条路径依赖于 libcrypto,那么您最终会在运行时(动态)链接这两个库,并且可能甚至使用两者的函数,具体取决于每个调用的来源。

请注意,如果您也静态链接库,则上述问题应该是您关心的问题。如果这会在您的库中留下对 libcrypto 的任何间接动态依赖,或者在使用您的库的程序中来自其他源的任何动态依赖,那么您最终将同时使用多个版本的 libcrypto。

底线

对于可执行文件,最好的选择是(1)全静态链接或(2)(对于 ELF)RPATH/LD_LIBRARY_PATH/RUNPATH,确保所有组件通过相同的SONAME 需要目标库。我倾向于提供一个设置LD_LIBRARY_PATH 的包装脚本,这样它的作用范围就很窄了。

对于可重复使用的,“不要那样做”可能是最好的选择。同时使用另一个库的两个不同版本(在您的情况下为 libcrypto)最终使用程序的高潜力使得所有可用选项都没有吸引力。当然,除非您可以接受同一个程序使用多个库版本,在这种情况下,静态链接和RPATH / RUNPATH(但不是LD_LIBRARY_PATH)是您最好的替代方案。


*请注意,至少有些版本的libtool 有添加RPATH 条目的习惯,无论您是否要求它们——这是需要注意的。您可能需要修补安装在项目中的 libtool 脚本以避免这种情况。

【讨论】:

  • 很棒的答案。我在找人来解释我的选择范围,你已经非常清楚地做了。谢谢。
  • 我倾向于提供一个设置LD_LIBRARY_PATH 的包装脚本,这样它的作用范围就很窄了。 有趣的是,我喜欢使用RPATHRUNPATH在可执行文件本身中,因为LD_LIBRARY_PATH 将传播到子进程。当然,这意味着我必须将可执行文件与正确的共享对象打包在一起,这也会导致问题。
  • 这是一个有效的考虑,@AndrewHenle。我所说的“使其效果范围很窄”是指相对于在您的默认 shell 配置中使用 LD_LIBRARY_PATH,而不是相对于使用 RPATH 和/或 RUNPATH,后者的范围确实更窄。我显然背叛了我对使用RPATH 和(在较小程度上)RUNPATH 的普遍偏见。但所有这些工具都有其用途和合理的用例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-23
  • 1970-01-01
  • 1970-01-01
  • 2020-04-11
  • 2014-11-22
  • 2016-01-06
  • 2021-05-19
相关资源
最近更新 更多