MachO文件概述
Mach-O
其实是Mach Object
文件格式的缩写,是macOS
以及iOS
上可执行文件的格式,类似于Windows
上的PE
格式(Portable Executable
),Linux
上的ELF
格式(Executable and Linking Format
)
Mach-O
文件格式
Mach-O
是一种用于可执行文件、目标代码、动态库的文件格式。作为a.out
格式的替代,Mach-O
提供了更强的扩展性属于
MachO
格式的常见文件
- 目标文件
.o
- 库文件:
.a
、.dylib
、Framework
- 可执行文件
dyld
.dSYM
file
命令使用
file
命令,用来探测给定文件的类型语法
file (选项) (参数)
选项
-b:列出辨识结果时,不显示文件名称;
-c:详细显示指令执行过程,便于排错或分析程序执行的情形;
-f<名称文件>:指定名称文件,其内容有一个或多个文件名称时,让file依序辨识这些文件,格式为每列一个文件名称;
-L:直接显示符号连接所指向的文件类别;
-m<魔法数字文件>:指定魔法数字文件;
-v:显示版本信息;
-z:尝试去解读压缩文件的内容。
参数
文件:要确定类型的文件列表,多个文件之间使用空格分开,可以使用
Shell
通配符匹配多个文件案例1:
生成目标文件,查看文件格式
使用
clang
命令,将test.m
生成目标文件
clang -c test.m -o test.o
使用
file
命令,查看目标文件的文件类型
file test.o
-------------------------
test.o: Mach-O 64-bit object x86_64
案例2:
生成可执行文件,查看文件格式
使用
clang
命令,将test.o
生成可执行文件
clang test.o
使用
file
命令,查看可执行文件的文件类型
file a.out
-------------------------
a.out: Mach-O 64-bit executable x86_64
案例3:
使用
clang
命令,可以将目标文件生成可执行文件,也可以直接将.m
源文件生成可执行文件将目标文件生成
test1
可执行文件
clang -o test1 test.o
将
.m
源码文件生成test2
可执行文件
clang -o test2 test.m
不同方式生成的可执行文件,使用
HASH
验证它们的一致性
md5 a.out
md5 test1
md5 test2
-------------------------
MD5 (a.out) = e3e459acc7a9aecee970ed12a0c1368d
MD5 (test1) = e3e459acc7a9aecee970ed12a0c1368d
MD5 (test2) = e3e459acc7a9aecee970ed12a0c1368d
在源码没有变化的时候,使用
clang
编译出的二进制文件是一致的案例4:
将多个
.m
源码文件,生成一个可执行文件创建
test1.m
文件,写入以下代码:```
import
int main(){ return 1; }
> 创建`test2.m`文件,写入以下代码:
>
import
int test2(){ return 2; }
> 将多个源码文件,生成一个可执行文件
>
clang -o test1 test1.m test2.m
> 
> 案例5:
> 将多个目标文件,链接成一个可执行文件
> 使用`clang`命令,将`test1.m`和`test2.m`生成目标文件
>
clang -c test1.m test2.m
> 将多个目标文件,链接成一个可执行文件
>
clang -o test2 test2.o test1.o
> 
> 不同方式生成的可执行文件,使用`HASH`验证它们的一致性
>
md5 test1
md5 test2
MD5 (test1) = 1c9ad179590712c5a2a8683af9173d6e MD5 (test2) = 40f53096fe90fab7a531cee885e0b8f1
> 源码并没有发生变化,但两个可执行文件生成的`HASH`值不同。问题的本质,两次生成可执行文件时,编译源文件的顺序是不一样的
> 使用`objdump`命令,分别查看`test1`和`test2`可执行文件的代码段
>
objdump —macho -d test1
test1: (TEXT,text) section _main: 100003f80: 55 pushq %rbp 100003f81: 48 89 e5 movq %rsp, %rbp … _test2: 100003fa0: 55 pushq %rbp 100003fa1: 48 89 e5 movq %rsp, %rbp …
>
objdump —macho -d test2
test2: (TEXT,text) section _test2: 100003f90: 55 pushq %rbp 100003f91: 48 89 e5 movq %rsp, %rbp … _main: 100003fa0: 55 pushq %rbp 100003fa1: 48 89 e5 movq %rsp, %rbp …
> 两个可执行文件的代码截然不同
> 可执行文件是一个或多个目标文件的集合,会受到文件编译顺序的影响。原理和`Xcode`中的`Compile Sources`一样<br />

> 使用相同编译顺序,将多个目标文件,链接成一个可执行文件
>
clang -o test3 test1.o test2.o
> 使用`HASH`验证它们的一致性
>
md5 test1
md5 test3
MD5 (test1) = 1c9ad179590712c5a2a8683af9173d6e MD5 (test3) = 1c9ad179590712c5a2a8683af9173d6e
> 多个目标文件,当源码和编译顺序相同,使用`clang`编译出的二进制文件是一致的
> 案例6:
> 查看`.a`的文件格式
>
file libAFNetworking.a
libAFNetworking.a: current ar archive
> 案例7:
> 查看`.dylib`的文件格式
>
file libAFNetworking.dylib
libAFNetworking.dylib: Mach-O 64-bit dynamically linked shared library x86_64
> 案例8:
> 查看`dyld`的文件格式
> 使用`find`命令,找到`dyld`文件路径
>
find /usr -name “dyld”
find: /usr/sbin/authserver: Permission denied /usr/lib/dyld /usr/share/file/magic/dyld
> 查看文件格式
>
file /usr/lib/dyld
/usr/lib/dyld: Mach-O universal binary with 2 architectures: [i386:Mach-O dynamic linker i386] [x86_64:Mach-O 64-bit dynamic linker x86_64] /usr/lib/dyld (for architecture i386): Mach-O dynamic linker i386 /usr/lib/dyld (for architecture x86_64): Mach-O 64-bit dynamic linker x86_64
> - 动态链接器
> 案例9:
> 查看`.dSYM`的文件格式
> 右键`TestInject.app.dSYM`文件,显示包内容
> 进入`Contents/Resources/DWARF`目录,找到`TestInject`文件
> 查看文件格式
>
file TestInject
TestInject: Mach-O 64-bit dSYM companion file x86_64
> - 二进制符号文件,常用于分析`App`的崩溃信息
#####可执行文件
> 项目中,在`Build Settings`的`Mach-O Type`配置项,默认为`Executable`(可执行文件)<br />

> 在`iOS11`及以上系统中,项目生成的可执行文件为`arm64`单一架构
>
file WeChat
WeChat: Mach-O 64-bit executable arm64
> 使用`iOS10.3`系统编译项目
>
file WeChat
WeChat: Mach-O universal binary with 2 architectures: [arm_v7:Mach-O executable arm_v7] [arm64:Mach-O 64-bit executable arm64] WeChat (for architecture armv7): Mach-O executable arm_v7 WeChat (for architecture arm64): Mach-O 64-bit executable arm64
> - 格式变为通用二进制文件,包含`armv7`和`arm64`两种架构
> 项目中,`Build Settings`中包含架构的设置<br />

> - `Architectures`:设置支持的架构
> - `Build Active Architecture Only`:只编译当前设备支持的架构,`Debug`模式默认`YES`
> - `$(ARCHS_STANDARD)`:`Xcode`内置的环境变量,不同`Xcode`版本值不一样,当前版本下表示`armv7 + arm64`
> 案例1:
> 增加一个兼容架构
> `iOS`系统下,还有一个`armv7s`架构,支持`iPhone5`、`iPhone5c`
> 打开项目,在`Build Settings`中的`Architectures`配置项,选择`other`,增加`armv7s`<br />

> 编译项目
>
file WeChat
WeChat: Mach-O universal binary with 3 architectures: [arm_v7:Mach-O executable arm_v7] [arm_v7s:Mach-O executable arm_v7s] [arm64:Mach-O 64-bit executable arm64] WeChat (for architecture armv7): Mach-O executable arm_v7 WeChat (for architecture armv7s): Mach-O executable arm_v7s WeChat (for architecture arm64): Mach-O 64-bit executable arm64
> - 同时包含三种架构,`armv7`、`armv7s`、`arm64`
#####通用二进制文件
> 通用二进制文件(`Universal Binary`)
> - 苹果公司提出的一种程序代码,能同时适用多种架构的二进制文件
> - 同一个程序包中同时为多种架构提供最理想的性能
> - 因为需要储存多种代码,通用二进制应用程序通常比单一平台二进制的程序要大
> - 由于两种架构有共通的非执行资源(代码以外的),所以并不会达到单一版本的两倍之多
> - 由于执行中只调用一部分代码,运行起来也不需要额外的内存
> - 也被称为“胖二进制文件”(`Fat Binary`)
> `lipo`命令
> 使用`lipo -info`,可以查看`MachO`文件包含的架构
>
lipo -info MachO文件
> 使用`lipo -thin`,拆分某种架构
>
lipo MachO文件 -thin 架构 -output 输出文件路径
> 使用`lipo -create`,合并多种架构
>
lipo -create MachO1 MachO2 -output 输出文件路径
> 案例1:
> 从通用二进制文件中拆分架构
> 查看可执行文件
>
lipo -info WeChat
Architectures in the fat file: WeChat are: armv7 armv7s arm64
> - `WeChat`中包含`armv7`、`armv7s`、`arm64`三种架构
> 使用`lipo`命令,拆分出`arm64`架构
>
lipo WeChat -thin arm64 -output WeChat_arm64
> 使用`lipo`命令,拆分出`armv7`架构
>
lipo WeChat -thin armv7 -output WeChat_armv7
> 
> 案例2:
> 合并架构
> 使用`lipo`命令,将`arm64`和`armv7`架构合并
>
lipo -create WeChat_arm64 WeChat_armv7 -output WeChat_64_v7
> 查看`WeChat_64_v7`可执行文件
>
lipo -info WeChat_64_v7
Architectures in the fat file: WeChat_64_v7 are: armv7 arm64
> - 包含`armv7`、`arm64`两种架构
#####MachO文件结构
> 因为`MachO`文件本身是一种文件格式,所以我们一定需要了解其文件内部结构
> `Mach-O`文件结构<br />

> `Header`:包含该二进制文件的一般信息
> - 字节顺序、架构类型、加载指令的数量等
> - 使得可以快速确认一些信息,比如当前文件用于`32位`还是`64位`,对应的处理器是什么、文件类型是什么
> `Load Commands`:一张包含很多内容的表
> - 内容包括区域的位置、符号表、动态符号表等
> `Data`:通常是对象文件中最大的部分
> - 包含`Segement`的具体数据
> 通用二进制文件,最上面是`Fat Header`,包含自身信息和架构信息。多架构之间,对应多套`Mach-O`的文件结构<br />

> `otool`命令
> 用来查看可执行文件的`MachO`信息
>
Usage: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/otool [-arch arch_type] [-fahlLDtdorSTMRIHGvVcXmqQjCP] [-mcpu=arg] [—version]