关于duplicate symbol的思考
iOS 开发中经常会遇到duplicate symbol这个问题,在编译链接的时候就会出现。相信有经验的开发都知道是怎么回事。那今天要讲的就是编译器在link时的一个坑!
讲个故事
最近在我们在发布SDK的时候,测试和预发,以及灰度都做完了。在正式发布的时候直接闪退了。看到这个问题直接一脸懵逼!
在SDK中,我们接入了人脸识别的SDK,在APP里面接入的时候,出现了闪退。挂在了RSA验签上,为什么同样的SDK会在正式发布的时候必闪退呢?由于涉及些公司安全东西,本篇是个阉割版,就不说解决过程了,我直接告诉结果了!
iOS系统API中并没有提供RSA公钥验签的算法API。既然挂在了这里,怀疑是扣了openssl的部分代码,进行了修改。APP里面也接入了openssl,然后在编译链接的时候,实际的RSA调用函数是外面的openssl的RSA,导致了crash。
探索
既然是两个库里面同时存在同样的symbol,为什么不报duplicate symbol的错误?我们下面写个demo进行测试下。
一、建立两个测试Library(LinkLibraryA和LinkLibraryB)和一个demo工程。
- LinkLibraryA
//.h
#ifndef LinkLibraryA_h
#define LinkLibraryA_h
#include <stdio.h>
void sameMethod();
#endif /* LinkLibraryA_h */
//.c
void sameMethod()
{
printf("==== Library A ====");
}
- LinkLibraryB
//.h
#ifndef LinkLibraryB_h
#define LinkLibraryB_h
#include <stdio.h>
void sameMethod();
#endif /* LinkLibraryB_h */
//.c
void sameMethod()
{
printf("==== Library B ====");
}
- demo
//mian.c
#include <stdio.h>
#include "LinkLibraryA.h"
int main(int argc, const char * argv[]) {
sameMethod();
return 0;
}
二、测试过程
直接编译发现xcode并没有报duplicate symbol错误。
测试1——先链接LinkLibraryA再链接LinkLibraryB
运行结果:==== Library A ====
测试2——先链接LinkLibraryB再链接LinkLibraryA
运行结果:==== Library B ====
通过以上两个测试,我们可以发现,存在同样的symbol,link的时候是不报错的!而且执行结果是依赖于静态库ld的顺序的!
进一步测试
- 修改下LinkLibraryB
//.h
#ifndef LinkLibraryB_h
#define LinkLibraryB_h
#include <stdio.h>
int sameMethod();
void testMethod(int a);
#endif /* LinkLibraryB_h */
//.c
int sameMethod()
{
printf("==== Library B ====");
return 1;
}
void testMethod(int a)
{
printf("==== test link===");
}
将sameMethod增加一个返回值,增加另个一入参是int的testMethod的函数。
- 修改下demo测试代码
//main.c
#include <stdio.h>
#include "LinkLibraryB.h"
int main(int argc, const char * argv[]) {
testMethod(sameMethod());
return 0;
}
在main函数里面,我们在testMethod方法中,将sameMethod当入参传入。
测试1——先链接LinkLibraryA再链接LinkLibraryB
运行结果:编译不通过!报duplicate symbol _sameMethod
测试2——先链接LinkLibraryB再链接LinkLibraryA
运行结果:=== Library B ======== test link===
总结
通过以上测试可以发现,如果linker解决unresolved symbols时,如果已经找到symbol,那么将不再去其他静态库里面寻找。
后面两组测试,第一组报错了,是因为ld LinkLibraryA的时候已经发现sameMethod,但是需要解决testMethod的symbol,接着ld LinkLibraryB的时候,LinkLibraryB里面的LinkLibraryB.o中找到了testMethod,但是,又一次发现了sameMethod,所以报错了!(后来,我把testMethod换成单独的文件去写,无论先链接谁,都是编译不报错的,但是逻辑和上面说的一样,从侧面也说明了linker优先寻找本静态库的符号,如果找到将不再去其他静态库进行寻找)
如果在other linker flags里面添加-all_load那么也就正常报错了,也就是我们所说的linker不允许存在重复的符号!
思考
通过以上测试,我们发现了在接入静态库的时候,容易出现此类问题。所以,我觉得以下几个点,大家还是需要关注下:
- C没有函数重载,哪怕你参数多一个,编译链接的时候,也不会报错。因此,命名应有一套规范,防止和外面的C函数进行冲突
这点我觉得可以参考OC的方法,加上前缀。
- C函数不对外开放的函数,尽量加上static,限制它的作用域
- 对于接入的多个SDK依赖共同的SDK,请一定要仔细确认版本。
以上问题,大家注意下,希望大家别再踩了这个坑!