MachO文件
Mach-O其實是Mach Object文件格式的縮寫,是mac以及iOS上可執(zhí)行文件的格式, 類似于windows上的PE格式 (Portable Executable ), linux上的elf格式 (Executable and Linking Format)
MachO格式的常見文件
目標文件.o
庫文件.a .dylib Framework
可執(zhí)行文件
dyld
.dsym
File指令
通過 $file 文件路徑 查看文件類型。




從截圖中可以看出,MatchO有很多不同的類型,可以通過在Xcode上指定。

通用二進制文件
蘋果公司提出的一種程序代碼。能同時適用多種架構(gòu)的二進制文件
同一個程序包中同時為多種架構(gòu)提供最理想的性能。
因為需要儲存多種代碼,通用二進制應用程序通常比單一平臺二進制的程序要大。
但是 由于兩種架構(gòu)有共通的非執(zhí)行資源,所以并不會達到單一版本的兩倍之多。
而且由于執(zhí)行中只調(diào)用一部分代碼,運行起來也不需要額外的內(nèi)存。
在Xcode編譯可以指定生成哪些架構(gòu)的Match-O文件(Architectures和Valid Architectures交集)

lipo命令
使用lifo -info 可以查看MachO文件包含的架構(gòu)
使用lipo -create 合并多種架構(gòu)
$lipo -create MachO1 MachO2 -output 輸出文件路徑
MachO文件結(jié)構(gòu)

Mach-O 的組成結(jié)構(gòu)如圖所示包括:
Header 包含該二進制文件的一般信息
字節(jié)順序、架構(gòu)類型、加載指令的數(shù)量等。
使得可以快速確認一些信息,比如當前文件用于32位還是64位,對應的處理器是什么、文件類型是什么
與Mach-O對應的數(shù)據(jù)結(jié)構(gòu)都可以在/usr/include/mach-o/loader.h中找到
/*
* The 64-bit mach header appears at the very beginning of object files for
* 64-bit architectures.
*/
struct mach_header_64 {
uint32_t magic; /* mach magic 標識符 */
cpu_type_t cputype; /* CPU 類型標識符,同通用二進制格式中的定義 r */
cpu_subtype_t cpusubtype; /* CPU 子類型標識符,同通用二級制格式中的定義 */
uint32_t filetype; /* 文件類型 */
uint32_t ncmds; /* 加載器中加載命令的條數(shù) */
uint32_t sizeofcmds; /* 加載器中加載命令的總大小 */
uint32_t flags; /* dyld 的標志 */
Mach-O 支持多種類型文件,所以此處引入了 filetype 字段來標明,這些文件類型定義在 loader.h 文件存在。
#define MH_OBJECT 0x1 /* Target 文件:編譯器對源碼編譯后得到的中間結(jié)果 */
#define MH_EXECUTE 0x2 /* 可執(zhí)行二進制文件 */
#define MH_FVMLIB 0x3 /* VM 共享庫文件(還不清楚是什么東西) */
#define MH_CORE 0x4 /* Core 文件,一般在 App Crash 產(chǎn)生 */
#define MH_PRELOAD 0x5 /* preloaded executable file */
#define MH_DYLIB 0x6 /* 動態(tài)庫 */
#define MH_DYLINKER 0x7 /* 動態(tài)連接器 /usr/lib/dyld */
#define MH_BUNDLE 0x8 /* 非獨立的二進制文件,往往通過 gcc-bundle 生成 */
#define MH_DYLIB_STUB 0x9 /* 靜態(tài)鏈接文件(還不清楚是什么東西) */
#define MH_DSYM 0xa /* 符號文件以及調(diào)試信息,在解析堆棧符號中常用 */
#define MH_KEXT_BUNDLE 0xb /* x86_64 內(nèi)核擴展 */
Load commands 一張包含很多內(nèi)容的表
內(nèi)容包括區(qū)域的位置、符號表、動態(tài)符號表等。描述了文件中數(shù)據(jù)的具體組織結(jié)構(gòu),不同的數(shù)據(jù)類型使用不同的加載命令表示。
在Mach-O文件中,loadCommand是用于加載指令的,它的大小和數(shù)目在header中已經(jīng)被提供,在Mach.h下以loadCommand結(jié)構(gòu)體展示
struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};
該結(jié)構(gòu)體中有兩個成員,一個cmd提供該loadcommand的類型,cmdsize則表示command的大小.
loadCommands中記錄了很多信息,包括動態(tài)鏈接器(比如dyld)的位置,程序的入口地址(main),依賴庫的信息,代碼的位置.符號表的位置等等.

