一、什么是Mach-O文件?
Mach-O 即 Mach Object,它是一种文件格式(Mac OS 二进制可执行文件)。
二、Mach-O 文件内容详解
- Mach-O 二进制文件由段(segment)组成,可通过
MachOView
查看。
- 一个segment由零个或者多个section组成,每个section里面会放置不同的数据或代码。
- segment需要页对齐(Mac OS 页大小4k,iOS 页大小16k),section不一定是页面对齐的。
- segment、section命名规则:
(1)segment 名称采用双下划线开头 +全字母大写(如 __DATA
);
(2)section 名称采用双下划线开头+全字母小写(如 __data
);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 常见segment的含义: __TEXT:包含可执行代码和其他只读数据 __DATA:包含可所有读写内容,如:全局变量,静态变量等 __LINKEDIT:加载程序(动态链接器)使用的元数据,如:符号,字符串和重定位的表项
常见__TEXT段中section的含义: __TEXT,__text: 只放置可执行代码(机器码) __TEXT,__cstring:字符串常量(最终构建产品时会删除其中重复项) __TEXT,__const:初始化的const变量
常见__DATA段中section的含义: __DATA,__data:初始化的可变变量,如C字符串和数据数组 __DATA,__la_symbol_ptr:懒符号指针 __DATA,__objc_selrefs:引用的objc方法 __DATA,__objc_classrefs:引用的objc类 __DATA,__objc_superrefs:引用的objc超类 __DATA,__objc_classlist:objc类列表 __DATA,__objc_nlclslist:objc非懒加载类列表 __DATA,__objc_catlist:objc分类列表 __DATA,__objc_nlcatlist:objc非懒加载分类列表 __DATA,__objc_protolist:objc协议列表 __DATA,__objc_protorefs:引用的objc协议
|
三、Mach-O 文件结构
一个Mach-O文件一般会包含三个主要区域:Header、Load commands、Data。

