【问题标题】:GCC linking to a shared object's linker nameGCC 链接到共享对象的链接器名称
【发布时间】:2011-06-08 19:13:42
【问题描述】:

假设我有:

  • /usr/lib/libsomething.so.1 在机器 A 上;
  • /usr/lib/libsomething.so.2 在机器 B 上。

两台机器都有/usr/lib/libsomething.so 符号链接到它们各自的库。

如果我使用gcc-lsomething(甚至/usr/lib/libsomething.so)链接,它将遵循符号链接,并且机器A 上的ldd 会产生类似的内容:

libsomething.so.1 => /usr/lib/libsomething.so.1

这意味着它将无法在机器 B 上找到库。

现在我知道这些是主要的版本号更改,并且我知道它们可能不兼容,但我愿意承担这个风险。我想告诉链接器的是寻找libsomething.so,不要跟随符号链接,所以ldd会显示

libsomething.so => /usr/lib/libsomething.so.1

在 A 但是

libsomething.so => /usr/lib/libsomething.so.2

在 B 上。然后加载程序将跟随符号链接到那里的任何版本。

另外,我不希望延迟加载 dlopen 或任何东西。我希望它在编译时链接到共享对象。

这可能吗?

【问题讨论】:

    标签: gcc linker ld shared-objects


    【解决方案1】:

    当然,可以使用任何可用版本的共享库制作可执行文件。

    问题在于您将可执行文件链接到特定版本的sonamelibsomething.so.1libsomething.so.2)。您应该使用未版本化的 soname libsomething.so 来代替。

    为了实现这一点,您应该在构建机器上编译和安装 soname (ELF SONAME) 等于 libsomething.so(无版本)的库,以便链接器可以在构建可执行文件时选择此 soname。

    根据Shared Libraries HOWTO,您可以在构建库时传递所需的未版本化soname

    gcc -shared -Wl,-soname,libsomething.so -o libsomething.so.X objectsomething.o
    

    然后,一旦您安装库并运行ldconfig,您就有:

    • 符号链接/lib/libsomething.so指向机器A上的/lib/libsomething.so.1
    • 符号链接 /lib/libsomething.so 指向机器 B 上的 /lib/libsomething.so.2

    加载程序(运行ldd)将选择未版本化的符号链接,无论它指向何处:

    • libsomething.so => /lib/libsomething.so (0xNNNNNNNN) 在机器 A 上;
    • libsomething.so => /lib/libsomething.so (0xNNNNNNNN) 在机器 B 上。

    Linux 动态加载程序 (ld.so) 根据写入可执行文件 (ELF NEEDED) 中的 soname 值解析库。该值是在构建可执行文件时从库文件 (ELF SONAME) 复制的。只要目标系统上存在与可执行文件中记录的 soname 匹配的符号链接,就会加载该符号链接指向的库。


    让我们运行您的设置并显示用于验证假设的命令。

    为了清楚起见,我使用 Fedora 18 X86_64 进行测试并将输出调整为 i686

    • 编译libsomething.so.1libsomething.so.2。确保将SONAME 设置为未版本化的libsomething.so

      readelf -a libsomething.so.1 | grep SONAME
      0xNNNNNNNN (SONAME)             Library soname: [libsomething.so]
      
      readelf -a libsomething.so.2 | grep SONAME
      0xNNNNNNNN (SONAME)             Library soname: [libsomething.so]
      
    • 将库安装到各自机器的/lib/ 目录下。在两台机器上运行ldconfig -v 并验证输出。

      ldconfig -v 2>&1 | grep something
      libsomething.so -> libsomething.so.1 (changed)
      
      ldconfig -v 2>&1 | grep something
      libsomething.so -> libsomething.so.2 (changed)
      
    • 编译可执行文件并确保它在NEEDED 中引用相同的soname,但没有版本。

      readelf -a executable | grep NEEDED
      0xNNNNNNNN (NEEDED)             Shared library: [libsomething.so]
      
    • 您的可执行文件现在依赖于未版本化的libsomething.so。将可执行文件复制到两台机器上并针对两台副本运行ldd

      ldd executable
      libsomething.so => /lib/libsomething.so (0xNNNNNNNN)
      

      最后一个输出在两台机器上是相同的,因为可执行文件是用 soname 构建的,没有版本。这使得加载程序在目标机器上采用未版本化的符号链接。根据机器的不同,符号链接可以指向库libsomething.so.1libsomething.so.2的不同实现。

    【讨论】:

    • 我不是在构建有问题的库,我认为真正的答案是不要按照我的建议去做,如果你真的想作弊,只需使用符号链接。不过,这提供了一些很好的信息,所以我接受了。
    【解决方案2】:

    这意味着它将无法在机器 B 上找到库。

    无论如何也不应该这样。

    根据soversions 的定义,libsomething.so.2 表示 API/ABI 与libsomething.so.1 不兼容。因此,仅仅在程序要加载的库表中添加libsomething.so实际上是错误的。 libsomething.so 符号链接仅作为 ld 提示默认选择哪个版本。

    无论文件 ld 实际上最终打开什么,它都会在程序中使用 DTNAME/SONAME 字段进行编码。如果您不希望这样,请不要为 libsomething 配备 soname。但它很容易变得痛苦……首先是在尝试运行程序时遇到不可用的符号。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-09
      • 2010-10-18
      • 2021-07-01
      • 1970-01-01
      相关资源
      最近更新 更多