一文搞懂系列——你真的了解如何生成动态库了吗?

引言

动态库的编译,这有什么难度,这不是手到擒来的事情吗?无非不就是:

复制代码
gcc -FPIC -shared -o libxxx.so  *.o  *.c  

我若是提出这些需求场景,阁下又如何应对呢?

  • 动态库A依赖其他部分提供的能力。但是却不想将内部的能力暴露出去
  • 动态库A依赖外部函数func_xxx,但是该符号即可能存在我们自己的库B中,也可能存在客户动态库C中。如何保证调用指定接口
  • 对外提供的库,如何让客户只能访问指定接口,实现其它接口的隐藏等。

至此你还能面不改色,自信的说:动态库编译简单吗?嘴角颤抖,不屈的低语:就是简单

也有朋友可能就会说:"现实工作中怎么会有这么奇葩的要求?就是你难为人,没事找事"。但是我想说的是,这些场景真的很常见,我在工作中就遇到过,不妨听我细说。

-Wl,--exclude-libs,ALL

工作场景:当今IT行业,一个产品的输出,基本都是有多个部门相互合作,紧密配合才能实现的。而各个部门之间的常见的配合方式就是提供SDK。比如:我之前在海康是做门禁产品的。其中有一个重要功能就是人脸识别。该功能流程可以分解

视频流获取 --> 提取图片帧 --> 人脸识别算法获取唯一ID --> 比对数据库中的ID --> 放行

其中视频流获取 --> 提取图片帧是bsp团队开发,他们提供动态库libB.a,我们调用其中对应接口。人脸识别算法计算唯一ID则是研究院团队提供的算法库libC.a。比对数据库中的ID --> 放行则是我们团队的开发内容。我们对外提供libA.so

很明显我们,我们仅仅是做门禁业务开发,总不能把BSP团队或研究院的核心能力也提供给甲方吧?否则一定要加钱的。

但是我们该怎么做呢?因为我们知道,正常编译静态库libC.so的方式,肯定会将bsp和研究院提供的能力对外开放。客户集成时,是可以直接引用到libA.a和libB.a的对外接口。代码示例如下:

复制代码
//a.c
extern int printf(char* ftm,...);
int a()
{
    printf("i'am liba.so");
    return 0;
}

//b.c
extern int printf(char* ftm,...);
int b()
{
    printf("i'am libb.so");
}

//c.c
extern int a(void);
extern int b(void);

int c()
{
    a();
    b();
    return 0;
}

编译:

复制代码
yihua@ubuntu:~/test/1207$ gcc -c a.c
yihua@ubuntu:~/test/1207$ ar rcs -o libA.a a.o
yihua@ubuntu:~/test/1207$ gcc -c b.c
yihua@ubuntu:~/test/1207$ ar rcs -o libB.a b.o
yihua@ubuntu:~/test/1207$ gcc -FPIC -shared -o libC.so c.c -lA -lB -L.

符号关系:

如图所示,外部是可以通过libC.so去直接调用libA.a和libB.a的接口

如何解决这个问题呢?

链接器为我们提供了 -Wl,--exclude-libs 参数选项:隐藏静态库文件的符号。

我们加上编译选项再试试。

复制代码
yihua@ubuntu:~/test/1207$
yihua@ubuntu:~/test/1207$ gcc -FPIC -shared -o libC.so c.c -L.  -Wl,--exclude-libs,ALL -lA -lB
yihua@ubuntu:~/test/1207$

符号关系:

由图可知,libA.a 和 libB.a的内部符号已经被隐藏了。完结,撒花~~~

-Wl,-Bsymbolic

工作场景:在工作中,我们无法避免的会依赖其它动态库。比如:我司开发的SDK libA.so底层用到了mqtt通信,因此会依赖开源库libpaho-mqtt3c.so,但是我司对内部源码做了一些定制化修改;同时,我们也依赖第三方供应商的SDK libB.so,并且他们内部也采用了mqtt协议通信,同样集成了mqtt开源库,也许他们也在内部做了定制化处理。

关系如下:

分析:

情况一:当admfotaApp运行时,链接器会根据它的动态库依赖关系,加载相应的动态库。而libpaho-mqtt3c.so仅会加载一次。之后再进行符号链接时,就会出现异常。------ 加载库了非预期的库

