James Yu


写了那么多的if else 依然未能参透其精髓。。。


关于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,请一定要仔细确认版本。

以上问题,大家注意下,希望大家别再踩了这个坑!

comments powered by Disqus