Mach-o格式,是Mach操作系統內核(Mac、iOS系統的內核)主要支持的可執行文件格式。
用otool工具可以查看Mach-o的頭部,并參考Xcode自帶的關于Mach-o的頭文件仔細分析了一下,關于Mach-o的頭文件在/Applications/Xcode.app/Contents/Developer/Platforms/
iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/mach-o下面。
Note:下面的宏定義以MH_開頭,應該是mach_header的縮寫。
下面是Mach-o文件頭部的標注。
?? ~ otool -h Hello
Mach header
??????magic cputype cpusubtype? caps??? filetype ncmds sizeofcmds????? flags
?0xfeedfacf 16777223????????? 3? 0x80?????????? 2??? 18?????? 1616 0x00200085
從十六進制下面查看下(在VI下面執行 :%!xxd 命令 或者 用任何其他十六進制編輯器)
00000000: cffa edfe 0700 0001 0300 0080 0200 0000? ................
00000010: 1200 0000 5006 0000 8500 2000 0000 0000? ....P..... .....
疑惑點note: otool打印出來的cputype怎么是16777223呢?想了一下,明白是十進制表示的,0x10000070==16777223。
在下面的結構體聲明
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 */
};
#define MH_MAGIC??? 0xfeedface? /* the mach magic number */
#define MH_CIGAM??? 0xcefaedfe? /* NXSwapInt(MH_MAGIC) */
structmach_header_64 {
????uint32_t??? magic;????? /* mach-o 格式的標識符 */
????cpu_type_t? cputype;??? /* cpu區分符 */
????cpu_subtype_t?? cpusubtype; /* machine區分符 */
????uint32_t??? filetype;?? /* 文件類型 */
????uint32_t??? ncmds;????? /* 加載命令的個數 */
????uint32_t??? sizeofcmds; /* 加載命令的字節數 */
????uint32_t??? flags;????? /* 程序的標識位 */
????uint32_t??? reserved;?? /* 保留字段 */
};
#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */
64位的頭部結構體和32位的相比只多了一個reserved字段,然而這個字段是保留字段,目前還是0x0。等于說現在頭的結構都是一樣的。
第一個字段magic
magic字段就是個特征數值,沒有什么好說的,取自于下面的宏定義。
疑惑點note: 為什么是4個宏定義?查找資料并仔細觀察發現:CIGAM是MAGIC反過來的拼寫,寫反的意思是在大端序(big endian mode)(不了解大端序,請點擊wiki https://en.wikipedia.org/wiki/Endianness)環境下面使用。
第二&&三個字段CPU type 和 CPU subtype
定義了Mach-o所能支持的所有CPU類型,這兩個位置的標記表明了運行程序的平臺是啥,太長了就只把 x86和arm的copy出來記一下。示例中的就是CPU_TYPE_X86_64這個類型。
#define CPU_ARCH_ABI64? 0x01000000????? /* 64 bit ABI */
#define CPU_TYPE_X86??????? ((cpu_type_t) 7)
#define CPU_TYPE_I386?????? CPU_TYPE_X86???
#define CPU_TYPE_X86_64???? (CPU_TYPE_X86 | CPU_ARCH_ABI64)
#define CPU_TYPE_ARM??????? ((cpu_type_t) 12)
#define CPU_TYPE_ARM64????????? (CPU_TYPE_ARM | CPU_ARCH_ABI64)
第四個字段 filetype
表明Mach-o的文件類型,下面列出了所有的文件類型。每一種我在Github上面有收集的每一種filetype
#defineMH_OBJECT0x1
/* relocatable object file */
/* 一般后綴名為(.o),可重定位文件,編譯器的中間產物之一,靜態庫(后綴名是.a)就是這類文件的集合*/
#defineMH_EXECUTE0x2
/* demand paged executable file */
/* 示例中看到的可執行文件,最基礎的類型 */
#defineMH_FVMLIB0x3
/* fixed VM shared library file */
/* 后續補上,不知道在哪里見過 */
#defineMH_CORE0x4
/* core file */
/* 程序Crash之后產生的Core文件,現在默認的core dumps都是關閉的,可以通過 XXX 打開,一個Crash就生成了700多M的一個文件,我截取了他的前1600字節數據,收集起來。 */
#defineMH_PRELOAD0x5
/* preloaded executable file */
/* */
#defineMH_DYLIB0x6
/* dynamically bound shared library */
/* 一般后綴名為(.dylib),動態鏈接庫,很常見*/
#defineMH_DYLINKER0x7
/* dynamic link editor */
/* 動態庫的連接器,就這個文件/usr/lib/dyld ,Fat文件就用lipo提取一下,下面會講到*/
#defineMH_BUNDLE0x8
/* dynamically bound bundle file */
/* Xcode里面可以創建bundle的Target,編譯之后在bundle后綴名的文件夾下面可以找到*/
#defineMH_DYLIB_STUB0x9
/* shared library stub for static */
/* linking only, no section contents */
/* 后續補上,不知道在哪里見過 */
#defineMH_DSYM0xa
/* companion file with only debug */
/* sections */
/* 這個也很常見,Xcode->Product->Archive打包之后,里面就會生成一個叫DSYM后綴名的文件夾,查看包內容就可以找到它*/
#defineMH_KEXT_BUNDLE0xb
/* x86_64 kexts */
/* 內核功能擴展文件,常見與 /System/Library/Extensions 下面,這個自己可以打開瞅瞅*/
第五和六個字段 ncmds、sizeofcmds
ncmds 指的是加載命令(load commands)的數量
sizeofcmds 所有加載命令的大小。如果沒有設置的話,沒有辦法知道加載命令從什么時候結束啦。
第七個字段 flags
dyld加載時的標志位
#defineMH_NOUNDEFS0x1
/* the object file has no undefined references */
/* 目前沒有未定義的符號,不存在鏈接依賴*/
#defineMH_INCRLINK0x2
/* the object file is the output of an incremental link against a base file and can’t be link edited again */
/* 一個對基本文件的一個增量鏈接的輸出文件,無法被再次鏈接*/
#define MH_DYLDLINK0x4
/* the object file is input for the dynamic linker and can’t be staticly link edited again */
/* 動態連接器(dyld)的輸入文件,無法被再次靜態鏈接 */
#define MH_BINDATLOAD0x8
/* the object file’s undefined references are bound by the dynamic linker when loaded. */
#define MH_PREBOUND0x10
/* the file has its dynamic undefined references prebound. */
#define MH_SPLIT_SEGS0x20
/* the file has its read-only and read-write segments split */
#define MH_LAZY_INIT0x40
/* the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete) */
#define MH_TWOLEVEL0x80
/* the image is using two-level name space bindings */
/* 這個鏡像使用的是兩級名稱空間綁定*/
#define MH_FORCE_FLAT0x100
/* the executable is forcing all images to use flat name space bindings */
#define MH_NOMULTIDEFS0x200
/* this umbrella guarantees no multiple defintions of symbols in its sub-images so the two-level namespace hints can always be used. */
#define MH_NOFIXPREBINDING 0x400
/* do not have dyld notify the prebinding agent about this executable */
#define MH_PREBINDABLE 0x800
/* the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set. */
#define MH_ALLMODSBOUND 0x1000
/* indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set. */?
#define MH_SUBSECTIONS_VIA_SYMBOLS 0x2000
/* safe to divide up the sections into sub-sections via symbols for dead code stripping */
#define MH_CANONICAL 0x4000
/* the binary has been canonicalized via the unprebind operation */
#define MH_WEAK_DEFINES0x8000
/* the final linked image contains external weak symbols */
#define MH_BINDS_TO_WEAK 0x10000
/* the final linked image uses weak symbols */
#define MH_ALLOW_STACK_EXECUTION 0x20000
/* When this bit is set, all stacks in the task will be given stack execution privilege. Only used in MH_EXECUTE filetypes. */
/* 當這個位被設置了之后,所有在任務中的棧被賦予棧內可執行的權限。僅在filetype是MH_EXECUTE的時候使用*/
#define MH_ROOT_SAFE 0x40000
/* When this bit is set, the binary declares it is safe for use in processes with uid zero */
/* 當這個位被設置了之后,程序聲明它對root(進程UID是0的用戶)是安全的 */
#define MH_SETUID_SAFE 0x80000
/* When this bit is set, the binary declares it is safe for use in processes when issetugid() is true */
#define MH_NO_REEXPORTED_DYLIBS 0x100000
/* When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported */
#defineMH_PIE 0x200000
/* When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes. */
/* 當這個位被設置了之后,系統加載主可執行程序在隨機的地址空間,僅在filetype是MH_EXECUTE的時候使用 */
#defineMH_DEAD_STRIPPABLE_DYLIB 0x400000
/* Only for use on dylibs. When linking against a dylib that has this bit set, the static linker will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being referenced from the dylib. */
#define MH_HAS_TLV_DESCRIPTORS 0x800000
/* Contains a section of type S_THREAD_LOCAL_VARIABLES */
#define MH_NO_HEAP_EXECUTION 0x1000000
/* When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don’t require it. Only used in MH_EXECUTE filetypes. */
/* 當這個位被設置了之后,,僅在filetype是MH_EXECUTE的時候使用 */
#define MH_APP_EXTENSION_SAFE 0x02000000
/* The code was linked for use in an application extension. */
關于Fat文件
有時候使用下面命令會看到顯示兩個Mach頭部,如下面:
?? ~ otool -h AlipayWallet
Mach header
??????magic cputype cpusubtype? caps??? filetype ncmds sizeofcmds????? flags
?0xfeedface????? 12????????? 9? 0x00?????????? 2??? 72?????? 7280 0x00210085
Mach header
??????magic cputype cpusubtype? caps??? filetype ncmds sizeofcmds????? flags
?0xfeedfacf 16777228????????? 0? 0x00?????????? 2??? 72?????? 8088 0x00210085
上面介紹的是單一的Arch結構,這一種是Fat的,可以通過 lipo 進行拆解開,使用下面命令
1lipo AlipayWallet -thin armv7 -output AlipayWallet_v7
在中的Fat頭部聲明
#define FAT_MAGIC?? 0xcafebabe
#define FAT_CIGAM?? 0xbebafeca? /* NXSwapLong(FAT_MAGIC) */
structfat_header {
????uint32_t??? magic;????? /* FAT_MAGIC or FAT_MAGIC_64 */
????uint32_t??? nfat_arch;? /* number of structs that follow */
};
#define FAT_MAGIC_64??? 0xcafebabf
#define FAT_CIGAM_64??? 0xbfbafeca? /* NXSwapLong(FAT_MAGIC_64) */
Fat文件就是一個包裝盒,里面填了若干個針對不同架構的指令集,第一個參數magic就是Fat的標識,第二個參數就是里面包含了幾個架構的指令集。
在中對指令集包裝的申明
struct fat_arch {
????cpu_type_t? cputype;???
????cpu_subtype_t?? cpusubtype;
????uint32_t??? offset;???? /* 指令集在文件中的偏移量,也就是從哪點開始 */
????uint32_t??? size;?????? /* 指令集的大小 */
????uint32_t??? align;????? /* 必須是2的n次方 */
};
struct fat_arch_64 {
????cpu_type_t? cputype;??? /* cpu specifier (int) */
????cpu_subtype_t?? cpusubtype; /* machine specifier (int) */
????uint64_t??? offset;???? /* file offset to this object file */
????uint64_t??? size;?????? /* size of this object file */
????uint32_t??? align;????? /* alignment as a power of 2 */
????uint32_t??? reserved;?? /* reserved */
};
Note: 看完之后,應該明白了mach-o文件共有
8種magic number {(32位/64位) * (大端序/小端序) * (是否是Fat文件)},
11種filetype
26中flag