瞭解 Mach-O檔案
theme: smartblue
「這是我參與2022首次更文挑戰的第15天,活動詳情檢視:2022首次更文挑戰」。
Mach-O檔案
想要一個應用程式執行起來,那麼它的可執行檔案格式一定要被作業系統所理解。在Window
系統的可執行檔案是PE
,而在OS X
和iOS
中的可執行檔案是Mach-O
。
那麼Mach-O
是怎麼生成的呢?蘋果公司目前使用的編譯器是LLVM,在程式編譯時,編譯器會對每個檔案進行編譯,然後生成Mach-O
檔案,而後連結器會將專案中的多個 Mach-O
檔案合併成一個,最終的這個就是我們的可執行Mach-O
檔案.
那麼Mach-O 檔案裡面有哪些內容呢?其實主要還是資料和程式碼,其中資料是一些初始值的定義,程式碼就是一些是函式的定義。下面我們一起了解下Mach-O
檔案。
Mach-O檔案簡介
Mach-O 是Mach Object檔案格式的縮寫,是運用於mac以及iOS上;它是一種用於可執行檔案、目的碼、動態庫的檔案格式;
Mach-O檔案型別
- Executable:應用可執行的二進位制檔案
- Dylib Library:動態連結庫
- Static Library:靜態連結庫
- Bundle:不能被連結 Dylib,只能在執行使用dlopen()載入
- Relocatable Object File:可重定向檔案
Mach-O檔案結構
Mach-O
檔案主要由三部分組成:Header
、Load commands
、Data
Header
部分描述當前Mach-O檔案的基本資訊 (架構,是否Fat二進位制檔案,CUP型別等等);
Load commands
部分主要描述:1.Mach-O檔案中在虛擬記憶體中空間是如何分配,從哪個記憶體地址開始到哪個記憶體地址結束。 2.不同段在Mach-O檔案中的位置,大小分佈。
Data
部分是描述記憶體如何被分配的內容。包括__TEXT, __DATA等
Header
型別:區分32位、64位
結構:
// 32位
struct mach_header {
uint32_t magic; /* mach magic number identifier */
cpu_type_t cputype; /* cpu specifier */
cpu_subtype_t cpusubtype; /* machine specifier */
uint32_t filetype; /* type of file */
uint32_t ncmds; /* number of load commands */
uint32_t sizeofcmds; /* the size of all the load commands */
uint32_t flags; /* flags */
};
// 64位
struct mach_header_64 {
uint32_t magic; /* mach magic number identifier */
cpu_type_t cputype; /* cpu specifier */
cpu_subtype_t cpusubtype; /* machine specifier */
uint32_t filetype; /* type of file */
uint32_t ncmds; /* number of load commands */
uint32_t sizeofcmds; /* the size of all the load commands */
uint32_t flags; /* flags */
uint32_t reserved; /* reserved */
};
註釋:\
magic
:確定Mach-O檔案執行框架,如64位/32位\
cputype
:CPU型別,如arm\
cpusubtype
:對應CPU型別的具體型號\
filetype
:檔案型別\
ncmds
:載入命令條數\
sizeofcmds
:所有載入命令的大小\
flags
:保留欄位\
reserved
:標誌位
LoadCommand
- cmd:指令型別
- cmdsize: 指令長度
以下是load_command的結構:
struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};
command 指令型別說明
-
LC_SEGMENT/LC_SEGMENNT_64
:將對應段中的資料載入並對映到程序的記憶體空間 -
LC_SEGMENT_TEXT
:程式碼段,其中_stub_helper用於關聯函式bind/rebind -
LC_SEGMENT_DATA
:可讀/可寫的資料段,函式指標,其中_la_symbol_ptr動態函式個數,及相對動態符號表的偏移量 -
LC_SEGMENT_LINKEDIT
: :動態連結載入指令,支援動態連結dyld,該段長度覆蓋符號表等資料(計算連結時程式的基址),符號表,動態符號表,字串表段中定義的offset偏移量都是基於_LINKEDIT的vm_add -
LC_SYMTAB
:符號表資訊,解析函式名 -
LC_DYSYMTAB
:動態符號表資訊,地址值為動態函式相對符號表的索引,_la_symbol_ptr對應的cmd可以換算出第一個動態函式對應動態符號表的初始地址,其次儲存是連續,結構長度固定的,可以通過遍歷獲取所有動態函式的對應的符號表索引
Data
Data中就是由Segment組成的,每一個Segment
定義了一些Mach-O檔案的資料、地址和記憶體保護屬性,這些資料在動態連結器載入程式時被對映到了虛擬記憶體中。每個段都有不同的功能。
Segment
一般包含下列功能:
- __PAGEZERO: 空指標陷阱段,對映到虛擬記憶體空間的第一頁,用於捕捉對NULL指標的引用;
- __TEXT: 包含了執行程式碼以及其他只讀資料。 為了讓核心將它 直接從可執行檔案對映到共享記憶體, 靜態聯結器設定該段的虛擬記憶體許可權為不允許寫。當這個段被對映到記憶體後,可以被所有程序共享。(這主要用在frameworks, bundles和共享庫等程式中,也可以為同一個可執行檔案的多個程序拷貝使用)
- __DATA: 包含了程式資料,該段可寫;
- __LINKEDIT: 含有為動態連結庫使用的原始資料,比如符號,字串,重定位表條目等等。
一般Segment
又會按不同的功能劃分為幾個區(Section
),即段所有字母大小,加兩個下橫線作為字首,而區則為小寫,同樣加兩個下橫線作為字首,
Segment
結構體:
```
struct segment_command { / for 32-bit architectures /
uint32_t cmd; / LC_SEGMENT /
uint32_t cmdsize; / includes sizeof section structs /
char segname[16]; / segment name /
uint32_t vmaddr; / memory address of this segment /
uint32_t vmsize; / memory size of this segment /
uint32_t fileoff; / file offset of this segment /
uint32_t filesize; / amount to map from the file /
vm_prot_t maxprot; / maximum VM protection /
vm_prot_t initprot; / initial VM protection /
uint32_t nsects; / number of sections in segment /
uint32_t flags; / flags /
};
struct segment_command_64 { / for 64-bit architectures / uint32_t cmd; / LC_SEGMENT_64 / uint32_t cmdsize; / includes sizeof section_64 structs / char segname[16]; / segment name / uint64_t vmaddr; / memory address of this segment / uint64_t vmsize; / memory size of this segment / uint64_t fileoff; / file offset of this segment / uint64_t filesize; / amount to map from the file / vm_prot_t maxprot; / maximum VM protection / vm_prot_t initprot; / initial VM protection / uint32_t nsects; / number of sections in segment / uint32_t flags; / flags / }; ```
Section
結構體:
```
struct section { / for 32-bit architectures /
char sectname[16]; / name of this section /
char segname[16]; / segment this section goes in /
uint32_t addr; / memory address of this section /
uint32_t size; / size in bytes of this section /
uint32_t offset; / file offset of this section /
uint32_t align; / section alignment (power of 2) /
uint32_t reloff; / file offset of relocation entries /
uint32_t nreloc; / number of relocation entries /
uint32_t flags; / flags (section type and attributes)/
uint32_t reserved1; / reserved (for offset or index) /
uint32_t reserved2; / reserved (for count or sizeof) /
};
struct section_64 { / for 64-bit architectures / char sectname[16]; / name of this section / char segname[16]; / segment this section goes in / uint64_t addr; / memory address of this section / uint64_t size; / size in bytes of this section / uint32_t offset; / file offset of this section / uint32_t align; / section alignment (power of 2) / uint32_t reloff; / file offset of relocation entries / uint32_t nreloc; / number of relocation entries / uint32_t flags; / flags (section type and attributes)/ uint32_t reserved1; / reserved (for offset or index) / uint32_t reserved2; / reserved (for count or sizeof) / uint32_t reserved3; / reserved / }; ```
- LeetCode 初級演算法之陣列(上),看看你都學會了嗎?
- LeetCode 初級演算法之連結串列,看看你都學會了嗎?
- LeetCode 初級演算法之字串(上),看看你都學會了嗎?
- 純程式碼佈局,也可以一樣的簡潔
- UIStackView之一問一答
- 使用UIStackView來簡化iOS的介面佈局
- 夏天來了,iOS開發者們該如何減少App耗電?(上)
- 夏天來了,App開發者們如何看待手機發燙問題?
- 聊聊iOS中UITableView複用的那些事
- 曾經經典的微信打飛機遊戲還有人記得嗎?
- iOS 原生渲染與 Flutter 有什麼區別 (上)
- 瞭解 Mach-O檔案
- CocoaPods中podsepc檔案設定詳解
- iOS 原生渲染與 Flutter 有什麼區別 (下)
- 簡單瞭解 iOS CVPixelBuffer (上)
- 談談 iOS 包瘦身方案
- 播放器重構的探索之路
- 如何使用CocoaPods製作私有庫
- iOS 元件化方案