C++ 在动态库中隐藏符号而不修改源代码
我有一个封闭源代码的第三方共享库,我需要链接。不幸的是,第三方库的创建者没有限制导出哪些符号以及导出所有符号。第三方库在内部使用了我在代码中使用的流行库的不兼容版本,但导出了冲突符号(google的protobuf库)。当protobuffer库版本检查发现库的编译时版本和运行时版本不兼容时,这会导致运行时错误。我可以通过恢复到与第三方库中使用的版本相匹配的protobufs 2.3的旧版本来解决此问题。但是,protbuf 2.3存在性能问题,使其对我的应用程序不可用。我需要一种在代码中使用Protobuf2.4的方法,并让第三方库使用它自己的内部v2.3 是否有一种方法可以生成新版本的第三方库,该库不从protobuf v 2.3库中导出符号,该库仅在给定so文件的情况下内部使用?如果我有消息来源,这将是一个更容易的问题。似乎像objcopy和strip这样的工具实际上无法修改动态符号表。到目前为止,我唯一的想法是创建我自己的垫片库,通过将调用重定向到第三方库(可能是用dlopen打开的?)只导出我需要的符号C++ 在动态库中隐藏符号而不修改源代码,c++,symbols,dynamic-library,C++,Symbols,Dynamic Library,我有一个封闭源代码的第三方共享库,我需要链接。不幸的是,第三方库的创建者没有限制导出哪些符号以及导出所有符号。第三方库在内部使用了我在代码中使用的流行库的不兼容版本,但导出了冲突符号(google的protobuf库)。当protobuffer库版本检查发现库的编译时版本和运行时版本不兼容时,这会导致运行时错误。我可以通过恢复到与第三方库中使用的版本相匹配的protobufs 2.3的旧版本来解决此问题。但是,protbuf 2.3存在性能问题,使其对我的应用程序不可用。我需要一种在代码中使用P
有更好的解决方案吗?更改传递给链接器的库的顺序可能会有所帮助。如果多个库输出相同的符号,则链接器应使用第一个库中的符号。但是,并不是所有的链接器都是这样的,所以请查看一下它的文档。我找到了一个有效的解决方案。。。我创建了一个垫片库,它将调用重定向到第三方库,允许库外的代码看到protbuf v2.4符号,而第三方库内的代码看到protbuf v2.3符号。此解决方案基于此处发布的想法: 我不得不修改dlopen标志以包括RTLD|u LAZY | RTLD|u LOCAL | RTLD|u DEEPBIND。RTLD_LOCAL标志可防止第三方库中的符号在垫片库外被看到(防止符号泄漏)。RTLD_DEEPBIND强制从第三方库内部调用,以仅查看符号的内部版本(防止符号泄漏) 具体来说,这里是我的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);
}
#包括
#包括
#包括
#包括“libhdfs/hdfs.h”
//#定义打印调试内容
//Helper函数,用于从libMapRClient检索指向函数的函数指针
//将内部使用的符号与外部已链接的符号隔离开来
//解决当前版本libMapRClient的符号冲突问题。
void*GetFunc(常量字符*名称){
#ifdef打印调试内容
printf(“重定向%s\n”,名称);
#恩迪夫
无效*手柄;
字符*错误;
handle=dlopen(“/opt/mapr/lib/libMapRClient.so”,RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND);
如果(!句柄){
fputs(dlerror(),stderr);
出口(1);
}
void*fp=dlsym(句柄、名称);
如果((error=dlerror())!=0){
fprintf(stderr,“%s\n”,错误);
出口(1);
}
返回fp;
}
hdfsFS hdfsConnect(常量字符*主机,tPort端口){
typedef hdfsFS(*FP)(常量字符*主机,tPort端口);
静态FP ext=0;
如果(!ext){
ext=(FP)GetFunc(“hdfsConnect”);
}
返回ext(主机、端口);
}
int hdfsCloseFile(hdfsFS,hdfsFile文件){
typedef int(*FP)(hdfsFS,hdfsFile文件);
静态FP ext=0;
如果(!ext){
ext=(FP)GetFunc(“hdfsCloseFile”);
}
返回ext(fs,文件);
}
。。。等其他公共API函数感谢您的想法。。。我尝试过这个,但效果不太好,因为它强制所有呼叫站点使用相同版本的符号(从版本2.3或2.4)。但是,我需要在第三方库中调用该符号的v2.3,并在我的代码中调用该符号的v2.4。否则,库将检测不匹配的版本并中止。只有当“我的代码”加载库或第三方代码加载库时发生中止时,才能切换库链接顺序。因此,您需要一种部分链接。在第一阶段,将您的代码链接到v2.4,并将第三方库的代码独立链接到v2.3,在第二阶段,将结果链接到一起。也许有一些链接器选项允许第一阶段的不完整链接?你使用哪种操作系统?Linux(fedora,ubuntu)。。。我对价格合理的科学/分布式计算的偏好…;-)垫片是否基本上只是对主应用程序隐藏了dlopen/dlsym的缺点?这似乎没有必要。