关于linux系系统的动态库全局函数重名问题

最近在使用某几个知名厂商的对象存储C SDK时,发现由于这些SDK的内部实现其实基本是一样的,进而导致了各个SDK的so中都或多或少有些被大家共同喜爱的全局函数(符号)名称,最后引发了在部分环境中出现了调用A厂商SDK的上传函数时崩溃在了B厂商SDK的同名全局函数中的问题,最开始感觉很是莫名其妙,后来查了资料发现,首先gcc编译时默认会将全局变量、函数等符号导出在都动态库中,而同时全局函数的加载又是抢占式的,这点通过如下简单试验便可验证:

lib1.c

#include "stdio.h"

void common()
{
    printf("common in lib1!\n");
}

void lib1_fn()
{
    printf("lib1_fn!\n");
    common();
}

lib2.c

#include "stdio.h"

void common()
{
    printf("common in lib2!\n");
}

void lib2_fn()
{
    printf("lib2_fn!\n");
    common();
}

app.c

#include <stdio.h>

int main()
{
    printf("hello world!\n");
    lib1_fn();
    lib2_fn();
    return 0;
}

有如上3个简单的c源码文件,两个库源码,一个执行程序源码,接下来执行编译:

gcc -fPIC -shared lib1.c -o lib1.so

gcc -fPIC -shared lib2.c -o lib2.so

gcc app.c lib1.so lib2.so -o app.out

此时执行app.out:“LD_LIBRARY_PATH=./ ./app.out“,会得到如下输出结果:

hello world!
lib1_fn!
common in lib1!
lib2_fn!
common in lib1!

可以看到,重名全局函数common执行的都是lib1中的版本,那么如果把最后编译app的gcc参数换成这样:“gcc app.c lib2.so lib1.so -o app.out”,然后再次执行app.out,便会得到这样的输出:

hello world!
lib1_fn!
common in lib2!
lib2_fn!
common in lib2!

这次lib2的common抢占了先机,那么如何让common各用各的呢?答案就是“Wl,-Bsymbolic”这个参数:

gcc -fPIC -shared -Wl,-Bsymbolic lib1.c -o lib1.so

gcc -fPIC -shared -Wl,-Bsymbolic lib2.c -o lib2.so

gcc app.c lib1.so lib2.so -o app.out

这样,再次执行app.out,得到的结果就是使用每个库自己的common的结果了:

hello world!
lib1_fn!
common in lib1!
lib2_fn!
common in lib2!

这样构建出来的lib1.so lib2.so无论app先链接哪个,都不会出现重名全局函数common的抢占误用问题了,不过这种处理方式具有一定的风险副作用,具体可以参考下面这篇文章,其中也提到了别的解决方法,可以学习下:

Option -Bsymbolic 会导致严重副作用 https://blog.csdn.net/weixin_41964962/article/details/107209950

博主友情提示:

如您在评论中需要提及如QQ号、电子邮件地址或其他隐私敏感信息,欢迎使用>>博主专用加密工具v3<<处理后发布,原文只有博主可以看到。