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.outmd5 test1md5 test2-------------------------MD5 (a.out) = e3e459acc7a9aecee970ed12a0c1368dMD5 (test1) = e3e459acc7a9aecee970ed12a0c1368dMD5 (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]