情况二:会导致mqtt开源库代码段被加载两次,也就是说进程中会有两套相同的符号和对应的代码段。libB.so和libA.so在进行符号链接时,可能就会出现异常。 ------ 符号链接时出现问题

示例代码如下:

复制代码
//a.c
extern int printf(const char* ftm,...);
int a()
{
    printf("i'am OEM a\n");
}

int c()
{
    printf("i'am OEM c\n");
}

//b.c
extern int printf(const char* ftm,...);
extern int c(void);
extern int d(void);
int b()
{
    printf("i'am abup a\n");
    c();
    d();
    return 0;
}

//c.c
extern int printf(const char* ftm,...);
int c()
{
    printf("i'am abup c\n");
}

//d.c
extern int printf(const char* ftm,...);
int d()
{
    printf("i'am abup d\n");
}

//main.c
extern int printf(const char* ftm,...);
extern int a(void);
extern int b(void);
int main()
{
        a();
        b();
        return 0;
}

编译如下:

复制代码
gcc -c c.c
gcc -c d.c
ar -crs -o libC.a c.o d.o   // 生成静态库
gcc -FPIC -shared -o libB.so b.c -lC -L.    //从这可以看出,b.c期望时引用c.c中的c()
gcc -FPIC -shared -o libA.so a.c
gcc main.c -o main -lA -lB -lC -L.

运行:

由上可知:输出内容是非预期的。我们更新一下编译命令gcc main.c -o main -lB -lA -L.,运行结果如下:

链接库的顺序不一样,居然会有不一样的结果?这是为什么呢?有兴趣的朋友可以搜索:全局符号介入 相关知识点。或参考我的一篇博客:全局符号介入引起的问题

针对该场景如何处理呢?

链接器中的-Wl,-Bsymbolic参数:告诉链接器强制使用本地的符号,也就是说,编译libB.so时,就确定符号地址。不需要等待运行时再链接。

比如我们在编译main.c时,增加该参数:

复制代码
gcc -FPIC -shared -o libB.so b.c -lC -L. -Wl,-Bsymbolic
gcc main.c -o main -lA -lB -L.

输出:

完结撒花~~~。

有兴趣的同学可以通过反汇编查看增加-Wl,-Bsymbolic参数前后,libB.so的汇编内容。

总结

本文从两个实际存在的场景,向大家介绍了动态库生成过程中的一些特定需求。简单介绍了 -Wl,-Bsymbolic-Wl,--exclude-libs,ALL两个链接属性及使用方式。

当然很多其它常用的参数比如:-fvisibility=hidden-Wl,--whole-archive。有兴趣的朋友可以了解一下。

若我的内容对您有所帮助,还请关注我的公众号。不定期分享干活,剖析案例,也可以一起讨论分享。

我的宗旨:

踩完您工作中的所有坑并分享给您,让你的工作无bug,人生尽是坦途

相关推荐
Ant?19 分钟前
rk3588 驱动开发(三)第五章 新字符设备驱动实验
数据库·驱动开发
Always_away33 分钟前
数据库系统概论|第三章:关系数据库标准语言SQL—课程笔记6
数据库·笔记·sql·学习
oioihoii1 小时前
C++23文本编码革新:迈向更现代的字符处理
java·数据库·c++23
九鼎科技-Leo1 小时前
写windows服务日志-.net4.5.2-定时修改数据库中某些参数
数据库·windows·.net
hkfkn2 小时前
Sql刷题日志(day6)
数据库·sql
游王子2 小时前
Milvus(9):字符串字段、数字字段
数据库·milvus
猫咪-95272 小时前
Mysql之存储过程(下)
数据库
小白教程3 小时前
解读和分析mysql性能数据时,如何确定性能瓶颈的具体位置?
数据库·mysql·mysql教程·mysql优化教程
LaughingZhu3 小时前
PH热榜 | 2025-04-26
前端·数据库·人工智能·mysql·开源
noravinsc5 小时前
django admin AttributeError: ‘UserResorce‘ object has no attribute ‘ID‘
数据库·django·sqlite