iOS鏈接與Symbol

Mach-O是macOS、iOS舱污、iPadOS存儲(chǔ)程序和庫(kù)的文件格式什往,對(duì)應(yīng)的系統(tǒng)通過(guò)應(yīng)用二進(jìn)制接口(ABI--MachO內(nèi)容的格式)來(lái)運(yùn)行該格式的文件。保存了在編譯過(guò)程和鏈接過(guò)程中產(chǎn)生的機(jī)器代碼和數(shù)據(jù)慌闭。為靜態(tài)鏈接和動(dòng)態(tài)鏈接提供了單一的文件格式别威。

當(dāng)我們點(diǎn)擊Xcode Run的時(shí)候,系統(tǒng)會(huì)加載IPA包內(nèi)的可執(zhí)行文件驴剔,調(diào)用fork函數(shù)省古,創(chuàng)建一個(gè)進(jìn)程,然后調(diào)用execve程序加載器丧失,將文件加載到內(nèi)存豺妓,分析MachO中的mach_header,以確認(rèn)它是有效格式的MachO文件布讹。

那么MachO到底是什么格式呢琳拭?如何查看MachO文件里面的內(nèi)容,我們可以通過(guò)一個(gè)可視化的工具M(jìn)achOView來(lái)查看描验,也可以通過(guò)objdump命令來(lái)查看objdump --macho --private-headers MachO文件路徑白嘁,通過(guò)控制臺(tái)輸出或MachOView可以看到,它里面有Header膘流、Load command還有一些Sections等絮缅,可以看出它其實(shí)就是有很多的配置項(xiàng)的二進(jìn)制文件。

編譯與鏈接:當(dāng)我們寫(xiě)的代碼進(jìn)行編譯的時(shí)候會(huì)生成一個(gè)個(gè).o文件呼股,也就是MachO文件耕魄,這個(gè)過(guò)程就是將代碼放到對(duì)應(yīng)的配置中,將各種類(lèi)型的符號(hào)進(jìn)行歸類(lèi)存放彭谁。而鏈接的本質(zhì)就是把多個(gè)目標(biāo)(.o)文件組合成一個(gè)可執(zhí)行文件吸奴。把多個(gè)目標(biāo)文件合并到一起,在合并的時(shí)候可以對(duì)其內(nèi)部符號(hào)對(duì)外暴露的屬性進(jìn)行修改缠局。

什么是符號(hào)则奥?符號(hào)存放在哪里?符號(hào)的概念比較廣甩鳄,可以說(shuō)我們編寫(xiě)的代碼中的函數(shù)逞度,變量等一切皆為符號(hào),他們?cè)贛achO中保存在一個(gè)叫Symbol Table的東西內(nèi)妙啃,除了Symbol Table(符號(hào)表)還有一個(gè)Indirect Symbol Table被稱(chēng)為間接符號(hào)表档泽,它是符號(hào)表的一個(gè)子集俊戳,里面保存了引用的其他庫(kù)的符號(hào),如我們使用使用NSLog()函數(shù)馆匿,但是NSLog()的實(shí)現(xiàn)是在Foundation中抑胎,它就是一個(gè)外部符號(hào),存放在間接符號(hào)表渐北。

符號(hào)有哪些種類(lèi)阿逃?我們可以親自實(shí)操一下,生成一個(gè)MachO文件然后通過(guò)終端去查看或MachOView查看赃蛛,假如我們使用MachOView會(huì)比較麻煩恃锉,所以我們先來(lái)了解一下如何實(shí)現(xiàn)xcode編譯后自動(dòng)實(shí)現(xiàn)在終端輸出我們想要查看的MachO信息,首先了解一個(gè)命令tty呕臂,通過(guò)man tty命令查看return user's terminal name可以知道這個(gè)命令輸出的是當(dāng)前終端的名稱(chēng)破托。既然知道了終端的名稱(chēng),那也就意味著我們可以通過(guò)重定向?qū)⒔Y(jié)束輸出到終端歧蒋。

