Mach-O、dyld

根本停不下來發表於2018-08-11

編寫的程式碼經過編譯連結後生成的可執行檔案就是 Mach-O 檔案。不過 Mach-O 不僅僅只是可執行檔案,它可以代表很多。

Mach-O 是 Mach object 的縮寫,是 Mac\iOS 上用於儲存程式、庫的標準格式。

Mach-O 檔案有哪些型別,檢視 xun 的原始碼可以看到:

Mach-O、dyld

如何證明一個檔案是 Mach-O 檔案:

假設我們編寫了一個 .c 檔案 test.c,在終端中對它進行編譯 $ clang -c test.c,這時就可以看到目錄中多出了一個 test.o 檔案,然後執行$ file test.o 就可看見結果:test.o: Mach-O 64-bit object x86_64。

常見的 Mach-O 檔案型別:

  • MH_OBJECT
    • 目標檔案(.o):原始檔與可執行檔案的中間產物 .c -> .o -> 可執行檔案。假設專案裡有三個 c 語言檔案,每個檔案分別編譯成一個目標檔案,三個目標檔案經過連線之後生成一個可執行檔案。
    • 靜態庫檔案(.a),靜態庫其實就是 N 個 .o 合併在一起
  • MH_EXECUTE: 可執行檔案
    • $ clang -o test2 test.c 執行這個命令,讓 test.c 生成可執行檔案 test2 。執行命令 $ file test2,即可驗證。
  • MH_DYLIB: 動態庫檔案
    • .dylib
    • .framework/xx
  • MH_DYLINKER: 動態連結編輯器
    • /usr/lib/dyld。這是一個專門用來載入我們動態庫檔案的東西,它也屬於 Mach-O 檔案。
  • MH_DSYM: 儲存二進位制檔案符號資訊的檔案
    • iOS 專案沒次打包的時候就會生成一個 dsym 檔案。除錯崩潰的時候會用到。

Mach-O 的基本結構

Mach-O 檔案都有共同的基本結構。
它主要由三部分組成:Header、Load commands、Data

Mach-O、dyld

  • Header
    • 檔案型別、目標架構型別等
  • Load commands
    • 描述檔案在虛擬記憶體中的邏輯結構、佈局(平時我們說的記憶體都是虛擬記憶體,比如堆空間、棧空間等等)
  • Raw segment data
    • 在 Load commands 中定義的 Segment 的原始資料

Load commands 僅僅是描述了結構與佈局,Raw segment data 說明了具體的資料。

簡單認識dyld(/usr/lib/dyld)

  • dyld 將可執行檔案載入進記憶體。程式碼編寫好後,編譯連結後生成了一個可以行檔案。要執行的話,就需要把它載入進記憶體,就是 dyld 將它載入進記憶體的。
  • dyld 將動態庫載入進記憶體。dyld 會先判斷動態庫共享快取中是否有這個動態庫,如果沒有的話,就載入;如果有的話就直接連結就

dyld用於載入以下型別的 Mach-O 檔案

  • MH_EXECUTE
  • MH_DYLIB
  • MH_BUNDLE

動態庫共享快取(dyld shared cache)

從 iOS3.1 開始,為了提高效能,絕大部分的系統動態庫都存放到了一個快取檔案中,路徑是:/System/Library/Caches/com.apple.dyld/dyld_shared_cache_armX

比如:MapKit.frameworks、UIKit.frameworks、CoreGraphics.frameworks 等等

動態庫共享快取非常明顯的好處就是:節省記憶體。

  • 假設現在有多個APP,APP1、APP2、APP3 它們都要用到 UIKit.frameworks。一種方式是把 UIKit.frameworks 分別載入到這三個 APP,另一種方式是把 UIKit.frameworks 放到 APP 記憶體之外的另一個地方,APP 們可以共享它,顯然這樣節省了記憶體。
  • 還有一點如果有多個動態庫 UIKit、MapKit 等等,蘋果還會把他們合併成一個檔案 dyld_shared_cache_armX,這樣可以把多個動態庫中一些通用的東西只用寫一份就可以了。

點選執行按鈕後,程式經歷的過程

預處理(.i) -> 彙編(.s、.asm) -> 編譯(.o、.obj) -> 連結(準確講叫做靜態連結) -> 可執行檔案

接下來要執行程式,比如點選 APP 圖示冷啟動程式 或者 Xcode 自動執行:

  • dyld 階段:dyld 會把可執行檔案載入到 APP 的記憶體中;還會載入動態庫,載入動態庫的時候會先判斷動態共享快取中有沒有這個動態庫,有的話就直接連結,沒有的話就先進行載入。它還會載入 MH_BUNDLE 檔案。簡單地概括就是它可以載入部分 Mach-O 檔案。
  • runtime 階段
  • main 階段

相關文章