dyld流程分析

在講dyld流程之前昂儒,我先提一個(gè)問題沟使,就是在我們程序運(yùn)行的時(shí)候,在main函數(shù)之前渊跋,會(huì)先走ViewController的load方法腊嗡, 再走C++的方法着倾,這是為什么?

main函數(shù)來之前

帶著這個(gè)問題叽唱,我們開始今天的探索之旅屈呕。

首先我們都知道在程序跑起來之前,依賴于很多庫棺亭,比如說動(dòng)態(tài)庫和靜態(tài)庫虎眨,我們稱為鏡像文件images,這些庫和文件在加載的時(shí)候都需要用到dyld程序進(jìn)行鏈接镶摘,dyld是蘋果的動(dòng)態(tài)鏈接器嗽桩,在程序加載前一個(gè)非常重要部分。鏈接完了之后就會(huì)生成一個(gè)可執(zhí)行文件exec凄敢。 流程如下:

編譯過程

那么明白這一點(diǎn)之后碌冶,接下來我們分析整個(gè)的流程必然就從dyld入手,dyld是開源的涝缝,先從蘋果開發(fā)者官網(wǎng)下載一份dyld扑庞,我這里下載的是750.6版本。

dyld打開拒逮,打開之后發(fā)現(xiàn)里面東西很多罐氨,不知道從哪入手,不過看過我之前文章的人滩援,我相信應(yīng)該知道堆棧這個(gè)東西栅隐,就是bt,我們?cè)?code>load里面打個(gè)斷點(diǎn)bt一下

load方法bt

