Mach-O¶
打开了一个App,可以理解在手机的操作系统上运行了一个App进程。
进程是特殊文件在内存中加载得到的结果,这种特殊文件必须是操作系统可理解的。
Mach-O 其实是Mach Object文件格式的缩写,是 mac 以及 iOS 上可执行文件的格式, 类似于 windows上的 PE 格式 (Portable Executable ), linux 上的 ElF 格式 (Executable and Linking Format) 。
但是Mach-O不仅仅是只是可执行文件的格式标准,还是macOS和iOS中一些其他文件标准的格式。
常见的Mach-O格式有下面几种可执行文件:
- 静态库、动态库文件(.a .dylib .framework等)
- dyld(动态连接器)
- .dsym文件(符号表)
- bundle资源文件
- 目标文件(编译输出的.0文件)
以下以可执行文件为重点去讲述Mach-O
可执行文件¶
开发的app,编译打包后,就会生成一个可执行的,Mach-O格式的二进制文件。
对于macOS和iOS中的App,其生成可执行二进制文件从支持的架构分为两种:
- 单架构的二进制文件
- 胖二进制文件
单架构的二进制文件就是运行于单个架构(如x86,ARM64等)的二进制文件。而胖二进制就是一个二进制文件里面包含多个单架构的二进制文件。
Mach-O内容¶
- VM Address : Virtual Memory Address, 段的虚拟内存地址,在内存中的位置
- VM Size : Virtual Memory Size, 段的虚拟内存大小, 占用多少内存
- File Offset : 段在文件中的偏移量
- File Size : 段在文件中的大小
- ASLR
以mac中的计算器App为例,用MachOView查看,看到这是一个Fat Binary类型的可执行文件。里面包含了两个Executable二进制文件,每一个Excutable里面的格式可以参照单个架构的Mach-O的格式。当我们点击app的时候,就会从中选合适的Excutable加载到内存中。
单架构的Excutable文件Mach-O文件格式的内部结构¶
Products文件夹下的.app
文件显示包内容,可执行文件在MachOView中整体如下
1、mach_header¶
文件头mach_header 位于 Mach-O 文件的头部,有以下作用:
- 表明该文件是 Mach-O 格式
- 标明文件类型,CPU 架构等信息,指定目标架构
- 标明load commands数量等
- 其他的文件属性信息
- 文件头信息影响后续的文件结构安排
2、load commands¶
加载的指令,依赖的库Foundation
load commands记录地址信息
load commands是一张包含很多内容的表。内容包括区域的位置、符号表、动态符号表等。 这一部分存储的是加载指令,指导操作系统该如何把可执行文件加载到内存中。有以下作用
- 指导data部分的数据应该怎么加载
- 指导dyld加载哪些库
- 指明符号表地址
- 其他
表头1 | 表头2 |
---|---|
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_LOAD_DYLIB(库名) | 依赖库的路径,包含三方库 |
LC_FUNCTION_STARTS | 函数起始地址表 |
LC_CODE_SIGNATURE | 代码签名 |
3、Text代码段¶
代码放在 _TEXT 段。
4、Data数据段¶
数据放在 _DATA 段。数据段除了全局变量、常量、自定义类还有很多东西,比如符号表。
里面会有符号表(间接符号表),访问NSLog。
data区主要就是负责代码和数据记录的。Mach-O 是以 Segment 这种结构来组织数据的,一个 Segment 可以包含 0 个或多个 Section。根据 Segment 是映射的哪一个 Load Command,Segment 中 section 就可以被解读为是是代码,常量或者一些其他的数据类型。在装载在内存中时,也是根据 Segment 做内存映射的。
这一部分存储的数据区域,包含程序运行的一切数据,表明数据加载到什么位置。部分数据如
- 代码数据
- 运行时的类数据
- 符号表,动态符号表
- 其他数据
对于Data区域的文档,可以按照Segment和section划分。Segment和section的关系有点像操作系统中的段和页的概念。
这里section可以简单为Segment的子节点,而Data中的数据是以Segment为单位划分的。常见的Segment有
- __PAGEZERO。一段随机大小,不可访问的空间
- __TEXT。代码区
- __DATA。数据区域
- __LINKEDIT;包含了方法和变量的元数据,代码签名等信息
5、Dynamic Symbol Table¶
Indirect Symbols间接符号¶
外部动态库符号 系统的符号。符号绑定。
6、Symbol Table(符号表)自定定义的¶
Symbols¶
总表:内部符号和外部符号都有