iOS程序啟動->dyld加載->runtime初始化(初識)

程序的開始main函數(shù)與Coding生涯的開始hello World!.png

iOS開發(fā)中霸琴,main函數(shù)是我們熟知的程序啟動入口,但實際上并非真正意義上的入口昭伸,因為在我們運行程序梧乘,再到main方法被調用之間,程序已經(jīng)做了許許多多的事情庐杨,比如我們熟知的runtime的初始化就發(fā)生在main函數(shù)調用前选调,還有程序動態(tài)庫的加載鏈接也發(fā)生在這階段,本文主要對從程序啟動到main函數(shù)中發(fā)生的主要事情進行簡單介紹灵份。

其實簡單總結起來就是:

系統(tǒng)先讀取App的可執(zhí)行文件(Mach-O文件)仁堪,從里面獲得dyld的路徑,然后加載dyld填渠,dyld去初始化運行環(huán)境弦聂,開啟緩存策略,加載程序相關依賴庫(其中也包含我們的可執(zhí)行文件)氛什,并對這些庫進行鏈接莺葫,最后調用每個依賴庫的初始化方法,在這一步枪眉,runtime被初始化捺檬。當所有依賴庫的初始化后,輪到最后一位(程序可執(zhí)行文件)進行初始化贸铜,在這時runtime會對項目中所有類進行類結構初始化堡纬,然后調用所有的load方法聂受。最后dyld返回main函數(shù)地址,main函數(shù)被調用烤镐,我們便來到了熟悉的程序入口饺饭。

下面我們將結合代碼對整個過程進行分析:

dyld加載

這里先說下Mach-O文件。
Mach-O文件格式是 OS X 與 iOS 系統(tǒng)上的可執(zhí)行文件格式职车,像我們編譯過程產(chǎn)生的.O文件瘫俊,以及程序的可執(zhí)行文件,動態(tài)庫等都是Mach-O文件悴灵。它的結構如下:

mach-o文件.jpg

有如下幾個部分組成:

  1. Header:保存了一些基本信息扛芽,包括了該文件運行的平臺、文件類型积瞒、LoadCommands的個數(shù)等等川尖。
  2. LoadCommands:可以理解為加載命令,在加載Mach-O文件時會使用這里的數(shù)據(jù)來確定內(nèi)存的分布以及相關的加載命令茫孔。比如我們的main函數(shù)的加載地址叮喳,程序所需的dyld的文件路徑,以及相關依賴庫的文件路徑缰贝。
  3. Data: 這里包含了具體的代碼馍悟、數(shù)據(jù)等等。
    我們可以通過Mach-O文件查看器MachOView查看一個測試項目(這里放上地址)編譯后的可執(zhí)行文件內(nèi)容:
Mach-O文件內(nèi)容.png

這里可以看到剩晴,程序需要的dyld的路徑在LC_LOAD_DYLINKER命令里锣咒,一般都是在/usr/lib/dyld 路徑下。這里的LC_MAIN指的是程序main函數(shù)加載地址赞弥,下面還有寫LC_LOAD_DYLIB指向的都是程序依賴庫加載信息毅整,如果我們程序里使用到了AFNetworking,這里就會多一條名為LC_LOAD_DYLIB(AFNetworking)的命令绽左,如下圖

三方庫.png

這里可以看到一些我們比較常用的三方庫:AFNetworking,IQKeyboard等悼嫉。

系統(tǒng)加載程序可執(zhí)行文件后,通過分析文件來獲得dyld所在路徑來加載dyld拼窥,然后就將后面的事情甩給dyld了退敦。

從dyld開始

dyld: (the dynamic link editor)動態(tài)鏈接器歧斟,其源碼是開源的拣技。
ImageLoader: 用于輔助加載特定可執(zhí)行文件格式的類疹味,程序中對應實例可簡稱為image(如程序可執(zhí)行文件,F(xiàn)ramework庫房交,bundle文件)彻舰。

dyld接手后得做很多事情,主要負責初始化程序環(huán)境,將可執(zhí)行文件以及相應的依賴庫與插入庫加載進內(nèi)存生成對應的ImageLoader類的image(鏡像文件)對象刃唤,對這些image進行鏈接隔心,調用各image的初始化方法等等(注:這里多數(shù)事情都是遞歸的,從底向上的方法調用)尚胞,其中runtime也是在這個過程中被初始化硬霍,這些事情大多數(shù)在dyld:_mian方法中被發(fā)生,我們可以看段簡潔的代碼:

