1、Mach-O简介

Mach-O是Mach object的缩写,是Mac/iOS上用于存储程序、库等的标准格式,在 xnu源码 中可以看到Mach-O格式的详细定义,属于Mach-O格式的文件类型有11种:

  1. #define MH_OBJECT 0x1 /* relocatable object file */
  2. #define MH_EXECUTE 0x2 /* demand paged executable file */
  3. #define MH_FVMLIB 0x3 /* fixed VM shared library file */
  4. #define MH_CORE 0x4 /* core file */
  5. #define MH_PRELOAD 0x5 /* preloaded executable file */
  6. #define MH_DYLIB 0x6 /* dynamically bound shared library */
  7. #define MH_DYLINKER 0x7 /* dynamic link editor */
  8. #define MH_BUNDLE 0x8 /* dynamically bound bundle file */
  9. #define MH_DYLIB_STUB 0x9 /* shared library stub for static */
  10. /* linking only, no section contents */
  11. #define MH_DSYM 0xa /* companion file with only debug */
  12. /* sections */
  13. #define MH_KEXT_BUNDLE 0xb /* x86_64 kexts */

源码位置:/EXTERNAL_HEADERS/mach-o/loader.h

2、常见的Mach-O文件类型

2.1、MH_OBJECT

2.1.1、目标文件 .o

目标文件源文件和可执行文件的中间产物,如下图所示:
image.png
可以手动创建一个C语言文件 test.c,通过命令行编译该文件 $ clang -c test.c 生成test.o文件,再通过file指令查看:
$ file test.o

  1. test.o: Mach-O 64-bit object x86_64

2.1.2、静态库文件 .a

静态库其实就是N个.o合并在一起,找一个静态库文件liby.a,通过file指令查看:
$ file liby.a

  1. liby.a: Mach-O universal binary with 3 architectures: [arm64:current ar archive random librarycurrent ar archive random library] [arm64e:current ar archive random librarycurrent ar archive random library] [x86_64:current ar archive random librarycurrent ar archive random library]
  2. liby.a (for architecture arm64): current ar archive random library
  3. liby.a (for architecture arm64e): current ar archive random library
  4. liby.a (for architecture x86_64): current ar archive random library

2.2、MH_EXECUTE:可执行文件

将test.c文件生成可执行文件test2:$ clang -o test2 test.c,再通过file指令查看test2:
$ file test2

  1. test2: Mach-O 64-bit executable x86_64

2.3、MH_DYLIB:动态文件

2.3.1、.dylib后缀文件

找到一个动态库文件,通过file指令查看:
$ file libgmalloc.dylib

  1. libgmalloc.dylib: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit dynamically linked shared library x86_64
  2. - Mach-O 64-bit dynamically linked shared library x86_64] [arm64e:Mach-O 64-bit dynamically linked shared library arm64e
  3. - Mach-O 64-bit dynamically linked shared library arm64e]
  4. libgmalloc.dylib (for architecture x86_64): Mach-O 64-bit dynamically linked shared library x86_64
  5. libgmalloc.dylib (for architecture arm64e): Mach-O 64-bit dynamically linked shared library arm64e

输出结果显示,.dylib文件是Mach-O格式文件,并且是一个动态库文件。

2.3.2、.framework/xx

找到XCTest.framework中的XCTest,通过 file指令查看:
$ file XCTest

  1. XCTest: Mach-O universal binary with 4 architectures: [arm_v7:Mach-O dynamically linked shared library arm_v7
  2. - Mach-O dynamically linked shared library arm_v7] [arm_v7s:Mach-O dynamically linked shared library arm_v7s
  3. - Mach-O dynamically linked shared library arm_v7s] [arm64:Mach-O 64-bit dynamically linked shared library arm64
  4. - Mach-O 64-bit dynamically linked shared library arm64] [arm64e:Mach-O 64-bit dynamically linked shared library arm64e
  5. - Mach-O 64-bit dynamically linked shared library arm64e]
  6. XCTest (for architecture armv7): Mach-O dynamically linked shared library arm_v7
  7. XCTest (for architecture armv7s): Mach-O dynamically linked shared library arm_v7s
  8. XCTest (for architecture arm64): Mach-O 64-bit dynamically linked shared library arm64
  9. XCTest (for architecture arm64e): Mach-O 64-bit dynamically linked shared library arm64e

