Mach-O
Mach-O(Mach Object)是macOS、iOS、iPadOS存储程序和库的文件格式。对应系统通过应用二进制接口(application binary interface,缩写为 ABI)来运行该格式的文件。
Mach-O格式用来替代BSD系统的a.out格式。Mach-O文件格式保存了在编译过程和链接过程中产生的机器代码和数据,从而为静态链接和动态链接的代码提供了单一文件格式。
Mach-O格式常见文件
- 目标文件
.o
- 库文件
.a
.dylib
Framework
- 可执行文件
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)
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END