如何隱藏SDK中符號
一、 問題引入
在當下的開發中,應用的功能做的越來越複雜,工程也越來越大,所以為了儘可能縮短開發周期,不可避免的會用到許多第三方庫,隨之而來的也會遇到好多問題。比如,程序調用函數funa,funa函數從在於兩個庫liba.a,libb.a中,並且程序執行需要連接這兩個庫,那麼程序執行時是調用liba.a中funa還是調用的libb.a中的funa呢?
其實這個取決於鏈接時的順序,比如先鏈接的liba.a,這個時候通過liba.a的導出符號表就可以找到funa在liba.a中定義,並加入符號表中;鏈接libb.a的時候發現符號表已經存在funa,就不會再次更新符號表,所以調用的始終是liba.a中的funa函數。
這裡的調用嚴重的依賴於鏈接庫載入的順序,很大程度上會導致混論。作為SDK的提供者,我們尤其要避免這點。
正常我們使用的庫中包含了好多符號信息,如圖1所示:
這些符號信息有以下幾個弊端:
1、增大了庫的體積;
2、隱蔽性較差;
3、容易帶來衝突。
在開發過程中第三點帶來的問題尤其嚴重,特別是當我們提供的SDK用到第三方庫的時候(因為使用我們SDK的客戶也有可能用到跟我們一樣的第三方庫)。
二、 解決辦法
1、對第三方庫處理
下面繼續以x264(下文以libx264.a帶過)為例說明如何編譯第三方的庫。
沒有隱藏符號的第三方庫如「圖1」所示,函數前面會帶有external的標示。在最終對外發布的SDK中_x264_predict_16x16_dc_c還是打著external的標籤,及對外可見。如圖2所示:
隱藏符號後,在libx264.a中,原先打上external標籤的函數,會以private external標識。如圖3所示:
那麼如何才能得到我們想要的、打上private external標籤的庫呢,有兩種方法可以做到。
1)對每個函數加屬性
__attribute__((visibility(「hidden」))) void funa_hidden()
{
printf(「hidden symbol
」);
}
void funa_visible()
{
printf(「exported symbol」);
}
這樣做的好處是可以根據需要對每個函數做定製處理。但若我們用到的三方庫代碼量大,這種方法就是費時費力了。
2)編譯庫時統一處理
利用gcc的擴展屬性,編譯庫時加上-fvisibility=hidden。
a) 靜態庫
gcc –static –o libtest.a –fvisibility=hidden –c test.c
b) 動態庫
gcc –dynamic –o libtest.so –fvisility=hidden –c test.c
其中dynamic為clang的寫法,大部分gcc寫法為shared。
上邊兩種方法只處理了c/c++,因為語法問題,彙編需要做特殊里,但也是在函數頭加屬性,但它的屬性寫法為.private_extern。
.macro function name, export=0, align=2
.macro endfunc
ELF .size
ame, . -
ame
FUNC .endfunc
.purgem endfunc
.endm
.text
.align align
.if export
.global EXTERN_ASM
ame
.private_extern EXTERN_ASM
ame
ELF .type EXTERN_ASM
ame, %function
FUNC .func EXTERN_ASM
ame
EXTERN_ASM
ame:
.else
ELF .type
ame, %function
FUNC .func
ame
ame:
.endif
.endm
因為需要處理的彙編文件較少,所以對彙編採用了直接編輯源文件的方法。其實個人覺得也應該能在編譯時做統一處理,有興趣的可以自己找一下方法。
2、對xcode工程的處理
對xcode工程處理相對直觀、簡單了許多。只需在工程的設置里做如下處理。
1) 打開工程設置,跳轉到build setting頁面;
2) 搜索hidden;
3) 將Symbols Hidden by Default設置Yes;
其實通過觀察編譯的過程可以發現,通過上述設置,蘋果最終將其轉化為步驟1的命令進行編譯。編譯的結果也是在庫里加了private external而已。
3、符號剝離
最後一步,也是最關鍵的一步,就是真正將步驟1或步驟2中打上private external標籤的函數做最終的處理,把它們從要發布的庫里剝離。
1) 首先設置prelink
在target的build setting里搜索prelink,將Perform Single-Object Prelink置為Yes,然後把該工程需要的庫都直接拖到Prelink libraries中。如圖5所示:
2) 設置post process
將Deployment Postprocessing置為Yes。如圖6所示:
3) 設置剝離方式
將Strip Style設置為Non-Global Symbols。如圖7所示:
到目前為止,所有的設置都已經完成,接下來編譯。有興趣的同學可以觀察一下編譯的過程,會發現通過設置prelink,xcode會將庫里所有的目標文件根據你支持的architecture分類打包,如libxxx-armv7-master.o/libxxx-arm64-master.o,最後一步執行Strip命令將所有需要隱藏的符號剝離。
容聯技術乾貨推薦>>>
技術乾貨 | PushKit和CallKit實現——基於實時語音通話技術乾貨 | HTML5之簡單的音視頻應用開發探尋
技術乾貨 | H.264 SVC技術介紹及應用分析
技術乾貨丨ELK日誌系統部署與優化
推薦閱讀:
※張明雲的Live——安卓 SDK 開發實戰經驗分享
※與 Xcode 相比,用 Adobe AIR/Flex做 iOS 開發有哪些優勢和局限?
※無人機 SDK 平台開放後,有哪些 App 場景?
※矽谷和國內的 iOS 開發到底有何不同?
※手游第三方服務商興衰史:建立核心競爭壁壘方能始終