认识Mach-O文件

/ 0评 / 0

什么是 Mach-O

Mach-O 是 Mach object 的缩写,是 iOS/Mac 上用于存储程序、库的标准格式。

可执行文件是 Mach-O 文件的一种类型,不能说 Mach-O 文件等价于可执行文件。

在 xnu 源码中,可以查看 Mach-O 格式的详细定义。在源码的 EXTERNAL_HEADERS/mach-o/loader.h 文件中,我们可以看到 Mach-O 格式的所有文件类型:

#define    MH_OBJECT    0x1        /* relocatable object file */
#define    MH_EXECUTE    0x2        /* demand paged executable file */
#define    MH_FVMLIB    0x3        /* fixed VM shared library file */
#define    MH_CORE        0x4        /* core file */
#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 */
#define    MH_FILESET    0xc        /* set of mach-o's */

xnu是苹果iOS/MacOS等操作系统的内核,下源码地址:https://opensource.apple.com/tarballs/xnu/

常见的 Mach-O 文件类型:

类型 描述
MH_OBJECT 源文件编译后的目标文件(.o),静态库文件(.a),静态库文件其实就是多个 .o 文件合并在一起
MH_EXECUTE 可执行文件,iOS里在 xx.app/xx ,Mac则在 xx.app/Contents/MacOS/xx
MH_DYLIB 动态库文件,.dylib 、xx.framework/xx
MH_DYLINKER 动态链接编辑器,也就是 /usr/lib/dylb 工具
MH_DSYM 存储着二进制文件符号信息的文件,xx.dSYM/Contents/Resources/DWARF/xx ,常用于分析APP的崩溃信息

Mach-O文件的基本结构

Mach-O由3个部分组成

名称 描述
Header 包含文件类型,目标架构类型等
Load commands 描述文件在虚拟内存中的逻辑结构和布局,相当于一份目录索引
Raw segment data 存放了所有的原始数据,而Load commands相当于Raw segment data的索引目录

窥探Mach-O的结构

命令行工具

file 命令查看 Mach-O 文件的基本信息:

file Dagou
Dagou: Mach-O 64-bit executable arm64

otool 命令查看 Mach-O 特定部分和段的内容:

otool -h 文件路径  # 查看Mach-O文件的header信息
otool -l 文件路径  # 查看Mach-O文件的load commands信息

lipo 命令用来处理多架构 Mach-O 文件:

lipo -info 文件路径  # 查看架构信息
lipo 文件路径 -thin 架构类型 -output 输出文件路径  # 导出某种类型的架构
lipo -create 文件路径1 文件路径2 -output 输出文件路径  # 合并多种架构类型

GUI工具

使用 MachOView 可视化查看 Mach-O 文件的结构,下载地址:https://github.com/mythkiven/MachOView

Universal Binary(通用二进制文件)

同时适用于多种架构的二进制文件,它包含了多种不同架构的独立的二进制文件,它有以下特点:

1.因为需要存储多种架构的二进制,所以通用二进制文件要比单架构二进制文件更大。

2.因为两种架构之间可以共用一些资源,所以两种架构的通用二进制文件大小不会达到单一架构版本的2倍。

3.运行过程中只会调用其中的部分代码,所以运行起来不会占用额外的内存。

4.通用二进制文件通常也被称为“胖二进制文件(Fat binary)”。

iOS开发中的编译链接

在iOS开发中,有很多功能都是现成可用的,比如 UIKit 框架、GUI 框架、I/O、网络框架等等,这些库都是通过链接器链接到 Mach-O 文件中的。

静态库:静态库是编译时链接的库,需要链接进 Mach-O 文件中,如果需要更新就必须重新编译一次,无法做到动态加载和更新。

动态库:动态库是运行时链接的库,Mach-O 是文件编译之后的产物,动态库并没有参与 Mach-O 文件的编译和链接。所以 Mach-O 文件中没有包含动态库的符号定义,也就是说这些符号会直接显示未定义,但是他们的名字和对应库的路径会被记录下来。在运行时通过 dlopendlsym 导入动态库时,会根据记录的路径找到对应的库,再通过记录的名字符号找到绑定的地址。

编译:将 OC 代码编译成汇编代码,汇编代码编译成机器代码。

反编译:在同一种架构平台下,每一条汇编指令都有与之对应的唯一的机器指令,所以可以把机器代码反编译成汇编代码,但汇编代码没办法反编译成 OC 代码。

勾选 Xcode - Debug - Debug Workflow - Always Show Disassembly,断点则可看到汇编代码。

动态库共享缓存:iOS 系统的动态库的 Mach-O 文件存放在动态库共享缓存(dyld shared cache)里,比如 UIKit、MapKit 等等库。从 iOS3.1 开始,为了提高性能,绝大部分系统动态库文件都打包存放到了一个缓存文件中。缓存文件路径:/System/Library/Caches/com.apple.dyld/dyld_shared_cache_armXXX ,XXX 代表 ARM 处理器指令集架构。

指令集(CPU指令架构的集合):

指令集 机型
armv6 iPhone、iPhone3G、iPod Touch、iPod Touch2
armv7 iPhone3GS、iPhone4、iPhone4S、iPad、iPad2、iPad3(The New iPad)、iPad mini、iPod Touch3G、iPod Touch4、iPod Touch5
armv7s iPhone5、iPhone5C、iPad4
arm64 iPhone5S、iPhone6、iPhone6 Plus、iPhone6S、iPhone6S Plus、iPhoneSE、iPhone7、iPhone7 Plus、iPhone8、iPhone8 Plus、iPhoneX、iPad5、iPad Air、iPad Air2、iPad Pro、iPad Pro2、iPad mini with Retina display、iPad mini3、iPad mini4、iPod Touch6 等等
arm64e iPhoneXS、iPhoneXR、iPhoneXS Max 等等

动态库共享缓存一个非常明显的好处是节省内存,现在 idaHopper 反编译工具都可以识别动态库共享缓存。
所有指令集原则上都是向下兼容的,也就是 arm64 指令集架构原则上支持以上所有设备。

dyld

dyld动态加载器(又叫动态链接编辑器),是 iOS 中用来加载可执行文件、动态库的工具,其实它本身也是一个 Mach-O 文件。它可以加载 MH_EXECUTEMH_DYLIBMH_BUNDLE 三种类型的 Mach-O 文件。

dyld 位置 /usr/lib/dyld ,源码地址:https://opensource.apple.com/tarballs/dyld/

从编码到APP安装到手机

想要了解 Mach-O 文件,首先要了解从编写代码,开发 APP 到 APP 打包并安装到手机上的整个过程:

1.首先我们编写完成代码之后,会通过 LLVM 编译器 预处理 我们的代码,比如将宏替换到指定位置。

2.预处理结束后,LLVM 会对代码进行 词法分析语法分析,生成 AST(AST是抽象语法树,主要用来进行快速遍历,实现静态代码检查的功能)。

3.AST 会生成 IR,IR 是一种更接近机器码的语言,通过 IR 可以生成不同平台的机器码。对于 iOS 平台,IR 生成可执行的 Mach-O 文件。

4.最后通过签名等操作生成 .app 文件,然后对 .app 文件进行压缩就得到我们可以安装的 ipa 安装包。

5.ipa 安装包可以通过开发者账号上传到 App Store,然后让用户下载安装。或者通过Xcode、iFunBox、爱思助手等工具来安装。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注