Mach-O文件結(jié)構(gòu)

主要內(nèi)容:

  1. 理解可執(zhí)行文件
  2. 理解Mach-O文件
  3. Mach-O文件結(jié)構(gòu)
  4. Mach Header
  5. Load Commands
  6. Data
  7. 理解大小端模式
  8. 理解通用二進(jìn)制文件

一、理解可執(zhí)行文件

1.可執(zhí)行文件
  1. 進(jìn)程,其實(shí)就是可執(zhí)行文件在內(nèi)存中加載得到的結(jié)果;
  2. 可執(zhí)行文件必須是操作系統(tǒng)可理解的格式,而且不同系統(tǒng)的可執(zhí)行文件的格式也是不同的;
2.不同平臺的可執(zhí)行文件
  • Linux:ELF文件
  • WindowsPE32/PE32+文件
  • OS和iOSMach-O(Mach Object)文件

二、理解Mach-O文件

作為iOSiPadOSmacOS平臺的可執(zhí)行文件格式,Mach-O文件涉及App啟動運(yùn)行、bitcode分析、 crash符號化等諸多多個功能:

1. Mach-O文件
  1. Mach-O文件是iOSiPadOSmacOS平臺的可執(zhí)行文件格式。對應(yīng)系統(tǒng)通過應(yīng)用二進(jìn)制接口(application binary interface,縮寫為ABI)來運(yùn)行該格式的文件;
  2. Mach-O格式用來替代BSD系統(tǒng)中的a.out格式,保存了在編譯和鏈接過程中產(chǎn)生的機(jī)器代碼和數(shù)據(jù),從而為靜態(tài)鏈接和動態(tài)鏈接的代碼提供單一文件格式。
  3. Mach-O提供了更強(qiáng)的擴(kuò)展性,以及更快的符號表信息訪問速度;
2.Mach-O格式的常見文件類型
  1. Executable:可執(zhí)行文件(.out .o);
  2. Dylib:動態(tài)鏈接庫;
  3. Bundle:不能被鏈接,只能在運(yùn)行時(shí)使用dlopen()加載;
  4. Image:包含ExecutableDylibBundle
  5. Framework:包含Dylib、資源文件和頭文件的文件夾;

三、Mach-O文件結(jié)構(gòu)

1.查看Mach-O的兩種方法
  1. 使用MachOView軟件,可直接查看MachO文件的結(jié)構(gòu);
  2. 使用終端命令objdump
2.查看Mach-O文件結(jié)構(gòu)

使用MachOView查看Mach-O,效果如下:

image

Mach-O文件中包含三個主要的部分:

  1. Header:頭部,描述CPU類型、文件類型、加載命令的條數(shù)大小等信息;
  2. Load Commands:加載命令,其條數(shù)和大小已經(jīng)在header中被提供;
  3. Data:數(shù)據(jù)段;

其他的信息還有:

  1. Dynamic Loader Info:動態(tài)庫加載信息
  2. Function Starts:入口函數(shù)
  3. Symbol Table:符號表
  4. Dynamic Symbol Table: 動態(tài)庫符號表
  5. String Table:字符串表

四、Mach Header(可執(zhí)行文件頭)

1.功能總結(jié)
  1. Header是鏈接器加載時(shí)最先讀取的內(nèi)容,因?yàn)樗鼪Q定了一些基礎(chǔ)架構(gòu)系統(tǒng)類型等信息;
  2. Header包含整個Mach-O文件的關(guān)鍵信息,如CPU類型文件類型加載命令的條數(shù)大小等信息,使得系統(tǒng)能夠迅速定位Mach-O文件的運(yùn)行環(huán)境;
  3. Header針對32位和64位架構(gòu)的CPU,分別對應(yīng)mach_headermach_header_64的結(jié)構(gòu)體;
2.源碼分析

Header被定義在loader.h文件中,具體代碼如下:

struct mach_header_64 {
    uint32_t    magic;          // 32位或者64位,系統(tǒng)內(nèi)核用來判斷是否是mach-o格式
    cpu_type_t  cputype;        // CPU架構(gòu)類型,比如ARM
    cpu_subtype_t   cpusubtype; // CPU的具體類型,例如arm64、armv7
    uint32_t    filetype;       // mach-o文件類型, 可執(zhí)行文件、目標(biāo)文件或者靜態(tài)庫和動態(tài)庫
    uint32_t    ncmds;          // LoadCommands加載命令的條數(shù)(加載命令緊跟header之后)
    uint32_t    sizeofcmds;     // 全部LoadCommands加載命令的大小
    uint32_t    flags;          // 標(biāo)志位標(biāo)識二進(jìn)制文件支持的功能,主要是和系統(tǒng)加載、鏈接有關(guān)
    uint32_t    reserved;       // 保留字段(相比于32位多出的字段)
    };

