前言
我们在前面学习了,编译最终会生成mach-o文件,那么mach-o结构是怎么的呢?
mach-o的定义
-
Mach-O其实是Mach Object文件格式的缩写,是mac以及iOS上可执行文件的格式, 类似于windows上的PE格式 (Portable Executable ), linux上的elf格式 (Executable and Linking Format)
-
记录编译后的可执行文件,对象代码,共享库,动态加载代码和内存转储的文件格式。不同于 xml 这样的文件,它只是二进制字节流,里面有不同的包含元信息的数据块,比如字节顺序,cpu 类型,块大小等。文件内容是不可以修改的,因为在 .app 目录中有个 _CodeSignature 的目录,里面包含了程序代码的签名,这个签名的作用就是保证签名后 .app 里的文件,包括资源文件,Mach-O 文件都不能够更改。
常见名词Mach-o
- Executable 可执行文件
- Dylib 动态库
- Bundle 无法被连接的动态库,只能通过dlopen()加载
- Image 指的是Executable,Dylib或者Bundle的一种,文中会多次使用Image这个名词。
- Framework 动态库(可以是静态库)和对应的头文件和资源文件的集合
mach-o的文件结构
Mach-O 的内容:- Mach-O Header:包含字节顺序,magic,cpu 类型,加载指令的数量等
- Load Commands:包含很多内容的表,包括区域的位置,符号表,动态符号表等。每个加载指令包含一个元信息,比如指令类型,名称,在二进制中的位置等。
- 原始段数据(Raw segment data):可以拥有多个段(segment),每个段可以拥有零个或多个区域(section)。每一个段(segment)都拥有一段虚拟地址映射到进程的地址空间。
Mach-O Header
我们打开machOView
在系统源码中也可以看到结构体Load Commands
Load commands是一张包含很多内容的表。 内容包括区域的位置、符号表、动态符号表等。
上图Load Commons中的大部分字段在下表中可以找到相关的含义符号 | 含义 |
---|---|
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 | 代码签名 |
其中LC_LOAD_DYLINKER和LC_LOAD_DYLIB
- LC_LOAD_DYLINKER 该字段标明我们的MachO是被谁加载进去的。 可以理解为LC_LOAD_DYLINKER指向的地址是微信APP加载小程序的引擎,而我们的MachO是小程序。在上图中可以看到我们的Demo1的LC_LOAD_DYLINKER指向的地址就是dyld。dyld确实是用来加载我们app的,在下面一节将会对dyld的源码进行分析,讲述dyld是如何对MachO进行加载的。
- LC_LOAD_DYLIB 该字段标记了所有动态库的地址,只有在LC_LOAD_DYLIB中有标记,我们MachO外部的动态库(如:Framework)才能被dyld正确的引用,否则dyld不会主动加载,这也是上篇文章,代码注入的关键所在!
Data
Data 通常是对象文件中最大的部分,包含Segement的具体数据,如静态C字符串,带参数/不带参数的OC方法,带参数/不带参数的C函数。
使用命令xcrun size -x -l -m main查看segment中的内容
显示为
Mac-mini-2:测试mac jxq$ xcrun size -x -l -m main
Segment __PAGEZERO: 0x100000000 (vmaddr 0x0 fileoff 0)
Segment __TEXT: 0x1000 (vmaddr 0x100000000 fileoff 0)
Section __text: 0x52 (addr 0x100000ef0 offset 3824)
Section __stubs: 0x18 (addr 0x100000f42 offset 3906)
Section __stub_helper: 0x38 (addr 0x100000f5c offset 3932)
Section __cstring: 0x1b (addr 0x100000f94 offset 3988)
Section __unwind_info: 0x48 (addr 0x100000fb0 offset 4016)
total 0x105
Segment __DATA: 0x1000 (vmaddr 0x100001000 fileoff 4096)
Section __nl_symbol_ptr: 0x8 (addr 0x100001000 offset 4096)
Section __got: 0x8 (addr 0x100001008 offset 4104)
Section __la_symbol_ptr: 0x20 (addr 0x100001010 offset 4112)
Section __cfstring: 0x20 (addr 0x100001030 offset 4144)
Section __objc_imageinfo: 0x8 (addr 0x100001050 offset 4176)
total 0x58
Segment __LINKEDIT: 0x1000 (vmaddr 0x100002000 fileoff 8192)
total 0x100003000
- Segment __PAGEZERO
- Segment __TEXT 包含可执行的代码,以只读和可执行方式映射
- Segment __DATA 包含了将会被更改的数据,以可读写和不可执行方式映射。
- Segment __LINKEDIT 包含了方法和变量的元数据,代码签名等信息。
总结
这里我们大概了解了mach-o文件的内容,那么mach-o是如何执行的呢? 我们将在下一篇文章,动态链接学习。