Mach-O文件结构

Mach-O类型的文件

  • Mach-O是一种文件的格式; 是iOS/Mac OS上存储程序以及库的标准格式

    • Mach Object
  • Mach-O格式的文件

#define MH_OBJECT   0x1     /* 目标文件*/
#define MH_EXECUTE  0x2     /* 可执行文件 */
#define MH_FVMLIB   0x3     /* fixed VM shared library file */
#define MH_CORE     0x4     /*核心转储文件 */
#define MH_PRELOAD  0x5     /* preloaded executable file */
#define MH_DYLIB    0x6     /* dynamically bound shared library */
#define MH_DYLINKER 0x7     /* dynamic link editor */
#define MH_BUNDLE   0x8     /* dynamically bound bundle file */
#define MH_DYLIB_STUB   0x9     /* shared library stub for static */
                    /*  linking only, no section contents */
#define MH_DSYM     0xa     /* companion file with only debug */
                    /*  sections */
#define MH_KEXT_BUNDLE  0xb     /* x86_64 kexts */
  • 常见的Mach-O格式的文件

    • MH_OBJECT 目标文件

      • .o

      • .a/ .framework静态库

        • 静态库即多个.o文件存放在一起实现特定的功能
    • MH_EXECUTE 可执行文件

      • .app/MyApp

      • .out

    • MH_DYLIB 动态库

      • .framework/xxx

      • /dylib

    • MH_DYLINKER 动态链接器

      • usr/lib/dyld
    • MH_DSYM 存储二进制文件符号信息的文件

      • .dYSM/Contents/Resources/DWARF/MyApp
    image.png
  • 查看项目targetMach-O文件的类型

    • MH_EXECUTE类型
    image.png

Mach-O文件的基本结构

  • Mach-O包含三个主要区域

    • Header: 文件类型, 目标架构

    • Load command: 描述文件在虚拟内存中的逻辑与布局

    • Raw segment date: Load command中定义的原始数据

image.png
  • 使用otool查看Mach-O文件
➜ otool -h  DingTalk
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedface      12          9  0x00           2    79       7860 0x00218085
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777228          0  0x00           2    79       8672 0x00218085
  • 使用file查看Mach-O文件
# 该Mach-O文件类型是 executable  只有armv7指令集
➜  HomeDesign3D China.app file HomeDesign3D\ China
HomeDesign3D China: Mach-O executable arm_v7

# 该Mach-O文件 类型是 executable;  有armv7, arm64指令集是一个通用二进制文件
➜  DingTalk.app file DingTalk
DingTalk: Mach-O universal binary with 2 architectures: [arm_v7: Mach-O executable arm_v7] [arm64]
DingTalk (for architecture armv7):  Mach-O executable arm_v7
DingTalk (for architecture arm64):  Mach-O 64-bit executable arm64
  • 通用二进制文件

    • universal binary或者Fat binary

    • 含有多个不同架构的独立二进制文件; 故体积较大

    • 执行时, 只会选择一种架构的二进制文件

    image.png
  • 使用MachOView查看Mach-O文件

    • 以查看DingTalk为例
image.png
image.png
image.png
  • RAWRVA

    • RV: 虚拟地址

    • RAW: 文件偏移地址(物理地址)

    • RVA: 相对虚拟地址的偏移

Mach-O结构详解

Mach Header(arm64)
  • Magic Number : 魔数, 表示支持设备的CPU位数

    • oxFEEDFACE : 表示32位二进制

    • oxFEEDFACF : 表示64位二进制

  • cputypecpusubtype: CPU类型和子类型

  • filetype : Mach-O文件类型

  • ncmdssizeofcmds: 用于加载器的 加载命令的条数和大小

  • flags : 动态链接器dyld的标志

image.png

LC_SEGMENT / LC_SEGMENT_64段的详解

  • 常见段

    • __PAGEZERO: 空指针陷阱段

    • _TEXT: 程序代码段

    • __DATA: 程序数据段

    • __RODATA: read only程序只读数据段

    • __LINKEDIT: 链接器使用段

