Linux, C - 是否可以链接两个具有相同函数名称的动态库

问题描述

我被迫将同一个第三方动态库(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");
}

调用初始化函数

ma​​in.c

#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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...