1、Header 结构
每个Mach-O文件的开始是一个head structure用来标识这个文件是一个Mach-O文件。header中还包含了其他基本文件类型、目标体系结构等信息。
我们先简单看看mach_header(本文以64位为准)结构,通过usr/include/mach-0/loader.h我们可以找到mach_header_64结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| struct mach_header_64 { uint32_t magic; /* mach 魔数标识符 */ cpu_type_t cputype; /* cpu 说明 */ cpu_subtype_t cpusubtype; /* cpu子类型说明 */ uint32_t filetype; /* 文件类型 */ uint32_t ncmds; /* 加载命令条数 */ uint32_t sizeofcmds; /* 所有load commands大小 */ uint32_t flags; /* 标志 */ uint32_t reserved; /* 保留字段 */ };
/* mach_header_64 的魔数字段的常量(64位架构) */ #define MH_MAGIC_64 0xfeedfacf /* 64位 mach 魔数 */ #define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */
|
(1)cputype
和 cpusubtype
字段常量值定义在 usr/include/mach/machine.h 中:
由于值太多,这里就不贴出来了,感兴趣的可以自己去看一下。下面举两个示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */
#define CPU_TYPE_ARM ((cpu_type_t) 12) #define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)
/* * ARM subtypes */ #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) /* ARMv7-A and ARMv7-R */
/* * ARM64 subtypes */ #define CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0)
cputype、cpusubtype示例:
cputype 12 //表示arm架构CPU cputype 9 // 表示armv7
cputype 16777228 //表示arm64架构CPU,(CPU_TYPE_ARM | CPU_ARCH_ABI64) = (12 | 0x01000000) = 0x0100000C, 0x0100000C转换成十进制等于16777228 cpusubtype 0 // 表示arm64通用架构
|
(2)filetype字段常见的文件类型常量
1 2 3 4 5 6 7
| #define MH_OBJECT 0x1 /* 源码编译后的文件,文件扩展名.o */ #define MH_EXECUTE 0x2 /* 二进制可执行文件 */ #define MH_DYLIB 0x6 /* 动态库 */ #define MH_DYLINKER 0x7 /* 动态链接器 */ #define MH_BUNDLE 0x8 /* 运行时加载的代码,文件扩展名.bundle */ #define MH_DSYM 0xa /* 带有调试信息(对应二进制文件的符号信息)的文件dsym */
|
(3)flags 字段常见的常量
1 2 3 4 5 6 7 8 9 10
| #define MH_NOUNDEFS 0x1 /* 目标文件中没有未定义的引用 */ #define MH_DYLDLINK 0x4 /* 对象文件是动态链接器的输入,无法再次静态编辑链接 */ #define MH_TWOLEVEL 0x80 /* 镜像使用两级命名空间 */ #define MH_WEAK_DEFINES 0x8000 /* 最终链接的镜像包含外部弱符号*/ #define MH_BINDS_TO_WEAK 0x10000 /* 最终链接的镜像使用弱符号 */ #define MH_PIE 0x200000 /* 设置此位时, 操作系统将会在随机地址处加载主可执行文件。仅用于MH_EXECUTE文件类型。 */
示例: flags 0x00218085 // 0x1+0x4+0x80+0x8000+0x10000+0x200000 = 0x00218085
|
2、Load commands 结构
1 2 3 4 5
| struct load_command { uint32_t cmd; /* 加载命令类型 */ uint32_t cmdsize; /* 命令大小 */ };
|
(1)加载命令的cmd字段常见的常量值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #define LC_REQ_DYLD 0x80000000
#define LC_SEGMENT 0x1 /* 文件映射的段 */ #define LC_SYMTAB 0x2 /* 链接编辑stab符号表信息 */ #define LC_SYMSEG 0x3 /* 链接编辑gdb符号表信息(已过时) */ #define LC_THREAD 0x4 /* 线程 */ #define LC_UNIXTHREAD 0x5 /* Unix线程(包括堆栈) */ #define LC_DYSYMTAB 0xb /* 动态链接器符号表信息 */ #define LC_LOAD_DYLIB 0xc /* 加载动态链接共享库 */ #define LC_LOAD_DYLINKER 0xe /* 加载动态链接器 */ #define LC_TWOLEVEL_HINTS 0x16 /* 两级命名空间查找提示 */ #define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD) /* 加载允许丢失的动态链接共享库(所有符号均弱导入)*/ #define LC_SEGMENT_64 0x19 /* 64位文件映射的段 */ #define LC_UUID 0x1b /* uuid */ #define. LC_RPATH (0x1c | LC_REQ_DYLD) /* 运行路径添加 */ #define LC_CODE_SIGNATURE 0x1d /* 本地代码签名 */ #define LC_LAZY_LOAD_DYLIB 0x20 /* 将dylib的加载延迟到首次使用 */ #define LC_ENCRYPTION_INFO 0x21 /* 加密段 information */ #define LC_DYLD_INFO 0x22 /* 压缩的dyld信息 */ #define LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD) /* 仅压缩的dyld信息 */ #define LC_VERSION_MIN_MACOSX 0x24 /* 适用于MacOSX的最低版本 */ #define LC_VERSION_MIN_IPHONEOS 0x25 /* 适用于iOS的最低版本 */ #define LC_FUNCTION_STARTS 0x26 /* 函数起始地址的压缩表 */ #define LC_MAIN (0x28|LC_REQ_DYLD) /* 代替 LC_UNIXTHREAD */ #define LC_DATA_IN_CODE 0x29 /* __text中的非指令表 */ #define LC_SOURCE_VERSION 0x2A /* 用于构建二进制文件的源代码版本 */ #define LC_ENCRYPTION_INFO_64 0x2C /* 64位加密段信息 */ #define LC_BUILD_VERSION 0x32 /* 适用于构建平台OS最低版本 */
|
具体的命令结构这里就不多做介绍了,请自行查看:usr/include/mach-0/loader.h
四、通用二进制可执行文件
五、常见应用场景
六、参考资料
完整优秀版请移步小专栏:
Mach-O文件初识
更多好文推荐,扫描下方的二维码,关注《iOS开发秘籍》公众号,免费解锁完整版

本文内容中部分来自网络,后续会持续更新完善。欢迎一起学习交流!
如需转载,请注明出处
Mach-O文件初识
↑
小火箭走一波,一瓶可乐鼓励下~~~^_^~~~~