如何解决C程序中不同静态库之间的符号冲突问题

在将helix player移植到iOS平台时,由于iOS不允许动态库,需将库编译为静态库,导致符号冲突。文章介绍了静态库与动态库的区别,并提供了一种使用perl脚本自动修改源码,避免同名函数冲突的方法,从而成功解决链接问题。
摘要由CSDN通过智能技术生成

之前在将helix player移植到ios平台时遇到过这个问题,现在整理一下,给自己做个总结,也希望能对别人有所帮助。

问题的描述: 

如果helix在ffmpeg之前是一个小有名气的开源的播放引擎,由Realnetworks维护,像nokia的塞班系统上都用的是这个播放引擎,而且现在国内的一些手机上还有它的影子。helix将各个功能模块以动态库的形式进行组织管理。当播放一个视频时,比如rmvb,它会解析fileformat,然后知道要加载哪个模块来解析这个文件; decode时,也是根据codec的fourcc来判断加载哪个decoder库。

这种组织方式确实有一些好处,比如各个模块相对独立,每个模块只有在用到时才会被加载,用完后一段时间内不再使用的话会自动卸载。但是由于一些基础的静态库会被每个动态库都包含,所以整个程序所占的内存减小了,但占的磁盘增加了。

Helix在android平台上工作得很好,但是当我们想要把它移植到iOS平台时,却遇到了一个大麻烦,因为iOS不允许APP中包含动态库,否则app审查通不过。没办法,只得将这些程序编译成静态库。 这样基础库只有一份了,能够节省一些磁盘空间。

在这个过程中我们遇到的一个问题是: 有几个格式相同但版本不同的编解码器(codec),它们是用c语言写的,它们的代码结构和文件名相同,而且很多函数名都一样。由于c语言的函数编译之后没有mangling,导致最终程序中会有多个同名的函数,这在链接的时候能通过(gcc会选择所遇到第一个同名函数,所以这些同名函数就不能保证哪个会先被链接进可执行程序),但是在执行的时候就会出现各种奇怪的现象,严重的甚至crash。

这里需要理解静态库方式和动态库方式的区别,动态库方式时不会有问题(这里指的是dlopen这种显式的方式,如果是隐式的方式,即将动态库和可执行程序一起链接的话,也会存在和静态库一样的问题),因为动态库本身就是一个独立的实体,它有自己的代码段和数据段,它里面的函数都是相对于它加载时的起始地址来定位的,加载地址在加载前是不确定的,但加载后就是确定的了。从某种意义上说,动态库有点类似于可执行程序。而静态库方式在链接时必须先找到所依赖的函数,然后确定它的偏移地址,这个地址相对于可执行程序的起始地址是固定的,这个地址是链接时确定的。


解决方法:

假设我们有libA和libB两个静态库,他们有很多同名函数,并且这两个库通常是在两个不同的目录下dir1和dir2。

由于我们有源码,所以可以通过修改源码的方式来区分不同的codec版本。如果是到源文件中一行一行地找,那样将会是一个噩梦。

幸运的是,我们有perl这样的脚本语言,它的正则表达式功能会让你感到游刃有余。对了,写一个perl脚本,自动地完成这个任务吧。

比如dir1/f1.c和dir2/f2.c都包含了func这个函数,那么我们可以在dir1/f1.c的开头加上:#define func LIBA_func, 在dir2/f2.c的开头加上:#define func LIBB_func。

在用到func的文件中也如法炮制,比如dir1/f3.c用到了dir1/f1.c中的func,则在dir1/f3.c开头加上:#define func LIBA_func

dir2/f4.c用到了dir2/f2.c中的func,则在dir2/f4.c开头加上:#define func LIBB_func

再重新编译这两个静态库,链接就OK了。


以下是具体步骤:

(1)先分别编译这两个静态库,得到libA.a和libB.a;

(2)用nm命令将他们的符号表给打印出来,保存到两个文件中;

(3)解析这两个含有符号表的文件,找到同时存在这两个文件中的那些符号以及它们所在文件的文件名;

(4)第三步得到了符号->文件名的映射关系,我们将其转换成文件名->符号的映射关系;

(5)对每个文件,它们应该同时存在于libA和libB所对应的两个目录下。我们对这个文件中的每个符号,像上面那样加上宏定义。

(6)再重新编译两个库就行了。


例子:

设我们在test目录下有两个库lib1lib2,这两个库中都有一个duplicate_func函数。

如果我们将这两个库编译成动态库,再在main.cpp中调用之,显示的结果是正确的:

可以参考test目录下build.sh来编译:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值