關(guān)于duplicate symbol的思考
iOS 開發(fā)中經(jīng)常會遇到duplicate symbol這個問題丙者,在編譯鏈接的時候就會出現(xiàn)哥纫。相信有經(jīng)驗的開發(fā)都知道是怎么回事。那今天要講的就是編譯器在link時的一個坑召川!
講個故事
最近在我們在發(fā)布SDK的時候南缓,測試和預發(fā),以及灰度都做完了荧呐。在正式發(fā)布的時候直接閃退了汉形。看到這個問題直接一臉懵逼坛增!
在SDK中获雕,我們接入了人臉識別的SDK,在APP里面接入的時候收捣,出現(xiàn)了閃退届案。掛在了RSA驗簽上,為什么同樣的SDK會在正式發(fā)布的時候必閃退呢罢艾?由于涉及些公司安全東西楣颠,本篇是個閹割版尽纽,就不說解決過程了,我直接告訴結(jié)果了童漩!
iOS系統(tǒng)API中并沒有提供RSA公鑰驗簽的算法API弄贿。既然掛在了這里,懷疑是扣了openssl的部分代碼矫膨,進行了修改差凹。APP里面也接入了openssl,然后在編譯鏈接的時候侧馅,實際的RSA調(diào)用函數(shù)是外面的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;
}
二、測試過程
直接編譯發(fā)現(xiàn)xcode并沒有報duplicate symbol錯誤小渊。
測試1——先鏈接LinkLibraryA再鏈接LinkLibraryB
運行結(jié)果:==== Library A ====
測試2——先鏈接LinkLibraryB再鏈接LinkLibraryA
運行結(jié)果:==== Library B ====
通過以上兩個測試法褥,我們可以發(fā)現(xiàn),存在同樣的symbol粤铭,link的時候是不報錯的挖胃!而且執(zhí)行結(jié)果是依賴于靜態(tài)庫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增加一個返回值梆惯,增加另個一入?yún)⑹莍nt的testMethod的函數(shù)酱鸭。
- 修改下demo測試代碼
//main.c
#include <stdio.h>
#include "LinkLibraryB.h"
int main(int argc, const char * argv[]) {
testMethod(sameMethod());
return 0;
}
在main函數(shù)里面,我們在testMethod方法中垛吗,將sameMethod當入?yún)魅搿?/p>
測試1——先鏈接LinkLibraryA再鏈接LinkLibraryB
運行結(jié)果:編譯不通過凹髓!報duplicate symbol _sameMethod
測試2——先鏈接LinkLibraryB再鏈接LinkLibraryA
運行結(jié)果:=== Library B ======== test link===
總結(jié)
通過以上測試可以發(fā)現(xiàn),如果linker解決unresolved symbols時怯屉,如果已經(jīng)找到symbol蔚舀,那么將不再去其他靜態(tài)庫里面尋找。
后面兩組測試锨络,第一組報錯了赌躺,是因為ld LinkLibraryA的時候已經(jīng)發(fā)現(xiàn)sameMethod,但是需要解決testMethod的symbol羡儿,接著ld LinkLibraryB的時候礼患,LinkLibraryB里面的LinkLibraryB.o中找到了testMethod,但是,又一次發(fā)現(xiàn)了sameMethod缅叠,所以報錯了悄泥!(后來,我把testMethod換成單獨的文件去寫肤粱,無論先鏈接誰弹囚,都是編譯不報錯的,但是邏輯和上面說的一樣领曼,從側(cè)面也說明了linker優(yōu)先尋找本靜態(tài)庫的符號鸥鹉,如果找到將不再去其他靜態(tài)庫進行尋找)
如果在other linker flags里面添加-all_load那么也就正常報錯了,也就是我們所說的linker不允許存在重復的符號庶骄!
思考
通過以上測試宋舷,我們發(fā)現(xiàn)了在接入靜態(tài)庫的時候,容易出現(xiàn)此類問題瓢姻。所以,我覺得以下幾個點音诈,大家還是需要關(guān)注下:
- C沒有函數(shù)重載幻碱,哪怕你參數(shù)多一個,編譯鏈接的時候细溅,也不會報錯褥傍。因此,命名應有一套規(guī)范喇聊,防止和外面的C函數(shù)進行沖突
這點我覺得可以參考OC的方法恍风,加上前綴。
- C函數(shù)不對外開放的函數(shù)誓篱,盡量加上static朋贬,限制它的作用域
- 對于接入的多個SDK依賴共同的SDK,請一定要仔細確認版本窜骄。
以上問題锦募,大家注意下,希望大家別再踩了這個坑邻遏!