dyld::_main函數(shù)代碼.png

這里的_main函數(shù)是dyld的函數(shù)笼裳,并非我們程序里的main函數(shù)唯卖。

1.sMainExecutable = instantiateFromLoadedImage(....)與loadInsertedDylib(...)

這一步dyld將我們可執(zhí)行文件以及插入的lib加載進內(nèi)存,生成對應的image。
sMainExecutable對應著我們的可執(zhí)行文件躬柬,里面包含了我們項目中所有新建的類拜轨。
InsertDylib一些插入的庫,他們配置在全局的環(huán)境變量sEnv中允青,我們可以在項目中設置環(huán)境變量DYLD_PRINT_ENV為1來打印該sEnv的值橄碾。

環(huán)境變量設置.png

運行程序Log如下:

打印出插入庫的log.png

可以看到插入的庫為:libBacktraceRecording.dyliblibViewDebuggerSupport.
有時我們會在三方App的Mach-O文件中通過修改DYLD_INSERT_LIBRARIES的值來加入我們自己的動態(tài)庫,從而注入代碼颠锉,hook別人的App(相關資料)法牲。

2.link(sMainExecutable,...)和link(image,....)
對上面生成的Image進行進行鏈接琼掠。其主要做的事有對image進行l(wèi)oad(加載),rebase(基地址復位)拒垃,bind(外部符號綁定),我們可以查看源碼:

link方法.png
  • recursiveLoadLibraries(context, preflightOnly, loaderRPaths)
    遞歸加載所有依賴庫進內(nèi)存眉枕。

  • recursiveRebase(context)
    遞歸對自己以及依賴庫進行復基位操作恶复。在以前怜森,程序每次加載其在內(nèi)存中的堆椝偬簦基地址都是一樣的,這意味著你的方法副硅,變量等地址每次都一樣的姥宝,這使得程序很不安全,后面就出現(xiàn)ASLR(Address space layout randomization,地址空間配置隨機加載)恐疲,程序每次啟動后地址都會隨機變化腊满,這樣程序里所有的代碼地址都是錯的,需要重新對代碼地址進行計算修復才能正常訪問培己。

  • recursiveBind(context, forceLazysBound, neverUnload)
    對庫中所有nolazy的符號進行bind,一般的情況下多數(shù)符號都是lazybind的碳蛋,他們在第一次使用的時候才進行bind。

3.initializeMainExecutable()

這一步主要是調用所有image的Initalizer方法進行初始化省咨。這里的Initalizers方法并非名為Initalizers的方法肃弟,而是C++靜態(tài)對象初始化構造器,atribute((constructor))進行修飾的方法,在LmageLoader類中initializer函數(shù)指針所指向該初始化方法的地址笤受。

initiallizer函數(shù)指針.jpg

我們可以在程序中設置環(huán)境變量DYLD_PRINT_INITIALIZERS為1來打印出程序的各種依賴庫的initializer方法:

可以打印出調用了Initalizers的image的.png

運行程序穷缤,系統(tǒng)Log打印如下:

lnitializer調用log.png

(由于打印的比較長,這樣就截取開頭的log)可以看到每個依賴庫對應著一個初始化方法箩兽,名稱各有不同津肛。
這里最開始調用的libSystem.dylib的initializer function比較特殊,因為runtime初始化就在這一階段汗贫,而這個方法其實很簡單身坐,我們可以在這里看到init.c源碼,主要方法如下:

libSystem_initializer方法.jpg

其中l(wèi)ibdispatch_init里調用了到了runtime初始化方法_objc_init.我們可以落包、在程序中打個符號斷點來驗證:

_objc_init符號斷點.png

運行程序掀亥,然后斷點命中,我們來看下調用棧:

objc_init調用棧.png

這里可以看到_objc_init調用的順序妥色,先libSystem_initializer調用libdispatch_init再到_objc_init初始化runtime搪花。

