c - Linux,C - 是否可以链接到具有相同函数名称的 2 个动态库
问题描述
我被迫将同一个第三方动态库(Linux .so、C 语言)的两个版本链接到同一个可执行文件中,以在同一个进程中支持新旧功能。拥有两个可执行文件或远程服务是不可取的。
我假设这一定是一项可行的任务。我尝试尝试创建 2 个代理动态库的天真方法,每个代理动态库都链接到一个真实库并重命名函数。不幸的是,这次尝试失败了——两个新函数都调用了同一个目标函数。我仍然想相信问题在于我缺乏知识,因为有很多编译器和链接器(gcc 和 ld)选项。
我将不胜感激。我也期待使用 dlopen/dlsym,但首先要检查原始方法是否可行。
这是示例代码
/* ./old/b.c */
#include <stdio.h>
int b (int i)
{
printf("module OLD %d\n",i);
return 0;
}
/* ./old/Makefile */
libold.so: b.c
gcc -c -g b.c
gcc -shared b.o -o $@
/* ./new/b.c */
#include <stdio.h>
int b (int i)
{
printf("module new %d\n",i);
return 0;
}
/* ./new/Makefile */
libnew.so: b.c
gcc -c -g b.c
gcc -shared b.o -o $@
/* ./a1.c */
#include <stdio.h>
int b(int);
void call_new(void)
{
printf("will call new 1\n");
b(1);
printf("called new 1\n");
}
/* ./a2.c */
#include <stdio.h>
int b(int);
void call_old(void)
{
printf("will call old 2\n");
b(2);
printf("called old 2\n");
}
/* ./main.c */
#include <stdio.h>
int call_new(void);
int call_old(void);
int main()
{
call_new();
call_old();
return 0;
}
/* ./Makefile */
.PHONY: DEPSNEW DEPSOLD clean
main: liba1.so liba2.so main.c
gcc -c main.c
gcc -o main main.o -rdynamic -Wl,-rpath=new -Wl,-rpath=old -L . -la1 -la2
DEPSNEW:
make -C new
DEPSOLD:
make -C old
liba1.so: DEPSNEW a1.c
gcc -c -fpic a1.c
gcc -shared a1.o -L new -lnew -o liba1.so
liba2.so: DEPSOLD a2.c
gcc -c -fpic a2.c
gcc -shared a2.o -L old -lold -o liba2.so
clean:
find -name "*.so" -o -name "*.o" -o -name main | xargs -r rm
/* ./run.sh */
#/bin/sh
LD_LIBRARY_PATH=new:old:. main
结果不是我想要的——“新”库中的函数被调用了两次
will call new 1
module new 1
called new 1
will call old 2
module new 2
called old 2
解决方案
在这种情况下,您无法控制动态库的自动加载,以确保将为依赖库加载哪个库。您可以做的是将其中一个库(新库)用于动态链接器并手动链接第二个库,如下所示:
添加函数以动态加载和链接库中的函数。
a2.c
#include <stdio.h>
#include <dlfcn.h>
static int (*old_b)(int);
void init_old(void) {
void* lib=dlopen("./old/libold.so", RTLD_LOCAL | RTLD_LAZY);
old_b=dlsym(lib,"b");
}
void call_old(void)
{
printf("will call old 2\n");
old_b(2);
printf("called old 2\n");
}
调用初始化函数
主程序
#include <stdio.h>
void init_old(void);
int call_new(void);
int call_old(void);
int main()
{
init_old();
call_new();
call_old();
return 0;
}
修改链接器选项以添加动态加载库-ldl
liba2.so: DEPSOLD a2.c
gcc -c -fpic a2.c
gcc -shared a2.o -L old -lold -ldl -o liba2.so
本次修改后
~$ ./run.sh
will call new 1
module new 1
called new 1
will call old 2
module OLD 2
called old 2
推荐阅读
- amazon-web-services - Lambda 的 SNS 触发器问题
- python - 使用python进行图像提取
- loops - 创建循环并将输出保存在 .txt 或 .fasta 文件中
- javascript - 当悬停在列表上以显示地图传单上的标记时,在两个 js 事件之间感到困惑
- php - 多维数组在PHP中组合具有相同索引号的值
- angular - ion-datetime 月份和日期名称不能全局定义
- clickhouse - 由于 CANNOT_OPEN_FILE,无法在 MacOS 中安装 ClickHouse
- google-bigquery - UNNEST event_params 后表名缺失数据集
- ubuntu - 建立连接中止后,Ubuntu 上的 x11vnc 服务器超时
- flutter - 如何从不处理流并始终在颤动中听它