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