runtime初始化后不會閑著,在_objc_init中注冊了幾個通知嘹害,從dyld這里接手了幾個活撮竿,其中包括負責初始化相應依賴庫里的類結構,調用依賴庫里所有的laod方法笔呀。

就拿sMainExcuatable來說幢踏,它的initializer方法是最后調用的,當initializer方法被調用前dyld會通知runtime進行類結構初始化许师,然后再通知調用load方法房蝉,這些目前還發(fā)生在main函數(shù)前,但由于lazy bind機制微渠,依賴庫多數(shù)都是在使用時才進行bind搭幻,所以這些依賴庫的類結構初始化都是發(fā)生在程序里第一次使用到該依賴庫時才進行的。

main函數(shù)被調用

當所有的依賴庫庫的lnitializer都調用完后逞盆,dyld::main函數(shù)會返回程序的main函數(shù)地址檀蹋,main函數(shù)被調用,從而代碼來到了我們熟悉的程序入口云芦。

main函數(shù)入口.png

結語

這里只是簡單了概括了從程序啟動->dyld加載依賴庫->runtime初始化->main 的過程俯逾。但這階段還有很多事情未講,如果想深入了解還得結合源碼來學習舅逸,這里我已經(jīng)將dyld和runtime源碼都放在這了桌肴,大家可直接下載,也可以從opensource-apple下載琉历。

再嘮嗑會

dyld源碼前前后后讀個大概懂坠七,花了我3個多禮拜的空閑時間,由于C和C++基礎并不是很好,所以特意跑回學校買了幾本書補了下基礎灼捂,不過讀源碼的這段時間還是挺累的离例。

為什么要去讀源碼,主要是看別人的文章時并不能很好解決我的某些疑問悉稠,而且只有真正去認識源碼宫蛆,去親身體會才能加深對它的理解。

學習的旅途雖然頗累的猛,但一路下來收獲頗多耀盗。加油!

前行路卦尊,路漫漫叛拷,一人一酒似逍遙。


一張圖.jpg

參考資料

1.Mach-O 可執(zhí)行文件
2.dylib動態(tài)庫加載過程分析
3.iOS 程序 main 函數(shù)之前發(fā)生了什么
4.今日頭條iOS客戶端啟動速度優(yōu)化
5.App 啟動時間:過去岂却,現(xiàn)在和未來
6.優(yōu)化 App 的啟動時間
7.dyld在hook方面的小東西

喜歡的話點個喜歡唄_

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末忿薇,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子躏哩,更是在濱河造成了極大的恐慌署浩,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扫尺,死亡現(xiàn)場離奇詭異筋栋,居然都是意外死亡,警方通過查閱死者的電腦和手機正驻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進店門弊攘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人姑曙,你說我怎么就攤上這事襟交。” “怎么了渣磷?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵婿着,是天一觀的道長。 經(jīng)常有香客問我醋界,道長,這世上最難降的妖魔是什么提完? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任形纺,我火速辦了婚禮,結果婚禮上徒欣,老公的妹妹穿的比我還像新娘逐样。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布脂新。 她就那樣靜靜地躺著挪捕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪争便。 梳的紋絲不亂的頭發(fā)上级零,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天,我揣著相機與錄音滞乙,去河邊找鬼奏纪。 笑死,一個胖子當著我的面吹牛斩启,可吹牛的內(nèi)容都是我干的序调。 我是一名探鬼主播,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼兔簇,長吁一口氣:“原來是場噩夢啊……” “哼发绢!你這毒婦竟也來了?” 一聲冷哼從身側響起垄琐,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤朴摊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后此虑,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體甚纲,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年朦前,在試婚紗的時候發(fā)現(xiàn)自己被綠了介杆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡韭寸,死狀恐怖春哨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情恩伺,我是刑警寧澤赴背,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站晶渠,受9級特大地震影響凰荚,放射性物質發(fā)生泄漏。R本人自食惡果不足惜褒脯,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一便瑟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧番川,春花似錦到涂、人聲如沸脊框。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽浇雹。三九已至,卻和暖如春屿讽,著一層夾襖步出監(jiān)牢的瞬間昭灵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工聂儒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留虎锚,地道東北人。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓衩婚,卻偏偏與公主長得像窜护,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子非春,可洞房花燭夜當晚...
    茶點故事閱讀 42,700評論 2 345

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