bt之后我們可以看到最后一行的dyld_dyld_start玩徊,所以dyld入口就在_dyld_start`里面租悄,接下來我們?nèi)炙阉髡业较嚓P(guān)源碼

_dyld_start入口

找到相應(yīng)的入口之后,可以看到是通過一些匯編寫的恩袱,看不懂沒關(guān)系泣棋,旁邊有注釋,看注釋看到了這一句# call dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue) 憎蛤,這就好辦了外傅,這是一個(gè)C++的開始函數(shù),我們跟過去

start入口函數(shù)

跟過來之后俩檬,我們看到這里有一個(gè)main萎胰,我們可以從堆棧里面看出這是第二個(gè)流程。我們點(diǎn)到main里面去棚辽。

調(diào)用的main函數(shù)

發(fā)現(xiàn)里面有大幾百行代碼技竟,從頭看到尾肯定不行, 那么看一下最后它最后的返回值是result屈藐,然后找到賦值的地方榔组,發(fā)現(xiàn)主要是sMainExecutable這個(gè)主程序賦的值熙尉,那么接下來我們又開始找sMainExecutable這個(gè)玩意,最終發(fā)現(xiàn)它在6577行sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);在這個(gè)地方進(jìn)行了初始化搓扯,那么這個(gè)主程序是怎么進(jìn)行初始化的呢检痰,我們繼續(xù)點(diǎn)到instantiateFromLoadedImage里面去看一看

instantiateFromLoadedImage源碼

進(jìn)入instantiateFromLoadedImage源碼后,發(fā)現(xiàn)創(chuàng)建了一個(gè)ImageLoader實(shí)例對(duì)象锨推,通過instantiateMainExecutable方法創(chuàng)建

再進(jìn)到instantiateMainExecutable里面铅歼,其作用是為主可執(zhí)行文件創(chuàng)建映像,返回一個(gè)ImageLoader類型的image對(duì)象换可,即主程序椎椰。其中sniffLoadCommands函數(shù)時(shí)獲取Mach-O類型文件的Load Command的相關(guān)信息,并對(duì)其進(jìn)行各種校驗(yàn)

instantiateMainExecutable源碼

說到這里沾鳄,我們的dyld究竟做了些什么事情慨飘, 主要分為以下幾步:

1、環(huán)境變量的配置(根據(jù)環(huán)境變量設(shè)置相應(yīng)的值以及獲取當(dāng)前運(yùn)行架構(gòu))


環(huán)境變量的配置

2译荞、檢查共享緩存 (檢查是否開啟瓤的,以及共享緩存是否映射到共享區(qū)域,例如UIKit吞歼、CoreFoundation等)


檢查共享緩存

3堤瘤、主程序的初始化 (調(diào)用instantiateFromLoadedImage函數(shù)實(shí)例化了一個(gè)ImageLoader對(duì)象)


主程序的初始化

4、 加入動(dòng)態(tài)庫(遍歷DYLD_INSERT_LIBRARIES環(huán)境變量浆熔,調(diào)用loadInsertedDylib)


加入動(dòng)態(tài)庫

5、link 鏈接主程序


鏈接主程序

6桥帆、link 鏈接動(dòng)態(tài)庫


鏈接動(dòng)態(tài)庫

7医增、main()(把鏈接起來的所有東西運(yùn)行起來,并發(fā)送通知)

main()

那么我們是怎么進(jìn)行initializers初始化的呢老虫,我們點(diǎn)進(jìn)去看一下

initializeMainExecutable

來到initializeMainExecutable里面之后,我們看到主要是循環(huán)遍歷叶骨,執(zhí)行runInitializers方法。 我們?cè)偃炙阉?code>runInitializers(cons,找到源碼祈匙,其核心代碼是processInitializers函數(shù)的調(diào)用

runInitializers源碼實(shí)現(xiàn)

繼續(xù)全局搜索processInitializers忽刽,來到processInitializers函數(shù)源碼實(shí)現(xiàn)

processInitializers源碼實(shí)現(xiàn)

這里重點(diǎn)在594行,對(duì)我們的鏡像列表調(diào)用recursiveInitialization進(jìn)行遞歸實(shí)例化夺欲,我們繼續(xù)全局搜索recursiveInitialization一探到底

image.png

這個(gè)地方有兩個(gè)重點(diǎn)跪帝,第一個(gè)重點(diǎn)是1595行和1603號(hào)都調(diào)用了notifySingle,并傳入了dyld_image些阅。 第二個(gè)重點(diǎn)是1598行this->doInitialization(context);伞剑,我們先來看看notifySingle做了什么操作

notifySingle源碼

這里的重點(diǎn)是(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());這一句,然后再全局搜索sNotifyObjCInit市埋,發(fā)現(xiàn)沒有找到實(shí)現(xiàn)黎泣,只有賦值的操作

sNotifyObjCInit賦值的地方

接著我們搜索registerObjCNotifiers在哪個(gè)地方調(diào)用了恕刘,結(jié)果發(fā)現(xiàn)在_dyld_objc_notify_register調(diào)用了

搜索registerObjCNotifiers在哪調(diào)用

_dyld_objc_notify_register這個(gè)函數(shù)不知道大家熟不熟悉,反正它離我們很近抒倚,結(jié)果在我們libobjc源碼中一搜索褐着,就在我們的_objc_init里面

_dyld_objc_notify_register所在位置

看到這里,sNotifyObjCInit的賦值的就是來自objc中的load_images托呕,那么_objc_init是什么時(shí)候調(diào)用的呢含蓉,接下來我們回到上面說的二個(gè)重點(diǎn)的第二個(gè)重點(diǎn)this->doInitialization(context);

我們進(jìn)入到doInitialization源碼實(shí)現(xiàn)

doInitialization源碼實(shí)現(xiàn)

這里有兩句重點(diǎn),我們先來看第一句doImageInit

doImageInit源碼實(shí)現(xiàn)

進(jìn)入到doImageInit之后镣陕,其核心主要是for循環(huán)加載方法的調(diào)用谴餐,這里需要注意的一點(diǎn)是,libSystem庫的要求很高呆抑,需要先初始化運(yùn)行岂嗓,這里也標(biāo)了注釋libSystem initializer must run first

再來看看doModInitFunctions源碼,這個(gè)方法中加載了所有Cxx文件

doModInitFunctions源碼

說了這么多鹊碍,現(xiàn)在在load方法打個(gè)斷點(diǎn)來看看堆棧和整個(gè)初始化過程

堆棧和初始化過程

雖然把整個(gè)堆棧過程打印出來了厌殉,但是沒有看到_objc_init的調(diào)用,我們?cè)偌觽€(gè)符號(hào)斷點(diǎn)看一下

_objc_init

來到_objc_init之后侈咕,前面的流程都一樣公罕,來看一下libSystem_initializer。在libsystem工程中查找libSystem_initializer耀销,查看源碼實(shí)現(xiàn)

libSystem_initializer源碼實(shí)現(xiàn)

來到這個(gè)源碼之后楼眷,我們看到走了libdispatch_init函數(shù),在我們初始化過程里面

libdispatch_init

這個(gè)函數(shù)在libSystem_initializer前面熊尉,源碼是在libdispatch.dylib開源庫中的罐柳,接下來我們找到libdispatch搜索libdispatch_init,找到實(shí)現(xiàn)的源碼如圖:

libdispatch_init源碼
初始化過程

libdispatch_init源碼里我們看到了_os_object_init狰住,也在我們初始化過程里面张吉,我們繼續(xù)跟過去

_os_object_init源碼實(shí)現(xiàn)

跟過來之后,發(fā)現(xiàn)第一句就調(diào)用了_objc_init催植,_objc_init里面又調(diào)用了_dyld_objc_notify_register進(jìn)行注冊(cè)肮蛹,傳了第二個(gè)參數(shù)load_images。 注冊(cè)了之后回到dyld里面的notifySingle创南, 然后會(huì)跳到sNotifyObjCInit = 參數(shù)2 調(diào)用sNotifyObjcInit()伦忠,形成了一個(gè)閉環(huán)

看到這里,總的流程總算是看完了稿辙,我這篇文章截圖代碼沒有注釋缓苛,所以不一定要搞懂代碼的意思,只需了解dyld大致流程即可,畢竟這些代碼沒幾個(gè)人能玩的通轉(zhuǎn)未桥。 整個(gè)流程如圖:

dyld流程圖

明白了dyld的整體流程之后笔刹,我們?cè)賮砜次恼麻_始前提到的一個(gè)問題就很好分析了

首先在程序加載的時(shí)候來到objc_init調(diào)用_dyld_objc_notify_register

_objc_init

然后執(zhí)行load_imagesload_images里面有call_load_methods-> call_class_loads -> (*load_method)(cls, @selector(load)) 調(diào)用所有類的load

調(diào)用完load之后會(huì)來到doInitialization里面的doModInitFunctions冬耿,在doModInitFunctions會(huì)調(diào)用所有Cxx函數(shù)

doInitialization

可以打斷點(diǎn)bt驗(yàn)證一下

Cxx bt分析

走完Cxx函數(shù)之后我們接著往下走

讀寄存器

走完之后會(huì)回到_dyld_start舌菜,此時(shí)register read讀一下寄存器, rax 已經(jīng)是main at main.m:15亦镶,然后循環(huán)完之后會(huì)jmpq跳到main函數(shù)里面日月。

這就是為什么調(diào)用順序是load -> Cxx -> main

最后注意一點(diǎn),main是寫定的函數(shù)缤骨,寫入內(nèi)存爱咬,讀取到dyld,所以main函數(shù)的名稱是不能改的绊起,改了就會(huì)報(bào)錯(cuò)

iOS 底層原理 文章匯總

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末精拟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子虱歪,更是在濱河造成了極大的恐慌蜂绎,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笋鄙,死亡現(xiàn)場(chǎng)離奇詭異师枣,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)萧落,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門践美,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人找岖,你說我怎么就攤上這事拨脉。” “怎么了宣增?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)矛缨。 經(jīng)常有香客問我爹脾,道長(zhǎng),這世上最難降的妖魔是什么箕昭? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任灵妨,我火速辦了婚禮,結(jié)果婚禮上落竹,老公的妹妹穿的比我還像新娘泌霍。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布朱转。 她就那樣靜靜地躺著蟹地,像睡著了一般。 火紅的嫁衣襯著肌膚如雪藤为。 梳的紋絲不亂的頭發(fā)上怪与,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音缅疟,去河邊找鬼分别。 笑死,一個(gè)胖子當(dāng)著我的面吹牛存淫,可吹牛的內(nèi)容都是我干的耘斩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼桅咆,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼括授!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起轧邪,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤刽脖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后忌愚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體曲管,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年硕糊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了院水。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡简十,死狀恐怖檬某,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情螟蝙,我是刑警寧澤恢恼,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站胰默,受9級(jí)特大地震影響场斑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜牵署,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一漏隐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奴迅,春花似錦青责、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扁耐。三九已至,卻和暖如春浩村,著一層夾襖步出監(jiān)牢的瞬間做葵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工心墅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留酿矢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓怎燥,卻偏偏與公主長(zhǎng)得像瘫筐,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子铐姚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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

  • 先來看一下iOS/Mac OS系統(tǒng)內(nèi)核架構(gòu) 需要注意:dyld是運(yùn)行在用戶態(tài)的進(jìn)程(下面解釋)隐绵。也就是說:App啟...
    溪浣雙鯉閱讀 1,912評(píng)論 2 9
  • 編譯流程 在開始分析dyld之前之众,我們先看下分析下可執(zhí)行文件的整個(gè)編譯流程: 如上圖所示,我們編寫的源文件依许,會(huì)在預(yù)...
    打碟的DJ閱讀 310評(píng)論 0 2
  • 前言 在編寫一個(gè)應(yīng)用程序時(shí)候棺禾,我們看到的入口函數(shù)都是main.m 里面的 main函數(shù),曾以為這是程序的入口峭跳,其實(shí)...
    猿人閱讀 482評(píng)論 0 4
  • dyld dyld(the dynamic link editor)是蘋果的動(dòng)態(tài)鏈接器蛀醉,是蘋果操作系統(tǒng)一個(gè)重要組成...
    Mjs閱讀 359評(píng)論 1 1
  • 前言 之前悬襟,我們研究了很多關(guān)于iOS底層相對(duì)零碎的知識(shí)。而iOS對(duì)用戶來說拯刁,最重要的就是每一個(gè)APP脊岳。今天,我們來...
    iOS小木偶閱讀 773評(píng)論 1 2