image.png
  • section段常见字段

    • Segment Name: 该Segment的名称, 用于load_segment

    • VM Address: 该段的虚拟物理地址

    • VM Size: 该段所需要分配的虚拟内存大小(字节)

    • File Offset: 该段在文件中的偏移量

    • File Size: 该段在文件中占据的字节数

    • Maximum VM Protection: 段的页面所需要的最高内存保护

      • ox1: x 执行

      • ox2: w 写

      • 0x4: r 读

    • Initial VM Protection: 段页面初始化的内存保护

    • Number of Sections: 段中section区的数量

    • Flags: 其他标志位

tips: 小结:

根据LC_SEGMENT命令 设置进程虚拟内存

对于每一个段, 将其内容从Mach-O文件加载到内存中

即从Mach-O文件中的偏移量为 File Offset处加载File Size字节内容到虚拟内存地址VM AddressVM Size字节空间内

image.png

段中区section详解

  • 常见区section

    • __text: 主程序代码

    • __stubs, __stub_helper: 用于动态链接的桩

    • __cstring: 程序中c语言字符串

    • __const: 常量

    • __RODATA,__objc_methname: OC方法名称

    • __RODATA,__objc_methntype: OC方法类型

    • __RODATA,__objc_classname: OC类名

    • __DATA,__objc_classlist: OC类列表

    • __DATA,__objc_protollist: OC原型列表

    • __DATA,__objc_imageinfo: OC镜像信息

    • __DATA,__objc_const: OC常量

    • __DATA,__objc_selfrefs: OC类自引用(self)

    • __DATA,__objc_superrefs: OC类超类引用(super)

    • __DATA,__objc_protolrefs: OC原型引用

    • __DATA, __bss: 没有初始化和初始化为0 的全局变量

image.png

Load Commmands加载命令中其他信息

  • LC_MAIN

    • 设置程序主线程入口地址栈大小
image.png
  • LC_CODE_SIGNATURE

    • 包含Mach-O文件的代码签名

    • 没有签名签名不正确, 该进程会被kill, 程序崩溃

image.png

Mach-O中动态库的加载

  • 动态库来源

    • 系统提供的动态库

    • 第三方动态库

  • 如图: DingTalk使用大量的系统动态库

    • Mach-O镜像中有很多对外部库以及符号的引用

    • 这些引用将在程序启动时, 由动态链接器 /usr/lib/dyld来执行符号绑定

image.png
  • 加载动态链接器

    • LC_LOAD_DYLINKER: 内核执行该命令时, 启动dyld
image.png
  • 获取符号表

    • LC_SYMTAB: 符号地址表

    • LC_DYSYMTAB: 动态符号地址表

  • 加载动态库

    • LC_LOAD_WEAK_DYLIB

    • LC_LOAD_DYLIB

动态库加载流程小结

1.0 首先启动dyld动态链接器; 内核根据LC_LOAD_DYLINKER启动/usr/lib/dyld

2.0 如果Mach-O文件中使用了外部定义的符号或函数, 则会在文本段__TEXT__stubs, __stub_helper区; 区内放着本地未被定义的符号; 编译器在编译源码时会创建对这些未定义符号桩区的调用

3.0 dyld运行时, 会在符号桩区调用地址上; 添加JMP 到 真实函数地址的指令

4.0 至于dyld怎么找到指定的动态库中指定的函数地址? 此时dyld将加载Load Command中的LC_LOAD_DYLIB命令

5.0 LC_LOAD_DYLIB(动态库), dyld将加载每一个指定的库且搜寻匹配的符号

6.0 当符号匹配时, 将在符号表(由dyld加载LC_SYMTAB, LC_DYSYMTAB获取)查找对应的函数/符号地址

推荐阅读更多精彩内容