image.png

同樣在xcode run script中我們也可以這么玩土砂,我們寫(xiě)一個(gè)xcode_tty.sh腳本放到工作根目錄,來(lái)讓這個(gè)過(guò)程自動(dòng)化谜洽,Run Script中執(zhí)行sh $SRCROOT/xcode_tty.sh "${BUILT_PRODUCTS_DIR}/*" "/dev/ttys000"

#xcode_tty.sh腳本內(nèi)容
#!/bin/sh
EchoError() {
    if [[ -n "$2" ]]; then
        echo "$@" 1>&2>$2
    else
        echo "$@" 1>&2
    fi
}
if [[ ! -e "$2" ]]; then
    EchoError "===ERROR: Not Config tty to output."
    exit -1
fi
#查看__TEXT
objdump --macho -d $1 1>$2
#查看mach-header
#objdump --macho --private-headers $1 1>$2
#查看符號(hào)類(lèi)型
#`objdump --macho -syms $1 1>$2`
#查看導(dǎo)出符號(hào)表
#objdump --macho --exports-trie $1 1>$2
#查看間接符號(hào)表
#objdump --macho --indirect-symbols $1 1>$2

使用查看符號(hào)類(lèi)型的命令輸出結(jié)果中小寫(xiě)l代表本地符號(hào)萝映,小寫(xiě)的g代表是全局符號(hào),在main.m文件中定義幾個(gè)變量運(yùn)行 -syms命令阐虚,可以看出我們定義的變量是全局變量序臂,而加了static之后就變成了本地變量,并且可以看出全局符號(hào)中未初始化的符號(hào)存放在__common段敌呈,已初始化的存放在__data段:

int cpy_inta = 10;
int cpy_intaa;
static int cpy_intb = 10;
static int cpy_inbb;
 d  *UND* _cpy_inta
0000000000000000 l    d  *UND* _cpy_inta
0000000000000000 l    d  *UND* _cpy_intaa
0000000100008018 g     O __DATA,__data _cpy_inta
0000000100008040 g     O __DATA,__common _cpy_intaa

使用objdump --macho --exports-trie $1 1>$2命令可以查看所有的導(dǎo)出符號(hào)贸宏,也就說(shuō),符號(hào)還也可以分導(dǎo)入符號(hào)和導(dǎo)出符號(hào)磕洪,其實(shí)導(dǎo)出符號(hào)也就是對(duì)應(yīng)著全局符號(hào),也就是說(shuō)我們的全局符號(hào)是可以被別人使用的诫龙,但是全局符號(hào)并不一定全是導(dǎo)出符號(hào)析显,因?yàn)槲覀兊逆溄悠骺梢钥刂扑欠窨梢詫?dǎo)出。間接符號(hào)表保存著當(dāng)前可執(zhí)行文件使用的其他的庫(kù)的符號(hào)签赃,全局符號(hào)可以變成導(dǎo)出符號(hào)給別人使用谷异,因此這個(gè)間接符號(hào)表是不能被刪除,也就意味著所有的全局符號(hào)不能被刪除锦聊,我們的OC方法默認(rèn)都是全局符號(hào)歹嘹,因此我們沒(méi)有使用的OC類(lèi)要進(jìn)行刪除,不然還會(huì)占用空間孔庭。其實(shí)鏈接器也提供了一個(gè)參數(shù)可以控制符號(hào)不被導(dǎo)出OTHER_LGFLAGS=$(inherited) -Xlinker -unexported_symbol -Xlinker ${符號(hào)}尺上。追求極致的人可以通過(guò)鏈接器的此特性將使用的全局符號(hào)不需要被外部使用的全部設(shè)置為不導(dǎo)出材蛛,從而減小應(yīng)用包的體積。-unexported_symbol_list可以指定一個(gè)文件進(jìn)行批量處理怎抛。OTHER_LGFLAGS=$(inherited) -Xlinker -map -Xlinker 文件路徑可以輸出當(dāng)前可執(zhí)行文件使用的符號(hào)情況卑吭。

