Mach-O
Mach-O(Mach Object)是macOS、iOS、iPadOS存储程序和库的文件格式。对应系统通过应用二进制接口(application binary interface,缩写为 ABI)来运行该格式的文件。
Mach-O格式用来替代BSD系统的a.out格式。Mach-O文件格式保存了在编译过程和链接过程中产生的机器代码和数据,从而为静态链接和动态链接的代码提供了单一文件格式。
Mach-O格式常见文件
- 目标文件
.o - 库文件
.a.dylibFramework
- 可执行文件
dyld.dsym可在终端通过file命令查看文件类型可看到当前
Demo文件是一个Mach-O类型的64位x86_64架构的可执行文件
Mach-O文件结构
在MachOView工具上查看一个Mach-O文件
能够看到Mach-O文件格式由Header、Load Commands、__TEXT代码、__DATA代码、符号表和以及一些其它信息构成。dyld会根据Load Commands保存的信息找到具体的代码
Mach-O可理解为文件配置加上二进制代码,即:
Mach-O = 文件配置 + 二进制代码
用苹果官方图概括即:
Mach-O的组成结构如图所示包括了:
Header包含该⼆进制⽂件的⼀般信息- 字节顺序、架构类型、加载指令的数量量等
- 使得可以快速确认⼀些信息,⽐如当前⽂件⽤于32位还是64位,对应的处理器是什么、文件类型是什么
Load commands⼀张包含很多内容的表- 内容包括区域的位置、符号表、动态符号表等
Data通常是对象文件中最大的部分 -包含Segement的具体数据
Header
64位的Header结构
struct mach_header_64 {
uint32_t magic; /* 魔术,快速定位属于64还是32位 */
cpu_type_t cputype; /* cpu类型,如ARM */
cpu_subtype_t cpusubtype; /* cpu具体类型,如arm64/armv7 */
uint32_t filetype; /* 文件类型 */
uint32_t ncmds; /*loadCommands数量*/
uint32_t sizeofcmds; /*loadCommands大小 */
uint32_t flags; /* 标志位标识二进制文件支持功能。主要和系统加载、连接有关 */
uint32_t reserved; /* reserved */
};
在MachOView工具上查看Header
使用objdump --macho -private-header命令查看Mach-O文件,输出对应Header数据结构,与上面对应
LoadCommands
64位的load_command结构
struct segment_command_64 { /* for 64-bit architectures */
uint32_t cmd; /* command的类型 LC_SEGMENT_64 */
uint32_t cmdsize; /* section_64大小 */
char segname[16]; /* 段名称 */
uint64_t vmaddr; /* 段的虚拟内存地址 */
uint64_t vmsize; /* 段的虚拟内存大小 */
uint64_t fileoff; /* 段的文件偏移量 */
uint64_t filesize; /* 段在文件中的大小 */
vm_prot_t maxprot; /* 最大的虚拟机保护 */
vm_prot_t initprot; /* 最初的虚拟保护 */
uint32_t nsects; /* 段中的section数 */
uint32_t flags; /* 标记 */
};
其它的一些load_commands:
| LC_SEGMENT_64 | 将⽂件中(32位或64位)的段映射到进程地址空间中 |
| LC_DYLD_INFO_ONLY | 动态链接相关信息 |
| LC_SYMTAB | 符号地址 |
| LC_DYSYMTAB | 动态符号表地址 |
| LC_LOAD_DYLINKER | 使⽤谁加载,我们使用dyld |
| LC_UUID | ⽂件的UUID |
| LC_VERSION_MIN_MACOSX | 支持最低的操作系统版本 |
| LC_SOURCE_VERSION | 源代码版本 |
| LC_MAIN | 设置程序主线程的⼊口地址和栈⼤小 |
| LC_ENCRYPTION_INFO_64 | 获取加密信息 |
| LC_LOAD_DYLIB | 依赖库的路径,包含三方库 |
| LC_FUNCTION_STARTS | 函数起始地址表 |
| LC_DATA_IN_CODE | 定义在代码段内的非指令的表 |
| LC_CODE_SIGNATURE | 代码签名 |
Data
存放数据:代码,字符常量,类,方法等
- 代码段(__TEXT)
代码段开始地址是0,所以读内存当中的MachO开始的位置从代码段开始读的
- 数据段(__DATA)