可以看到也是Mach-O格式文件。

2.4、MH_DYLINKER:动态链接编辑器

所在路径:/usr/lib/dyld,通过file指令查看:
$ file dyld

  1. dyld: Mach-O universal binary with 3 architectures: [i386:Mach-O dynamic linker i386
  2. - Mach-O dynamic linker i386] [x86_64:Mach-O 64-bit dynamic linker x86_64
  3. - Mach-O 64-bit dynamic linker x86_64] [arm64e]
  4. dyld (for architecture i386): Mach-O dynamic linker i386
  5. dyld (for architecture x86_64): Mach-O 64-bit dynamic linker x86_64
  6. dyld (for architecture arm64e): Mach-O 64-bit dynamic linker arm64e

2.5、MH_DSYM:存储着二进制文件符号信息的文件

找到 dSYM(符号表)文件夹中和项目同名的文件(.dSYM/Contents/Resources/DWARF/xx),并通过file指令查看:
$ file iOSDemo

  1. iOSDemo: Mach-O 64-bit dSYM companion file arm64

也证明了符号表文件也是Mach-O格式的。

3、Universal Binary(通用二进制文件)

观察XCTest的文件描述信息可知,它是一个universal binary,支持4种架构,它就是一个通用二进制文件。
通用二进制文件有以下特点:

  • 同时适用多种架构的二进制文件,包含了多种不同架构的独立的二进制文件。
  • 因为需要存储多种架构的代码,通用二进制文件通常比单一平台二进制的程序要大。
  • 由于两种架构有共同的一些资源,所以并不会达到单一版本的两倍之多。
  • 由于执行过程中,只调用了一部分代码,运行起来也不需要额外的内存。
  • 因为文件比原来的要大,也被称为“胖二进制文件”(Fat Binary)

在Xcode的Build Settings中,可以通过修改Architectures配置来决定最终生成的二进制文件支持的架构。当使用Hopper查看通用二进制文件时,会提示FAT archive
image.png
可以通过lipo指令查看支持的架构:$ lipo -info XCTest

  1. Architectures in the fat file: XCTest are: armv7 armv7s arm64 arm64e

也可通过lipo命令把胖二进制文件转换成瘦二进制文件(指定一个支持架构):
$ lipo XCTest -thin armv7 -output XCTest_armv7
lipo也可以把两种架构的瘦二进制文件合并成一个胖二进制文件:
$ lipo -create XCTest_armv7 XCTest_arm64 -output XCTest

4、Mach-O的基本结构

根据 苹果官方文档 描述,Mach-O文件分为三个部分:Header信息、Load commands信息、Data信息。
image.png
Header:文件类型、目标架构类型等
Load commands:文件在虚拟内存中的逻辑、布局
Raw segment data:在Load commands中定义的Segment的原始数据
以暴走漫画的可执行文件WebToon为例,可以使用 otool 命令查看Mach-O特定部分和段的信息比如:
查看引用的动态库:$ otool -L WebToon
查看header信息:$ otool -h WebToon

更多操作可以在终端输入 $ otool 查看使用帮助

也可以使用GUI工具 MachOView 来查看,Mach-O的Header信息:
image.png
Mach-O的Load commands信息,包含内存分段信息告诉你每个段有多大,大概在哪里开始,还包含所加载的动态库:
image.png
Mach-O的Data中保存了内存中的真实信息,比如class-dump就是读取了Data中的头文件信息:
image.png

5、dyld和Mach-O

dyld(动态链接编辑器)它可以加载以下类型的Mach-O文件:
1、MH_EXECUTE(可执行文件)
2、MH_DYLIB(动态库)
3、MH_BUNDLE(动态绑定的文件)
App的可执行文件、动态库都是由dyld负责加载的,可以通过查看dyld源码/scr/dyld2.cpp 中进行验证:

  1. const mach_header* mh = (mach_header*)firstPages;
  2. switch ( mh->filetype ) {
  3. case MH_EXECUTE:
  4. case MH_DYLIB:
  5. case MH_BUNDLE:
  6. break;
  7. default:
  8. throw "mach-o, but wrong filetype";
  9. }