7

假设我有:

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

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

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

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 或任何东西。我希望它在编译时链接到共享对象。

这甚至可能吗?

4

2 回答 2

9

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

问题是您将可执行文件链接到特定于版本的soname (libsomething.so.1libsomething.so.2)。您应该使用未版本化的 sonamelibsomething.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指向/lib/libsomething.so.1机器 A 的符号链接;
  • /lib/libsomething.so指向/lib/libsomething.so.2机器 B 上的符号链接。

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

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

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


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

我使用 Fedora 18X86_64进行测试,并将输出调整i686为清晰。

  • 编译libsomething.so.1libsomething.so.2。确保SONAME设置为 unversioned 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)
    
  • 编译可执行文件并确保它引用相同的 soname,但没有NEEDED.

    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.

于 2013-03-12T19:17:06.993 回答
2

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

而且它不应该无论如何。

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

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

于 2011-01-14T02:12:21.517 回答