假设我有:

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

  • 两台机器都有指向各自库的 /usr/lib/libsomething.so 符号链接(symbolic link)。

    如果我使用gcc-lsomething(甚至是/usr/lib/libsomething.so)进行链接,它将遵循符号链接(symbolic link),并且机器A上的ldd会生成类似以下内容的代码:
    libsomething.so.1 => /usr/lib/libsomething.so.1
    

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

    现在,我知道这些是主要的版本号更改,并且我知道它们可能不兼容,但是我愿意冒险。我想告诉链接器的是寻找 libsomething.so ,不要遵循符号链接(symbolic link),所以 ldd 会显示
    libsomething.so => /usr/lib/libsomething.so.1
    

    在 A 但是
    libsomething.so => /usr/lib/libsomething.so.2
    

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

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

    这有可能吗?

    最佳答案

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

    问题是您将可执行文件链接到特定于版本的 soname( libsomething.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 ,您就会:
  • 符号链接(symbolic link) /lib/libsomething.so 指向机器 A 上的 /lib/libsomething.so.1
  • 符号链接(symbolic link) /lib/libsomething.so 指向机器 B 上的 /lib/libsomething.so.2

  • 加载器(运行 ldd )将选择未版本化的符号链接(symbolic link),无论它指向何处:
  • libsomething.so => /lib/libsomething.so (0xNNNNNNNN) 在机器 A 上;
  • libsomething.so => /lib/libsomething.so (0xNNNNNNNN) 在机器 B.

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

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

    为了清晰起见,我使用 Fedora 18 X86_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)
    
  • 编译可执行文件并确保它在 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 构建的,没有版本。这使加载程序在目标计算机上采用未版本控制的符号链接(symbolic link)。并且根据机器,符号链接(symbolic link)可以指向库 libsomething.so.1libsomething.so.2 的不同实现。
  • 关于GCC 链接到共享对象的链接器名称,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4685493/

    10-13 01:18