ios底层 mach-o文件分析

1,794 阅读4分钟

前言

我们在前面学习了,编译最终会生成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的文件结构

sd
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_DYLINKERLC_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是如何执行的呢? 我们将在下一篇文章,动态链接学习。