dyld 加載 Mach-O

原文出自【聽雲技術博客】:http://blog.tingyun.com/web/a...web

前言數據結構

最近看 ObjC的runtime 是怎麼實現 +load 鉤子函數的實現。進而引伸分析了 dyld 處理 Mach-O 的這部分機制。架構

1.簡單分析 Mach-O 在dyld 中是如何被加載到內存中的;函數

2.分析了 +load 的 特殊加載時機;spa

+ load3d

1.png

上圖的調用棧告訴咱們哪些函數被調用了。指針

dyld 是Apple 的動態連接器;在 xnu 內核爲程序啓動作好準備後,就會將 PC 控制權交給 dyld 負責剩下的工做 (dyld 是運行在 用戶態的, 這裏由 內核態 切到了用戶態)。blog

每當有新的鏡像加載以後,都會執行 load-images 方法進行回調,這裏的回調是在整個ObjC runtime 初始化時 -objc-init 註冊的 :繼承

二.png

有新的鏡像被 map 到 runtime 時,調用 load-images 方法,並傳入最新鏡像的信息列表 infoList:遞歸

三.png

這裏的鏡像就是 一些 System framework 的二進制。

進入 下圖函數 load-images-nolock 查找 load 函數

四.png

調用 prepare-load-methods 對 load 方法的調用進行準備(將須要調用 load 方法的類添加到一個列表中)

五.png

調用 -getObjc2NonlazyClassList 獲取全部的類的列表以後,會經過 remapClass 獲取類對應的指針,而後調用 schedule-class-load 遞歸地 將當前類和沒有調用 + load 父類進入列表。

六.png

在執行 add-class-to-loadable-list(cls) 將當前類加入加載列表以前,會先把父類加入待加載的列表,保證父類在子類前調用 load 方法。

在將鏡像加載到運行時、對 load 方法的準備就緒,執行 call-load-methods,開始調用 load 方法:

七.png

其中 call-class-loads 會從一個待加載的類列表 loadable-classes 中尋找對應的類,而後找到 @selector(load) 的實現並執行。

八.png

分析到這裏,已經能得知 load 函數是如何被調用的。

九.png

接下來分析 dyld 這部分怎麼加載鏡像的

1.1 數據結構

mach-o 文件頭 操做。

1.png

1.2 ImageLoader

2.png

每個加載的 Mach-O 文件都會存在這樣一個ImageLoader 的 實例,上圖能夠看出 這裏ImageLoader是一個抽象類,每一種具體的Mach-O 文件都會繼承 ImageLoader類, 繼承關係 以下圖:

16.png

在加載時會根據Mach-O的格式不一樣選擇生成不用的實例。

1.3 -main

聽雲Blog參考圖片

4.png

在調用-main 函數以後,作了一下幾件事情:

選擇運行環境(iOS 模擬器)

初始化數據、設置全局變量、上下文信息

檢查文件是否Restricted

走完這些流程,就會調用 instantiateFromLoadedImage 函數,開始加載Mach-O 而且實例化 爲 ImageLoader。

1.4 instantiateFromLoadedImage

5.png

這個函數作了三件事情:

檢查Mach-O 文件是否合法

初始化 ImageLoader 實例

調用addImage 函數添加 初始化後的實例到管理模塊中

1.5 isCompatibleMachO

6.png

Mach-O 文件的合法性檢查:

mach-header 中的 cputype與當前運行的CPU 版本是否支持

mach-header 中的 subtype 在該CPU 架構下的全部版本均可以支持

cputype 就是CPU 平臺, x86,ARM ,POWERPC 等, 而subtype 就是同一個平臺下的不一樣版本, 例如:arm7,arm7.

1.6 ImageLoaderMachO: : instantiateMainExecutable

7.png

該函數主要經過 sniffLoadCommands 函數來判斷 Mach-O 文件是不是壓縮過的,而後分別 選擇不一樣的 子類實例化。

17.png

1.7 sniffLoadCommands

這個函數主要作兩件事情

判斷Mach-O文件是classic的仍是compressed的。

獲取mach-O文件的segment的數量。

7-1.png

8.PNG

9.PNG

10.PNG

1.8 ImageLoaderMachOClassic: :instantiateMainExecutable

classic 與 compressed 的初始化大同小異,先分析Classic 的實現

11.png

能夠看到加載的核心代碼 還在 instantiateStart 函數中

1.9 instantiateStart

12.png

這裏仍然沒有出現加載的核心代碼,只是根據以前得到的數據申請分配了內存,並計算 segments的 指針。 ImageLoaderMachOClassic 的構造函數纔是加載 的核心邏輯。

2.0 ImageLoaderMachOClassic

13.png

14.png

根據Mach-O 文件 segments 將數據加載到 內存中, 任何返回 調用 addImage 函數。

2.1 addImage

15.png

這個函數只是作了數據更新

將image 添加到管理容器中

更新了內存分佈的信息

end

整個加載過程基本分爲三個步驟:

合法加測

解析Mach-O文件頭信息,將segments 的具體信息 構建到image 的實例中

添加image 到管理容器

根據 dyld的源代碼的粗略分析, 更多信息須要分析 xnu 內核代碼。

參考

ObjC runtime 源代碼

dyld 源代碼

《Mac OSX and iOS Internals》

相關文章
相關標籤/搜索