由于可執(zhí)行文件目標(biāo)文件或者靜態(tài)庫動態(tài)庫等都是Mach-O格式,所以才需要filetype來說明。常用的文件類型有以下幾種:

#define MH_OBJECT   0x1     /* 目標(biāo)文件*/
#define MH_EXECUTE  0x2     /* 可執(zhí)行文件*/
#define MH_DYLIB    0x6     /* 動態(tài)庫*/
#define MH_DYLINKER 0x7     /* 動態(tài)鏈接器*/
#define MH_DSYM     0xa     /* 存儲二進(jìn)制文件符號信息,用于debug分析*/
3.MachOView演示
image

五、分析Load Commands

1.功能總結(jié)
  1. Load Commands是加載命令的列表,用于描述Data在二進(jìn)制文件和虛擬內(nèi)存中的布局信息;
  2. Load Commands記錄了很多信息,例如動態(tài)鏈接器的位置、程序的入口、依賴庫的信息、代碼的位置、符號表的位置等;
  3. Load commands由內(nèi)核定義,不同版本的command數(shù)量不同,其條數(shù)和大小記錄在header中;
  4. Load commandstype是以LC_為前綴常量,譬如LC_SEGMENTLC_SYMTAB等;
2..代碼分析

Load Command被定義在loader.h文件中,具體代碼如下:

struct load_command {
    uint32_t cmd;       /* 加載命令的類型 */
    uint32_t cmdsize;   /* 加載命令的大小 */
};

每個Load Command都有獨(dú)立的結(jié)構(gòu),但是所有結(jié)構(gòu)的前兩個字段是固定的。比如LC_SEGMENT_64,這是一個讀取segmentsection有關(guān)命令,具體代碼如下:

struct segment_command_64 { /* for 64-bit architectures */
    uint32_t    cmd;          // 表示加載命令類型
    uint32_t    cmdsize;      // 表示加載命令大小(還包括了緊跟其后的nsects個section的大小)
    char        segname[16];  // 16個字節(jié)的段名字
    uint64_t    vmaddr;       // 段的虛擬內(nèi)存起始地址
    uint64_t    vmsize;       // 段的虛擬內(nèi)存大小
    uint64_t    fileoff;      // 段在文件中的偏移量
    uint64_t    filesize;     // 段在文件中的大小
    vm_prot_t   maxprot;      // 段頁面所需要的最高內(nèi)存保護(hù)(4 = r,2 = w,1 = x)
    vm_prot_t   initprot;     // 段頁面初始的內(nèi)存保護(hù)
    uint32_t    nsects;       // 段中section數(shù)量
    uint32_t    flags;        // 標(biāo)志位
};

六、Data

1.功能總結(jié)
  1. Data中存儲了實(shí)際的數(shù)據(jù)與代碼,主要包含方法、符號表、動態(tài)符號表、動態(tài)庫加載信息(重定向、符號綁定等)等;
  2. Data中的排布完全按照Load Command中的描述;
  3. DataSegment(段)和 Section (節(jié))的方式來組成,通常,Data擁有多個segment,每個segment可以有零到多個section節(jié);
  4. 不同的segment都有一段虛擬地址映射到進(jìn)程的地址空間;

幾乎所有的Mach-O文件都包含3segment

  1. __TEXT:代碼段,只讀可執(zhí)行,存儲函數(shù)的二進(jìn)制代碼(__text)常量字符串(__cstring)OC的類/方法名等信息
  2. __DATA:數(shù)據(jù)段, 可讀可寫,存儲OC的字符串(__cfstring),以及運(yùn)行時(shí)的元數(shù)據(jù):class/protocol/method,以及全局變量,靜態(tài)變量等;
  3. __LINKEDIT:只讀,存儲啟動App需要的信息,如 bind & rebase 的地址、函數(shù)的名稱和地址等信息;
2.源碼分析

Data區(qū)中,Section占了很大的比例,而且在Mach-O中集中體現(xiàn)在__TEXT__DATA兩段里。

Section被定義在loader.h文件中,具體代碼如下:

struct section_64 { /* for 64-bit architectures */
    char        sectname[16];   // 當(dāng)前section的名稱
    char        segname[16];    // section所在的segment名稱
    uint64_t    addr;       // 內(nèi)存中起始位置
    uint64_t    size;       // section大小
    uint32_t    offset;     // section的文件偏移
    uint32_t    align;    // 字節(jié)大小對齊
    uint32_t    reloff;     // 重定位入口的文件偏移
    uint32_t    nreloc;   // 重定位入口數(shù)量
    uint32_t    flags;      // 標(biāo)志,section的類型和屬性
    uint32_t    reserved1;  // 保留(用于偏移量或索引)
    uint32_t    reserved2;  // 保留(用于count或sizeof)
    uint32_t    reserved3;  // 保留
};

七、理解大小端模式

分析Mach-O文件時(shí),經(jīng)常會看到內(nèi)存地址相關(guān)的內(nèi)容,這里就涉及到了大小端模式的概念;

  1. 小端模式:數(shù)據(jù)的低字節(jié),保存在內(nèi)存的低地址;
  2. 大端模式:數(shù)據(jù)的低字節(jié),保存在內(nèi)存的高地址;

iOS設(shè)備的處理器是基于ARM架構(gòu)的,默認(rèn)是采用小端模式(低字節(jié)放低位)讀取數(shù)據(jù)的,而網(wǎng)絡(luò)和藍(lán)牙傳輸數(shù)據(jù)通常是用的大端模式(低字節(jié)放高位):

下面以unsigned int value = 0x12345678為例,分別看看在兩種字節(jié)序下其存儲情況,我們可以用unsigned char buf[4]來表示value

Little-Endian: 低地址存放低位,如下:
低地址 ------------------> 高地址
0x78  |  0x56  |  0x34  |  0x12

Big-Endian: 低地址存放高位,如下:
低地址 -----------------> 高地址
0x12  |  0x34  |  0x56  |  0x78
內(nèi)存地址 小端模式存放內(nèi)容 大端模式存放內(nèi)容
0x4000 0x78 0x12
0x4001 0x56 0x34
0x4002 0x34 0x56
0x4003 0x12 0x78

八、理解通用二進(jìn)制文件

1.基本概念
  1. 通用二進(jìn)制文件的存儲結(jié)構(gòu),是將多種架構(gòu)的Mach-O文件打包在一起,CPU在讀取該二進(jìn)制文件時(shí)可以自動檢測并選用合適的架構(gòu);
  2. 通用二進(jìn)制文件會同時(shí)存儲多種架構(gòu),所以比單一架構(gòu)的二進(jìn)制文件大很多,會占用大量的磁盤空間。但由于系統(tǒng)運(yùn)行時(shí)會自動選擇最合適的,不相關(guān)的架構(gòu)代碼,不會占用內(nèi)存空間,所以執(zhí)行效率提高了;
  3. 通用二進(jìn)制格式也被稱為胖二進(jìn)制格式;
2.通用二進(jìn)制格式分析

通用二進(jìn)制格式的定義在<mach-o/fat.h>中:

  1. 下載xnu后,依次在 xnu -> EXTERNAL_HEADERS ->mach-o中找到該文件。
  2. 通用二進(jìn)制文件有兩個重要結(jié)構(gòu)體:fat_headerfat_arch

兩個結(jié)構(gòu)體的定義如下:

/*
 - magic:可以讓系統(tǒng)內(nèi)核讀取該文件時(shí)知道是通用二進(jìn)制文件
 - nfat_arch:表明下面有多個fat_arch結(jié)構(gòu)體,即通用二進(jìn)制文件包含多少個Mach-O
 */
struct fat_header {
    uint32_t    magic;      /* FAT_MAGIC */
    uint32_t    nfat_arch;  /* number of structs that follow */
};

/*
 fat_arch是描述Mach-O
 - cputype 和 cpusubtype:說明Mach-O適用的平臺
 - offset(偏移)、size(大小)、align(頁對齊)描述了Mach-O二進(jìn)制位于通用二進(jìn)制文件的位置
 */
struct fat_arch {
    cpu_type_t  cputype;    /* cpu specifier (int) */
    cpu_subtype_t   cpusubtype; /* machine specifier (int) */
    uint32_t    offset;     /* file offset to this object file */
    uint32_t    size;       /* size of this object file */
    uint32_t    align;      /* alignment as a power of 2 */
};

參考鏈接

  1. xnu
  2. Mach-O官方源碼
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,743評論 2 370

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