LC_SEGMENT_64(_PAGEZERO): 空指針陷阱段,這里是記錄的共享虛擬空間信息,它并不會占用實際的磁盤空間,只是一片虛擬內(nèi)存,這里記錄了它的位置和大小,這片空間一般用于置放空指針.
LC_SEGMENT_64(_TEXT): 只讀數(shù)據(jù)段,記錄了TEXT的起始位置和大小還有偏移值等信息,這些信息會告知具體的TEXT段在哪里.
LC_SEGMENT_64(_DATA):讀寫數(shù)據(jù)段,記錄了DATA段的起始位置和大小還有偏移值等信息,這些信息會告知具體的DATA段在哪里.
LC_SEGMENT_64(_LINKEDIT):鏈接器使用段,這里記錄了鏈接器(通常是dyld)需要的信息的位置.
LC_DYLD_INFO_ONLY:記錄具體的鏈接器需要的信息,比如重定向,懶加載,綁定等.
LC_SYMTAB:符號表的信息,記錄符號表的位置,偏移量,數(shù)據(jù)個數(shù)等,便于dyld使用LC_DYSYMTAB:符號表的額外信息,這些信息也會提供給dyld.
LC_LOAD_DYLINKER:該Mach-O使用的鏈接器信息,記錄了具體使用哪個鏈接器接管內(nèi)核后續(xù)的加載工作,以及鏈接器的位置信息,通常是dyld.
LC_UUID:Mach-O唯一標識符.
LC_VERSION_MIN_IPHONES:該Mach-O運行的最低系統(tǒng)版本.
LC_SOURCE_VERSION:源代碼版本信息.
LC_MAIN:入口地址.dyld會通過這個段去跳轉(zhuǎn)程序的主入口.
LC_ENCRYPTION_INFO_64:加密標識,標識了是否被加密,加密內(nèi)容的偏移及大小等.
LC_LOAD_DYLIB:依賴庫信息,dyld會通過這個段去加載動態(tài)庫,這個段標注了庫的位置以及版本等信息.LC_RPATH:@rpath的路徑信息.
LC_FUNCTION_STARTS:函數(shù)起始地址表.
LC_DATA_IN_CODE:代碼段非指令的表.
LC_CODE_SIGNATURE:代碼簽名信息.
DATA
Mach-O 的 Data 區(qū)域由 Segment 段和 Section 節(jié)組成。先來說 Segment 的組成,
#define SEG_PAGEZERO "__PAGEZERO" /* 當時 MH_EXECUTE 文件時,捕獲到空指針 */
#define SEG_TEXT "__TEXT" /* 代碼/只讀數(shù)據(jù)段 */
#define SEG_DATA "__DATA" /* 數(shù)據(jù)段 */
#define SEG_OBJC "__OBJC" /* Objective-C runtime 段 */
#define SEG_LINKEDIT "__LINKEDIT" /* 包含需要被動態(tài)鏈接器使用的符號和其他表,包括符號表、字符串表等 */
進而來看一下 Segment 的數(shù)據(jù)結(jié)構(gòu)具體是什么樣的
struct segment_command_64 {
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* section_64 結(jié)構(gòu)體所需要的空間 */
char segname[16]; /* segment 名字,上述宏中的定義 */
uint64_t vmaddr; /* 所描述段的虛擬內(nèi)存地址 */
uint64_t vmsize; /* 為當前段分配的虛擬內(nèi)存大小 */
uint64_t fileoff; /* 當前段在文件中的偏移量 */
uint64_t filesize; /* 當前段在文件中占用的字節(jié) */
vm_prot_t maxprot; /* 段所在頁所需要的最高內(nèi)存保護,用八進制表示 */
vm_prot_t initprot; /* 段所在頁原始內(nèi)存保護 */
uint32_t nsects; /* 段中 Section 數(shù)量 */
uint32_t flags; /* 標識符 */
};
部分的 Segment (主要指的 __TEXT 和 __DATA)可以進一步分解為 Section,下面給出 Section 具體的數(shù)據(jù)結(jié)構(gòu):
struct section_64 {
char sectname[16]; /* Section 名字 */
char segname[16]; /* Section 所在的 Segment 名稱 */
uint64_t addr; /* Section 所在的內(nèi)存地址 */
uint64_t size; /* Section 的大小 */
uint32_t offset; /* Section 所在的文件偏移 */
uint32_t align; /* Section 的內(nèi)存對齊邊界 (2 的次冪) */
uint32_t reloff; /* 重定位信息的文件偏移 */
uint32_t nreloc; /* 重定位條目的數(shù)目 */
uint32_t flags; /* 標志屬性 */
uint32_t reserved1; /* 保留字段1 (for offset or index) */
uint32_t reserved2; /* 保留字段2 (for count or sizeof) */
uint32_t reserved3; /* 保留字段3 */
};
以下列舉一些常見的 Section:
__text: 主程序代碼
__stubs, __stub_helper: 用于動態(tài)鏈接的樁
__cstring: 程序中c語言字符串
__const: 常量
__TEXT,__objc_methname:OC方法名稱
__TEXT__objc_methtype:OC方法類型
__TEXT__objc_classname:OC類名
__DATA,__objc_classlist:OC類列表
__DATA,__objc_protollist:OC原型列表
__DATA,__objc_imageinfo:OC鏡像信息
__DATA,__objc_const:OC常量
__DATA,__objc_selfrefs:OC類自引用(self)
__DATA,__objc_superrefs:OC類超類引用(super)
__DATA,__objc_protolrefs:OC原型引用
__DATA, __bss: 沒有初始化和初始化為0 的全局變量
Dynamic Loader Info:動態(tài)鏈接器所需要使用的信息(重定向,符號綁定,懶加載綁定等..)
后續(xù)的信息就是函數(shù)起始位置,符號表,字符表,代碼簽名等.