還有一個(gè)符號(hào)類(lèi)型叫Weak Symbol,Weak defintion Symbol表示弱定義符號(hào)马绝,如果鏈接器找到了另外一個(gè)非弱定義的符號(hào)豆赏,那么此弱定義符號(hào)將會(huì)被忽略,也就是說(shuō)它就是個(gè)備胎富稻。Weak Reference Symbol表示弱引用掷邦,如果鏈接器找不到該符號(hào)的定義,那么會(huì)將它置為0椭赋,使用if(弱引用) 相當(dāng)于if(0)抚岗。

//弱定義示例
void weakDefineFunction(void) __attribute__((weak));
//弱引用示例
void weakImportFunction(void) __attribute__((weak_import));
  • 弱定義并不會(huì)影響它的全局屬性和導(dǎo)出(如果將弱定義隱藏,它將變成一個(gè)本地符號(hào))纹份,它的好處就是在別人導(dǎo)入它之后可以進(jìn)行重寫(xiě)實(shí)現(xiàn)苟跪。如果多個(gè)文件中都寫(xiě)了弱定義的實(shí)現(xiàn)方法,那么將會(huì)按照順序查找到第一個(gè)非弱定義的即停止蔓涧。
  • 弱引用可以不實(shí)現(xiàn)件已,那么它在編譯時(shí)并不會(huì)報(bào)錯(cuò)。但是在鏈接時(shí)會(huì)產(chǎn)生報(bào)錯(cuò)undefined symbol元暴,可以通過(guò)鏈接器參數(shù)-U來(lái)告訴鏈接器它沒(méi)有定義篷扩,需要運(yùn)行時(shí)動(dòng)態(tài)查找,示例OTHER_LGFLAGS=$(inherited) -Xlinker -U -Xlinker _weakImportFunction茉盏。另外鏈接器還有一個(gè)-undefined的參數(shù)鉴未,它默認(rèn)指定未定義時(shí)error,還有warning鸠姨,dynamic_lookup铜秆,假如我們將找不到的符號(hào)全部聲明成dynamic_lookup那么程序?qū)⒃僖膊粫?huì)報(bào)錯(cuò)了,全部變成了動(dòng)態(tài)查找讶迁,當(dāng)然在正常開(kāi)發(fā)中千萬(wàn)不要這么亂搞连茧。在運(yùn)行時(shí)如果找不到定義它就是0。

Swift符號(hào)與OC不太一樣巍糯,swift是一種靜態(tài)語(yǔ)言啸驯,它的符號(hào)類(lèi)型根據(jù)關(guān)鍵字public/private來(lái)區(qū)分,使用public則是全局符號(hào)祟峦,使用private可以變成本地符號(hào)罚斗,因此swift中的關(guān)鍵字一定要合理的使用。

另外還有一種re-export符號(hào)宅楞,它是將引用的外部符號(hào)重新導(dǎo)出针姿。

了解了符號(hào)之后袱吆,我們才能更好的去理解Strip剝離符號(hào)。xcode提供的Strip Style有三種分別是All Symbol搓幌、Non-Global Symbol杆故、Debugging Symbol,究竟該如何使用溉愁?

  • 對(duì)于動(dòng)態(tài)庫(kù)处铛,因?yàn)槭且獙?dǎo)出給別人使用,所以它的所有的全局符號(hào)都不能被脫掉拐揭,使用Non-Global Symbol撤蟆。
  • 對(duì)于靜態(tài)庫(kù),它是.o文件的合集堂污,它的符號(hào)全部都放在重定位符號(hào)表中家肯,在鏈接時(shí)需要使用,因此它不能被脫掉盟猖,只有調(diào)試符號(hào)才能被脫掉讨衣,使用Debugging Symbol
  • 對(duì)于App式镐,它根本不需要導(dǎo)出給別人使用反镇,因此不管是本地符號(hào)、全局符號(hào)還有弱定義符號(hào)都可以干掉娘汞,所以在app上架是通常使用All Symbol歹茶,另外即使是All Symbol也不會(huì)脫掉間接符號(hào)表中的符號(hào)。

