【问题标题】:Rewrite shared library undef symbol version of compiled object重写编译对象的共享库 undef 符号版本
【发布时间】:2014-01-09 10:55:17
【问题描述】:

我正在尝试编译和链接一个与共享库(在本例中为 libcryto & libssl,但实际库不相关)链接的程序(我们称之为 myprog)。我在 Centos 5.5 上构建它,但希望相同的二进制文件能够在其他类似 RHEL 的发行版(例如 CloudLinux)上运行。两者都有相同库的版本和相同的SO_NAME(即DT_NEEDED版本对应)。

我的问题是这个。当我在 Centos 上编译时,我看到:

centos$ objdump -T myprog | fgrep SSL_new
0000000000000000      DF *UND*  0000000000000000  libssl.so.10 SSL_new

效果很好,因为:

centos$ objdump -T /usr/lib64/libssl.so.10 | fgrep SSL_new
00000000000447d0 g    DF .text  0000000000000390  libssl.so.10 SSL_new

但是,在 CloudLinux 上:

cloudlinux$ objdump -T /usr/lib64/libssl.so.10 | fgrep SSL_new
00000033a623a120 g    DF .text  0000000000000390  Base        SSL_new

请注意,SSL 符号的版本已从 libssl.so.10 更改为 Base

这意味着当我运行二进制文件时,我得到:

cloudlinux$ ./myprog
./myprog: /usr/lib64/libcrypto.so.10: no version information available (required by ./myprog)
./myprog: /usr/lib64/libssl.so.10: no version information available (required by ./myprog)

我知道这“只是一个警告”,但我想摆脱它。

我想要的是让我的二进制文件在 cloudlinux 和 centos 上运行而不会出现警告。据我所知,openssl 库的 ABI 至少在我使用的位上是相同的。现在我无法更改共享库,因为 myprog 旨在在任何 cloudlinux 或 centos 库上运行。我会接受一个在安装时修复符号(以某种方式)的解决方案,例如objcopy

一种方法是为SSL_new@libssl.so.10 引入一个弱符号,它是SSL_new@Base 的别名。显然,这将适用于 ssl 库中的每个符号(因此包装器不合适)。但我无法让-Wl,--defsym=x=y 获取xy 的值,其中包含@ 符号。

另一种可行的方法是简单地从二进制文件中删除符号版本信息(即从每个 ssl 符号中删除 libssl.so.10 版本,并依赖 DT_NEEDED 名称中的版本信息)。这似乎是一个简单的请求,但我也不知道该怎么做。有没有objcopy的咒语?

更糟糕的情况是,在 Centos 上构建一个可以在 CloudLinux 上运行的(单独的)版本就可以了。我尝试这样做:

#define OSLB(SYMNAME) __asm__(".symver " #SYMNAME "," #SYMNAME "@Base");
OSLB (SSL_new);

但是,链接抱怨 SSL_new@Base 是一个未定义的符号(它在 Centos 上是这样,因为 Centos .so 没有那个符号)。

有没有办法解决这个问题?

【问题讨论】:

    标签: linux linker libc binutils objcopy


    【解决方案1】:

    另一种可行的方法是简单地从二进制文件中删除符号版本信息(即从每个 ssl 符号中删除 libssl.so.10 版本,并依赖 DT_NEEDED 名称中的版本信息)。这似乎是一个简单的请求,但我也不知道该怎么做。有什么 objcopy 咒语吗?

    删除链接后的版本信息是不可行的——这些字符串被输入到哈希表中,重建这些哈希表对于objcopy来说太复杂了。

    您要做的是创建一个存根libssl.so.10,并将DT_SONAME 设置为libssl.so.10,它定义了您需要的所有符号,但没有 任何版本信息。例如,如果您只需要 SSL_new 符号,就可以这样做:

     echo "void SSL_new() { }" > t.c &&
     gcc -fPIC -shared -o libssl.so -Wl,--soname='libssl.so.10' t.c
    

    现在将您的程序链接到这个存根库,它将不需要需要任何版本化符号。

    然后,运行时链接器将在 CentOS 上用 SSL_new@libssl.so.10 和在 CloudLinux 上用 SSL_new@Base 来满足这个未定义的未版本化 SSL_new

    【讨论】:

    • 这似乎是前进的方向。我必须通过 .o 文件构建共享库才能使其工作:gcc -c -Wall -Werror -fPIC -o dummy.o dummy.c && gcc -shared -o dummy.so -Wl,--soname='libssl.so.1.0.0' dummy.o && gcc -o objtest objtest.c -l:./dummy.so。这种方法的优点是 (a) 生成的二进制文件可以在任何地方工作,因为它会生成未版本化的符号,(b) 你不需要担心函数原型,(c) C 文件 dummy.c 可以自动生成来自 .so 文件的 objdump -T 的小 perl。
    • @abligh “我必须通过 .o 文件构建共享库才能使其工作”——它应该在没有拆分编译/链接步骤的情况下也能正常工作。我很好奇为什么没有。
    • 不知道,但是结果上的 objdump 产生了“不是动态二进制文件”或一些类似的错误。
    【解决方案2】:

    我能够通过这种方式摆脱这些警告(使用 Debian/Arch 而不是 CentOS/Cloud Linux,但它也应该对你有用):

    1. 删除所有 libssl-devel 包以避免混淆

    2. 从源代码编译 libssl

      ./config --prefix=/usr/local --openssldir=/usr/local/openssl shared

    3. 据此构建您的软件。

    对我来说,这些版本参考然后消失了,它运行时没有警告:

    Version References:
      required from libcrypto.so.1.0.0:
        0x066a2b20 0x00 10 OPENSSL_1.0.0
      required from libssl.so.1.0.0:
        0x066a2b20 0x00 05 OPENSSL_1.0.0
    

    【讨论】:

    • 不幸的是,这远非理想,因为我正在尝试自动构建它,所以我有一个可以在两个系统上运行的二进制文件(或者更糟糕的情况是 2 个二进制文件可以在每个系统中运行)。如果我这样做,我需要删除 -devel 包并在构建之间重新插入另一个包,并且仍然有两个二进制文件。
    • 我不明白你的意思。以上仅在构建机器上需要。你应该最终得到一个在两个真实系统上运行的二进制文件。
    • 我认为你看错了。您在帖子中的版本参考是 DT_NEEDED 版本,这已经是正确的。我的问题是每个符号的版本控制。我所说的是每个符号的版本控制将在您刚刚构建的 libssl-devel 包的 ld 行控制的版本映射中设置。尝试 objdump -T 就可以了。我想我可以尝试下载 libssl-devel 并对其进行修改,使其没有符号版本控制,用它构建并查看是否有帮助,但这似乎很重要,可以从 ELF 文件中删除一个标签。
    • 是的,我也想找到更好的方法,但这对我有用。在 openssl 中禁用符号应该很容易,因为上游默认不包含它们(所以我猜它是由 SRPM 中的补丁打开的)。
    猜你喜欢
    • 1970-01-01
    • 2016-09-26
    • 1970-01-01
    • 2018-07-05
    • 1970-01-01
    • 2013-04-16
    • 1970-01-01
    • 1970-01-01
    • 2016-09-27
    相关资源
    最近更新 更多