一、 問題引入
在當(dāng)下的開發(fā)中,應(yīng)用的功能做的越來越復(fù)雜,工程也越來越大,所以為了
盡可能縮短開發(fā)周期,不可避免的會(huì)用到許多第三方庫,隨之而來的也會(huì)遇到好
多問題艘绍。比如,程序調(diào)用函數(shù)funa,funa函數(shù)從在于兩個(gè)庫liba.a,libb.a中,
并且程序執(zhí)行需要連接這兩個(gè)庫,那么程序執(zhí)行時(shí)是調(diào)用liba.a中funa還是調(diào)用
的libb.a中的funa呢?
其實(shí)這個(gè)取決于鏈接時(shí)的順序,比如先鏈接的liba.a,這個(gè)時(shí)候通過liba.a的導(dǎo)出符號表就可以找到funa在liba.a中定義,并加入符號表中;鏈接libb.a的
時(shí)候發(fā)現(xiàn)符號表已經(jīng)存在funa,就不會(huì)再次更新符號表,所以調(diào)用的始終是liba.a中的funa函數(shù)大刊。
這里的調(diào)用嚴(yán)重的依賴于鏈接庫加載的順序,很大程度上會(huì)導(dǎo)致混論鬼悠。作為SDK的提供者,我們尤其要避免這點(diǎn)债鸡。
正常我們使用的庫中包含了好多符號信息,如圖1所示:
這些符號信息有以下幾個(gè)弊端:
1扩劝、增大了庫的體積;
2庸论、隱蔽性較差;
3、容易帶來沖突棒呛。在開發(fā)過程中第三點(diǎn)帶來的問題尤其嚴(yán)重,特別是當(dāng)我們提供的SDK用到第
三方庫的時(shí)候(因?yàn)槭褂梦覀僑DK的客戶也有可能用到跟我們一樣的第三方庫)聂示。
二、 解決辦法
1簇秒、對第三方庫處理 ?(C/C++)
下面繼續(xù)以x264(下文以libx264.a帶過)為例說明如何編譯第三方的庫鱼喉。
沒有隱藏符號的第三方庫如“圖1”所示,函數(shù)前面會(huì)帶有external的標(biāo)示。
在最終對外發(fā)布的SDK中_x264_predict_16x16_dc_c還是打著external的標(biāo)簽,
及對外可見趋观。如圖2所示:
隱藏符號后,在libx264.a中,原先打上external標(biāo)簽的函數(shù),會(huì)以private external標(biāo)識扛禽。如圖3所示:
那么如何才能得到我們想要的、打上private external標(biāo)簽的庫呢,有兩種方
法可以做到皱坛。
1)對每個(gè)函數(shù)加屬性__attribute__((visibility(“hidden”))) void funa_hidden()
{
printf(“hidden symbol\n”);
}
void funa_visible()
{
printf(“exported symbol”);
}
這樣做的好處是可以根據(jù)需要對每個(gè)函數(shù)做定制處理编曼。但若我們用到的三方庫代碼量大,這種方法就是費(fèi)時(shí)費(fèi)力了。
2)編譯庫時(shí)統(tǒng)一處理
? ? ? ? 利用gcc的擴(kuò)展屬性,編譯庫時(shí)加上-fvisibility=hidden麸恍。
a)靜態(tài)庫
? ? ? ? gcc –static –o libtest.a –fvisibility=hidden –c test.c
b)動(dòng)態(tài)庫
? ? ? ? ?gcc –dynamic –o libtest.so –fvisility=hidden –c test.c,其中dynamic為clang的寫法,大部分gcc寫法為shared灵巧。
? ? ? ? ?上邊兩種方法只處理了c/c++,因?yàn)檎Z法問題,匯編需要做特殊里,但也是在函數(shù)頭加屬性,但它的屬性寫法為.private_extern。.macro function name, export=0, align=2?
.macro endfunc ?
ELF .size \name, .?
- \name?
FUNC .endfunc
.purgem endfunc
.endm
.text
.align \align
.if \export
.global EXTERN_ASM\name
ELF .type EXTERN_ASM\name, %function
FUNC .func EXTERN_ASM\name
EXTERN_ASM\name:
.private_extern
EXTERN_ASM\name
.else
ELF .type
FUNC .func ? ?\name: ? .endif
.endm
\name, %function
\name
因?yàn)樾枰幚淼膮R編文件較少,所以對匯編采用了直接編輯源文件的方法抹沪。
其實(shí)個(gè)人覺得也應(yīng)該能在編譯時(shí)做統(tǒng)一處理,有興趣的可以自己找一下方法刻肄。
2、對xcode工程的處理 (iOS)
? ? ? ?對xcode工程處理相對直觀融欧、簡單了許多敏弃。只需在工程的設(shè)置里做如下處理。
1)打開工程設(shè)置,跳轉(zhuǎn)到build setting頁面;
2)搜索hidden;
3)將Symbols Hidden by Default設(shè)置Yes;
其實(shí)通過觀察編譯的過程可以發(fā)現(xiàn),通過上述設(shè)置,蘋果最終將其轉(zhuǎn)化為步
驟1的命令進(jìn)行編譯噪馏。編譯的結(jié)果也是在庫里加了private external而已麦到。
3、符號剝離
? ? ? ?最后一步,也是最關(guān)鍵的一步,就是真正將步驟1或步驟2中打上private
external標(biāo)簽的函數(shù)做最終的處理,把它們從要發(fā)布的庫里剝離欠肾。
1)首先設(shè)置prelink
在target的build setting里搜索prelink,將Perform Single-Object Prelink置為
Yes,然后把該工程需要的庫都直接拖到Prelink libraries中瓶颠。如圖5所示:
2)設(shè)置 post process
將Deployment Postprocessing置為Yes。如圖6所示:
3)設(shè)置剝離方式
將Strip Style設(shè)置為Non-Global Symbols刺桃。如圖7所示:
到目前為止,所有的設(shè)置都已經(jīng)完成,接下來編譯粹淋。有興趣的同學(xué)可以觀察
一下編譯的過程,會(huì)發(fā)現(xiàn)通過設(shè)置prelink,xcode會(huì)將庫里所有的目標(biāo)文件根據(jù)
你支持的architecture分類打包,如libxxx-armv7-master.o/libxxx-arm64-master.o,
最后一步執(zhí)行Strip命令將所有需要隱藏的符號剝離。