【问题标题】:Hiding symbols in a dynamic library without modifying source code在动态库中隐藏符号而不修改源代码
【发布时间】:2012-04-18 18:03:34
【问题描述】:

我有一个需要链接的闭源第 3 方共享库。不幸的是,第 3 方库的创建者并没有费心限制导出哪些符号并导出所有符号。第 3 方库在内部使用了我在代码中使用的流行库的不兼容版本,但导出了冲突的符号(google 的 protobuf 库)。当 protobuffer 库版本检查发现库的编译时和运行时版本不兼容时,这会导致运行时错误。我可以通过恢复到与第 3 方库中使用的版本相匹配的旧版本 protobufs 2.3 来解决问题。但是,protbuf 2.3 存在性能问题,导致我的应用程序无法使用它。我需要一种在我的代码中使用 protobuf 2.4 并让第 3 方库使用它自己的内部 v 2.3 的方法。

有没有办法生成一个新版本的 3rd 方库,它不会从仅给定 so 文件的内部使用的 protobuf v 2.3 库中导出符号?如果我有来源,那将是一个更容易的问题。像objcopy和strip这样的工具似乎并不能真正修改动态符号表。到目前为止,我唯一的想法是创建自己的 shim 库,通过将调用重定向到第 3 方库(也许用 dlopen 打开?),只导出我需要的符号。

有没有更好的解决方案?

【问题讨论】:

  • 您使用哪种操作系统?
  • Linux (fedora, ubuntu)...我更喜欢负担得起的科学/分布式计算... ;-)

标签: c++ symbols dynamic-library


【解决方案1】:

我找到了一个可行的解决方案...我创建了一个 shim 库,它将调用重定向到第三方库,允许库外的代码看到 protbuf v2.4 符号,而第三方库内的代码看到 protobuf v2。 3个符号。此解决方法基于此处发布的想法:http://www.linuxjournal.com/article/7795

我不得不修改 dlopen 标志以包含 RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND。 RTLD_LOCAL 标志防止第三方库中的符号在 shim 库之外被看到(防止符号泄漏)。 RTLD_DEEPBIND 强制来自 3rd 方库内部的调用仅查看符号的内部版本(防止符号泄漏)。

具体来说,这是我的 shim 库的示例摘录。

#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
#include "libhdfs/hdfs.h"

//#define PRINT_DEBUG_STUFF

// Helper function to retrieve a function pointer to a function from libMapRClient
// while isolating the symbols used internally from those already linked externaly
// to workaround symbol collision problem with the current version of libMapRClient.
void* GetFunc(const char* name){
  #ifdef PRINT_DEBUG_STUFF
    printf("redirecting %s\n", name);
  #endif
  void *handle;
  char *error;

  handle = dlopen("/opt/mapr/lib/libMapRClient.so", RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND);

  if (!handle) {
    fputs(dlerror(), stderr);
    exit(1);
  }
  void* fp = dlsym(handle, name);
  if ((error = dlerror()) != 0) {
    fprintf(stderr, "%s\n", error);
    exit(1);
  }
  return fp;
}


hdfsFS hdfsConnect(const char* host, tPort port) {
  typedef hdfsFS (*FP) (const char* host, tPort port);
  static FP ext = 0;
  if (!ext) {
    ext = (FP)GetFunc("hdfsConnect");
  }
  return ext(host, port);
}


int hdfsCloseFile(hdfsFS fs, hdfsFile file) {
  typedef int (*FP) (hdfsFS fs, hdfsFile file);
  static FP ext = 0;
  if (!ext) {
    ext = (FP)GetFunc("hdfsCloseFile");
  }
  return ext(fs, file);
}

...对于其他公共 API 函数以此类推

【讨论】:

  • 不是 shim 基本上只是从主应用程序中隐藏了 dlopen / dlsym 的讨厌吗?似乎没有必要。
【解决方案2】:

更改传递给链接器的库的顺序可能会有所帮助。如果多个库导出相同的符号,则链接器应使用第一个库中的符号。但是,并非所有链接器都符合此要求,因此请查看其文档。

【讨论】:

  • 感谢这个想法...我试过了,但它并不完全有效,因为它强制所有呼叫站点使用相同版本的符号(来自版本 2.3 或 2.4)。但是,我需要在第 3 方库中调用符号的 v2.3,并在我的代码中调用符号的 v2.4。否则,库会检测到不匹配的版本并中止。仅当我的代码加载库或第 3 方代码加载库时发生中止时,切换库链接顺序才会更改。
  • 所以你需要一种部分链接。在第一阶段,将您的代码链接到 v2.4,并独立地将第 3 方库的代码链接到 v2.3,然后在第二阶段将结果链接在一起。也许有链接器选项允许第一阶段的这种不完整链接?
猜你喜欢
  • 2012-05-30
  • 1970-01-01
  • 2011-10-07
  • 2021-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多