app使用靜態(tài)庫(kù)的符號(hào)最終會(huì)合并放到app內(nèi)你弦,也就意味著靜態(tài)庫(kù)的符號(hào)會(huì)變成了app內(nèi)的本地惊豺、全局、導(dǎo)出等符號(hào)禽作,因此最終也會(huì)被干掉尸昧,總的符號(hào)會(huì)變少。而動(dòng)態(tài)庫(kù)則只能增加符號(hào)旷偿,總的符號(hào)會(huì)變多彻磁。符號(hào)多寡也就影響包的體積。

Debugging Symbol:靜態(tài)庫(kù)的調(diào)試符號(hào)是存放在MachO文件的一個(gè)__DWARF的Segment中狸捅,DWARF全稱(chēng)Debugging With Attributed RecordFormats。符號(hào)剝離的過(guò)程為:MachO文件解析成模型Object累提,然后通過(guò)遍歷LoadCommands尘喝,找到Segname==__DWARF的LoadCommand,移除里面的section斋陪,再?gòu)姆?hào)表中移除Symbol朽褪,最后將修改后的模型Object重新寫(xiě)入MachO置吓,因此說(shuō)我們的MachO文件是一個(gè)可讀寫(xiě)的文件。Strip就是修改MachO文件缔赠。而動(dòng)態(tài)庫(kù)它是沒(méi)有__DWARF的段衍锚,它是遍歷符號(hào)表,將n_type包含N_STAB(0xe0)的全部刪除嗤堰,N_STAB(0xe0)代表調(diào)試符號(hào)戴质。

All Symbol:遍歷符號(hào),只要不是間接符號(hào)表中的符號(hào)踢匣,全部刪除
Non-Global Symbols:遍歷符號(hào)表告匠,判斷n_type!= N_EXT都可以刪除,N_EXT代表外部符號(hào)

Dead code Strip是鏈接器的一個(gè)參數(shù)离唬,它是用來(lái)剝離死代碼后专,將沒(méi)有用到的函數(shù)和數(shù)據(jù)干掉,Xcode中默認(rèn)是YES输莺。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末戚哎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子嫂用,更是在濱河造成了極大的恐慌型凳,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尸折,死亡現(xiàn)場(chǎng)離奇詭異啰脚,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)实夹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)橄浓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人亮航,你說(shuō)我怎么就攤上這事荸实。” “怎么了缴淋?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵准给,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我重抖,道長(zhǎng)露氮,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任钟沛,我火速辦了婚禮畔规,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘恨统。我一直安慰自己叁扫,他們只是感情好三妈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著莫绣,像睡著了一般畴蒲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上对室,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天模燥,我揣著相機(jī)與錄音,去河邊找鬼软驰。 笑死涧窒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的锭亏。 我是一名探鬼主播纠吴,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼慧瘤!你這毒婦竟也來(lái)了戴已?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤锅减,失蹤者是張志新(化名)和其女友劉穎糖儡,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體怔匣,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡握联,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了每瞒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片湾趾。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锈嫩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情岸浑,我是刑警寧澤诞丽,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布型将,位于F島的核電站够挂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏贷掖。R本人自食惡果不足惜嫡秕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望苹威。 院中可真熱鬧淘菩,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)腹暖。三九已至汇在,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間脏答,已是汗流浹背糕殉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留殖告,地道東北人阿蝶。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像黄绩,于是被迫代替她去往敵國(guó)和親羡洁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容