生成shim `.so`以重命名现有`.so`的符号

1 人关注

我有一个现有的预建的 .so 共享库(让我们称之为 libjniopenssl.so )。它是由第三方在 Debian Linux 上用 OpenSSL 1.0.1k-3+deb8u4 构建的。 我不得不在 CentOS 上运行 OpenSSL 1.0.2k 包的 libjniopenssl.so 。 尽管 OpenSSL 的版本略有不同,但 libjniopenssl.so 所使用的API在 1.0.1k 1.0.2k 之间没有改变。- 所以我希望它们在我的方案中是源代码和二进制兼容的。 不幸的是,仅仅在 CentOS 上运行 libjniopenssl.so 并不能工作。

替换代码1】通过 System.loadLibrary JVM 加载,但在 CentOS 上运行时却出现如下错误。

Unable to load libjniopenssl: java.lang.UnsatisfiedLinkError: /tmp/jna-112200956/jna6604950569974562639.tmp:
libcrypto.so.1.0.0: cannot open shared object file: No such file or directory

原因很简单,在CentOS上没有这样的文件libcrypto.so.1.0.0,因为OpenSSL 1.0.2k 16.el7只提供以下.so的。

$ ls -l /lib64/libcrypto*
lrwxrwxrwx. 1 root root      19 Jun  5  2018 /lib64/libcrypto.so.10 -> libcrypto.so.1.0.2k
-rwxr-xr-x. 1 root root 2512832 Apr 11  2018 /lib64/libcrypto.so.1.0.2k

由于某些原因 CentOS特意改名替换代码0】的文件名从默认的libcrypto.so.1.0.0改为libcrypto.so.1.0.2k,即使如此,在编译该版本时,从source它使用的名称是libcrypto.so.1.0.0

该错误信息建议尝试创建一个名称为/lib64/libcrypto.so.1.0.0的符号链接,该链接指向/lib64/libcrypto.so.1.0.2k

This resulted in the following error in run-time:

Unable to load libjniopenssl: java.lang.UnsatisfiedLinkError: /tmp/jna-112200956/jna2564265718506275007.tmp: 
/lib64/libcrypto.so.1.0.0: version `OPENSSL_1.0.0' not found (required by /tmp/jna-112200956/jna2564265718506275007.tmp)

检查libjniopenssl.so显示,有以下符号参考了OpenSSL

$ readelf -Ws libjniopenssl.so
Symbol table '.dynsym' contains 40 entries:
Num:    Value          Size Type    Bind   Vis      Ndx Name
    0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
    1: 0000000000000e98     0 SECTION LOCAL  DEFAULT    9
    2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
    4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND EVP_CIPHER_CTX_init@OPENSSL_1.0.0 (2)
    5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND malloc@GLIBC_2.2.5 (3)
    6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND OPENSSL_add_all_algorithms_noconf@OPENSSL_1.0.0 (2)
    7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND HMAC_CTX_cleanup@OPENSSL_1.0.0 (2)
    8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND EVP_CipherUpdate@OPENSSL_1.0.0 (2)
    9: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTable
    10: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND ERR_load_crypto_strings@OPENSSL_1.0.0 (2)
    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND free@GLIBC_2.2.5 (3)
    12: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    13: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (3)
    14: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND HMAC_CTX_init@OPENSSL_1.0.0 (2)
    15: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND HMAC_Final@OPENSSL_1.0.0 (2)
    16: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND EVP_sha1@OPENSSL_1.0.0 (2)
    17: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND HMAC_Init_ex@OPENSSL_1.0.0 (2)
    18: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND HMAC_Update@OPENSSL_1.0.0 (2)
    19: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND EVP_CIPHER_CTX_cleanup@OPENSSL_1.0.0 (2)
    20: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND EVP_MD_size@OPENSSL_1.0.0 (2)
    21: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND EVP_aes_128_ctr@OPENSSL_1.0.1 (4)

libcrypto.so.1.0.2kCentOS上有不同后缀的符号,例如。

readelf -Ws /lib64/libcrypto.so.1.0.2k | grep -iE "EVP_CIPHER_CTX_init"
705: 000000000012a6b0   151 FUNC    GLOBAL DEFAULT   13 EVP_CIPHER_CTX_init@@libcrypto.so.10

所以,在CentOS上,后缀是@@libcrypto.so.10,但libjniopenssl.so是针对共享库链接的,而共享库里有后缀@OPENSSL_1.0.0的符号。这可能是为什么只是添加符号链接不起作用。

进一步调查显示,Debian版本的OpenSSL 1.0.1k包括许多补丁, including one which 增加了符号版本管理.它只在OpenSSL 1.1.0中被引入,但被提供Debian软件包的人移植到1.0.1

相反,【替换代码5替换代码6】包不包括那个用于符号版本的补丁(但它们包括来自1.1.0的、符合.so中的符号的其他补丁)。

我可以在CentOS上针对它的OpenSSL的副本重新构建libjniopenssl.so,但这实际上涉及到维护(构建。存储、部署)自己的libjniopenssl.so的副本,而这个副本实际上来自第三方的git仓库。我不喜欢重建方案,所以我在寻找更优雅的方案。但我不得不说re-buildingCentOS论坛上的推荐。

我能想到的一个解决方案是在构建或部署时生成一个特殊的 shim libcrypto.so.1.0.0(基于目标系统提供的libcrypto的副本),它按照libjniopenssl.so的要求重命名了符号。但在引擎盖下,它将翻译所有对原始系统提供的libcrypto.so.1.0.2k的调用。

总的来说,我在寻找一些工具或工具集,能够从指定的 "实现".so中自动生成这样的 "shim/adapter/proxy".so,但能够重新定义一些或所有符号。 我发现有objcopy的工具有--redefine-symbol old=new,但它并不完全能做到我想要的。我不想从原始的.so中复制任何代码,我想翻译对原始 "实现 "的.so的调用。

UPD:通过运行本地实验,发现objcopy不支持动态库中的符号重命名。从这个问题可以看出邮件主题.

综上所述,我的问题是。

  • Is there an existing util capable to generate such shared shim library for a specified .so file?
  • Or maybe there is a better way to solve this incompatibility between CentOS and Debian OpenSSL packages, assuming I don't want to modify neither third-party provided libjniopenssl.so nor OS provided copy of OpenSSL?
  • 3 个评论
    Vi.
    蝶变的OpenSSL和CentOS的OpenSSL似乎是二进制不兼容的。通过重命名符号来强制链接会导致运行时的错误。
    他们建议重建它是有原因的。这是一个合理的建议,无论你是否想做,你都应该遵循。如果它们不兼容,不要指望任何工具能 "解决 "这个问题。在编程中记住这一点。 虚有其表不代表实有其事 .有一个很大的区别。不相容性就是不相容性,无论你多么不喜欢它。简而言之,答案是你走错了路,这就是它的方式。
    yugr
    @Vi. 这是真的,但在实践中,简单的符号链接往往就足够了,因为应用程序不会调用ABI中不兼容的子集。我知道这样做有风险,不正确,等等,但这仍然是一种常见的做法。
    java
    linux
    openssl
    shared-libraries
    mstyura
    mstyura
    发布于 2019-01-03
    1 个回答
    yugr
    yugr
    发布于 2021-08-22
    已采纳
    0 人赞同

    Implib.so 能够为任意的.so库生成shim包装器。运行

    $ implib-gen.py --dlopen-callback=mycallback libcrypto.so.1.0.0
    

    将生成libcrypto.so.1.0.0.tramp.Slibcrypto.so.1.0.0.init.c,它们将在libcrypto.so.1.0.0中的所有符号上实现无版本包装器。你还需要一个小的加载器来实现操作系统的特定实现(也就是libcrypto.so.1.0.2k)。

    $ cat mycallback.c
    #define _GNU_SOURCE
    #include <dlfcn.h>
    #include <stdio.h>
    #include <stdlib.h>
    #ifdef __cplusplus
    extern "C"
    #endif
    // Load OS-specific implementation underneath
    void *mycallback(const char *lib_name) {
      void *h = dlmopen(LM_ID_NEWLM, "libcrypto.so.1.0.2k", RTLD_LAZY | RTLD_DEEPBIND);
      if (h)
        return h;