前言 本文主要介绍 Mach-O文件格式
以及 通用二进制文件
Mach-O 文件概述
Mach-O
其实是 Mach Object
文件格式的缩写,是 Mac
以及 iOS
上 可执行文件的格式
,类似于 Window
上的 PE格式
(Portable Executable),linux
上的 elf格式
(Executable And LinkingFormat)。
Mach-O
是一种 用于可执行文件、目标代码、动态库的文件格式
,作为 a.out格式
的替代,Mach-O
提供了更强的扩展性
Mach-O 文件格式 常见的 Mach-O格式 有以下几种:
目标文件 .o
库文件,细分主要有以下几种:
可执行文件
dyld
dsym
我们可以通过终端的 file
指令来查看文件的类型
目标文件 .o
新建一个 .c
文件
1 2 % cd Desktop % touch demo.c
在 .c 文件中实现如下
1 2 3 4 5 6 #include <stdio.h> int main(){ printf("test\n"); return 0; }
通过 clang 命令编译 .c 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 % clang -c demo.c % file demo.o demo.o: Mach-O 64-bit object x86_64 % clang demo.o % ls a.out demo.c demo.o % file a.out a.out: Mach-O 64-bit executable x86_64 % ./a.out test
ls
:查看当前文件夹
clang -c demo.c
: 将 .c文件
编译成 .o文件
(OC在后端中使用的是LLVM编译,而前端使用的工具是 clang,即理解为 LLVM包含clang)
file demo.o
: 查看文件类型 Mach-O文件、64位、x86_64架构
clang demo.o
: 将 .o文件
编译成 可执行文件
./a.out
: 执行可执行文件
也可以通过一行命令将 .c文件
编译成可执行文件 : clang -o demo2 demo.c
1 2 3 4 5 6 7 % clang -o demo2 demo.c % ls demo.c demo2 % ./demo2 test
重复4
的操作,再次生成一个可执行文件 demo3
,此时问题来了,这三个可执行文件(即 a、demo2、demo3)是否是一样的?我们可以通过 md5
验证,如果hash值一样,则说明一样的,反之不一样
1 2 3 4 5 6 7 8 % md5 a.out MD5 (a.out) = 922bae326e46b2ac90f5ad4bf2ed031c % md5 demo2 MD5 (demo2) = 922bae326e46b2ac90f5ad4bf2ed031c % md5 demo3 MD5 (demo3) = 922bae326e46b2ac90f5ad4bf2ed031c
中间产物 .o文件 其中 .c
和 .out
文件的区别是中间多了一个 .o
文件。而在我们的实际开发中,其实是由多个源码的,所以最终的 可执行文件是由多个源码
生成的,如下所示,将 两个.o
文件编译成 一个可执行文件
test.c 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdio.h> // 函数外定义变量 x 和 y int x; int y; int addtwonum() { // 函数内声明变量 x 和 y 为外部变量 extern int x; extern int y; // 给外部变量(全局变量)x 和 y 赋值 x = 1; y = 2; return x+y; }
test1.c 代码如下:
1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int main() { int result; result = 1; printf("result 为: %d",result); return 0; }
通过 clang
将 两个.c文件
生成 两个.o文件
:clang -c test1.c test.c
1 2 3 % clang -c test1.c test.c % ls test1.o test.c test.o test1.c
通过 clang 将 两个.o文件
编译成 两个可执行文件
:clang -o demo test1.o test.o
1 % clang -o demo test1.o test.o
如果出现如下报错
1 2 3 4 5 6 % clang -o demo test1.o test.o duplicate symbol '_main' in: test1.o test.o ld: 1 duplicate symbol for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation)
说明: text.c
和 test1.c
中的 代码相同
,需要将其中一个中的代码删除即可,即保证 test1.c 和 test.c 内代码不同
如果此时换一下链接顺序呢?例如: clang -o demo1 test.o test1.o
多个源码一次性生成可执行文件: clang -o demo2 test1.c test.c
对比上述生成三个可执行文件,是否是同一个?这里我们也通过 md5 生成的 hash值 进行对比:
1 2 3 4 5 6 % md5 demo MD5 (demo) = 4be533adc572558230adcbd067379fea % md5 demo1 MD5 (demo1) = a3ff882d7d541fe3bc2a11538bc2e031 % md5 demo2 MD5 (demo2) = 4be533adc572558230adcbd067379fea
结论 :通过对比发现,如果改变了 .o文件的 连接顺序,那么 Mach-O 也会发生改变
这里可以通过 objdump --macho -d demo
查看 Mach-O
链接顺序,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 % objdump --macho -d demo demo: (__TEXT,__text) section _main: 100003f30: 55 pushq %rbp 100003f31: 48 89 e5 movq %rsp, %rbp 100003f34: 48 83 ec 10 subq $16, %rsp 100003f38: c7 45 fc 00 00 00 00 movl $0, -4(%rbp) 100003f3f: c7 45 f8 01 00 00 00 movl $1, -8(%rbp) 100003f46: 8b 75 f8 movl -8(%rbp), %esi 100003f49: 48 8d 3d 56 00 00 00 leaq 86(%rip), %rdi ## literal pool for: "result \344\270\272: %d" 100003f50: b0 00 movb $0, %al 100003f52: e8 2d 00 00 00 callq 0x100003f84 ## symbol stub for: _printf 100003f57: 31 c0 xorl %eax, %eax 100003f59: 48 83 c4 10 addq $16, %rsp 100003f5d: 5d popq %rbp 100003f5e: c3 retq 100003f5f: 90 nop _addtwonum: 100003f60: 55 pushq %rbp 100003f61: 48 89 e5 movq %rsp, %rbp 100003f64: 48 8d 0d a9 40 00 00 leaq _y(%rip), %rcx 100003f6b: 48 8d 05 9e 40 00 00 leaq _x(%rip), %rax 100003f72: c7 00 01 00 00 00 movl $1, (%rax) 100003f78: c7 01 02 00 00 00 movl $2, (%rcx) 100003f7e: 8b 00 movl (%rax), %eax 100003f80: 03 01 addl (%rcx), %eax 100003f82: 5d popq %rbp 100003f83: c3 retq
源文件链接顺序是 main + addtwonum
这里可以通过 objdump --macho -d demo1
查看 Mach-O
链接顺序,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 % objdump --macho -d demo1 demo1: (__TEXT,__text) section _addtwonum: 100003f20: 55 pushq %rbp 100003f21: 48 89 e5 movq %rsp, %rbp 100003f24: 48 8d 0d e9 40 00 00 leaq _y(%rip), %rcx 100003f2b: 48 8d 05 de 40 00 00 leaq _x(%rip), %rax 100003f32: c7 00 01 00 00 00 movl $1, (%rax) 100003f38: c7 01 02 00 00 00 movl $2, (%rcx) 100003f3e: 8b 00 movl (%rax), %eax 100003f40: 03 01 addl (%rcx), %eax 100003f42: 5d popq %rbp 100003f43: c3 retq 100003f44: 90 nop 100003f45: 90 nop 100003f46: 90 nop 100003f47: 90 nop 100003f48: 90 nop 100003f49: 90 nop 100003f4a: 90 nop 100003f4b: 90 nop 100003f4c: 90 nop 100003f4d: 90 nop 100003f4e: 90 nop 100003f4f: 90 nop _main: 100003f50: 55 pushq %rbp 100003f51: 48 89 e5 movq %rsp, %rbp 100003f54: 48 83 ec 10 subq $16, %rsp 100003f58: c7 45 fc 00 00 00 00 movl $0, -4(%rbp) 100003f5f: c7 45 f8 01 00 00 00 movl $1, -8(%rbp) 100003f66: 8b 75 f8 movl -8(%rbp), %esi 100003f69: 48 8d 3d 32 00 00 00 leaq 50(%rip), %rdi ## literal pool for: "result \344\270\272: %d" 100003f70: b0 00 movb $0, %al 100003f72: e8 09 00 00 00 callq 0x100003f80 ## symbol stub for: _printf 100003f77: 31 c0 xorl %eax, %eax 100003f79: 48 83 c4 10 addq $16, %rsp 100003f7d: 5d popq %rbp 100003f7e: c3 retq
源文件的链接顺序是 addtwonum + main
这里可以通过 objdump --macho -d demo2
查看 Mach-O
链接顺序,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 % objdump --macho -d demo2 demo2: (__TEXT,__text) section _main: 100003f30: 55 pushq %rbp 100003f31: 48 89 e5 movq %rsp, %rbp 100003f34: 48 83 ec 10 subq $16, %rsp 100003f38: c7 45 fc 00 00 00 00 movl $0, -4(%rbp) 100003f3f: c7 45 f8 01 00 00 00 movl $1, -8(%rbp) 100003f46: 8b 75 f8 movl -8(%rbp), %esi 100003f49: 48 8d 3d 56 00 00 00 leaq 86(%rip), %rdi ## literal pool for: "result \344\270\272: %d" 100003f50: b0 00 movb $0, %al 100003f52: e8 2d 00 00 00 callq 0x100003f84 ## symbol stub for: _printf 100003f57: 31 c0 xorl %eax, %eax 100003f59: 48 83 c4 10 addq $16, %rsp 100003f5d: 5d popq %rbp 100003f5e: c3 retq 100003f5f: 90 nop _addtwonum: 100003f60: 55 pushq %rbp 100003f61: 48 89 e5 movq %rsp, %rbp 100003f64: 48 8d 0d a9 40 00 00 leaq _y(%rip), %rcx 100003f6b: 48 8d 05 9e 40 00 00 leaq _x(%rip), %rax 100003f72: c7 00 01 00 00 00 movl $1, (%rax) 100003f78: c7 01 02 00 00 00 movl $2, (%rcx) 100003f7e: 8b 00 movl (%rax), %eax 100003f80: 03 01 addl (%rcx), %eax 100003f82: 5d popq %rbp 100003f83: c3 retq
源文件的链接顺序是 main + addtwonum
上述所说的链接顺序,对应到我们日常开发中是指工程中的 target -> Build Phase -> Compiles Sources
,这里就是对应源文件的 编译顺序
,如果源文件的顺序发生了变化,生成的可执行文件是不一样的
库 文件
静态库 & 动态库
静态库:静态库的存在形式主要有两种(建议用.framework):.a + .framework
.a
是一个 纯二进制文件,.a
不能直接使用,至少需要.h
文件配合,可能还会需要资源文件
.framework
中除了有二进制文件外,还有资源文件,且可以 直接使用
两者的关系: .a + .h + sourceFile = .frmework
优势:方便共享代码,便于合理使用
实现iOS的模块化,即将固定业务模块化为静态库
共享代码,但不希望被看到代码的具体实现
动态库:动态库的存在形式也是两种:.dylib + .framework
.framework 为什么即是静态库又是动态库?
系统的 .framework 是动态库
自定义的 .framework 是静态库
静态库和动态库的区别
静态库
在 链接
时会被完整的拷贝到项目中,如果有多个APP都是用了同一个静态库,会拷贝多份
,浪费内存
动态库
不会复制,只有一份
,在程序运行时 动态加载 到内存中,多个APP共用一份,节约内存
验证 .a .dylib 是否是 Mach-O文件
验证 .a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 zhangjian@zhangjiandeMBP SRSF % find /usr -name "*.a" find: /usr/sbin/authserver: Permission denied /usr/local/lib/libbrotlidec-static.a /usr/local/lib/libev.a /usr/local/lib/libjemalloc.a /usr/local/lib/libnghttp2.a /usr/local/lib/libevent_extra.a /usr/local/lib/libevent.a /usr/local/lib/libevent_core.a /usr/local/lib/libprotobuf.a /usr/local/lib/liblz4.a /usr/local/lib/libprotoc.a /usr/local/lib/libprotobuf-lite.a /usr/local/lib/libbrotlienc-static.a /usr/local/lib/libbrotlicommon-static.a /usr/local/lib/libuv.a /usr/local/lib/libzstd.a /usr/local/lib/libevent_openssl.a /usr/local/lib/libmysqlservices.a /usr/local/lib/libjemalloc_pic.a /usr/local/lib/libmysqlclient.a /usr/local/lib/libevent_pthreads.a /usr/local/Cellar/nghttp2/1.43.0/lib/libnghttp2.a /usr/local/Cellar/mysql-client/8.0.25/lib/libmysqlclient.a /usr/local/Cellar/libuv/1.41.0/lib/libuv.a /usr/local/Cellar/jemalloc/5.2.1_1/lib/libjemalloc.a /usr/local/Cellar/jemalloc/5.2.1_1/lib/libjemalloc_pic.a /usr/local/Cellar/brotli/1.0.9/lib/libbrotlidec-static.a /usr/local/Cellar/brotli/1.0.9/lib/libbrotlienc-static.a /usr/local/Cellar/brotli/1.0.9/lib/libbrotlicommon-static.a /usr/local/Cellar/icu4c/69.1/lib/libicui18n.a /usr/local/Cellar/icu4c/69.1/lib/libicutest.a /usr/local/Cellar/icu4c/69.1/lib/libicuio.a /usr/local/Cellar/icu4c/69.1/lib/libicudata.a /usr/local/Cellar/icu4c/69.1/lib/libicuuc.a /usr/local/Cellar/icu4c/69.1/lib/libicutu.a /usr/local/Cellar/lz4/1.9.3/lib/liblz4.a /usr/local/Cellar/zstd/1.5.0/lib/libzstd.a /usr/local/Cellar/mysql/8.0.25_1/lib/libmysqlservices.a /usr/local/Cellar/mysql/8.0.25_1/lib/libmysqlclient.a /usr/local/Cellar/libevent/2.1.12/lib/libevent_extra.a /usr/local/Cellar/libevent/2.1.12/lib/libevent.a /usr/local/Cellar/libevent/2.1.12/lib/libevent_core.a /usr/local/Cellar/libevent/2.1.12/lib/libevent_openssl.a /usr/local/Cellar/libevent/2.1.12/lib/libevent_pthreads.a /usr/local/Cellar/openssl@1.1/1.1.1k/lib/libcrypto.a /usr/local/Cellar/openssl@1.1/1.1.1k/lib/libssl.a /usr/local/Cellar/libev/4.33/lib/libev.a /usr/local/Cellar/protobuf/3.17.3/lib/libprotobuf.a /usr/local/Cellar/protobuf/3.17.3/lib/libprotoc.a /usr/local/Cellar/protobuf/3.17.3/lib/libprotobuf-lite.a % file /usr/local/Cellar/protobuf/3.17.3/lib/libprotobuf-lite.a /usr/local/Cellar/libevent/2.1.12/lib/libevent_openssl.a: current ar archive random library
这个.a库是看不出来是 dynamically类型
验证 .dylib
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 find: /usr/sbin/authserver: Permission denied /usr/local/Homebrew/Library/Homebrew/test/support/fixtures/mach/fat.dylib /usr/local/Homebrew/Library/Homebrew/test/support/fixtures/mach/i386.dylib /usr/local/Homebrew/Library/Homebrew/test/support/fixtures/mach/x86_64.dylib /usr/local/lib/libmysqlrouter_http_auth_backend.1.dylib /usr/local/lib/libmysqlharness_stdx.1.dylib /usr/local/lib/libevent_pthreads-2.1.7.dylib /usr/local/lib/libbrotlidec.1.dylib /usr/local/lib/libuv.dylib /usr/local/lib/libbrotlicommon.1.dylib /usr/local/lib/liblz4.1.9.3.dylib /usr/local/lib/libcares.dylib /usr/local/lib/libmysqlharness.1.dylib /usr/local/lib/libbrotlienc.dylib /usr/local/lib/libprotobuf-lite.dylib /usr/local/lib/libevent_openssl-2.1.7.dylib /usr/local/lib/libevent_extra.dylib /usr/local/lib/libbrotlienc.1.dylib /usr/local/lib/libuv.1.dylib /usr/local/lib/libevent_extra-2.1.7.dylib /usr/local/lib/libprotoc.28.dylib /usr/local/lib/libprotobuf.28.dylib /usr/local/lib/libmysqlharness_tls.1.dylib /usr/local/lib/libevent.dylib /usr/local/lib/libcares.2.dylib /usr/local/lib/libcares.2.4.2.dylib /usr/local/lib/libbrotlienc.1.0.9.dylib /usr/local/lib/libmysqlclient.21.dylib /usr/local/lib/libev.dylib /usr/local/lib/libprotoc.dylib /usr/local/lib/libzstd.1.5.0.dylib /usr/local/lib/libevent-2.1.7.dylib /usr/local/lib/libevent_pthreads.dylib /usr/local/lib/liblz4.dylib /usr/local/lib/libmysqlrouter_io_component.1.dylib /usr/local/lib/libmysqlrouter_http.1.dylib /usr/local/lib/libjemalloc.2.dylib /usr/local/lib/libnghttp2.14.dylib /usr/local/lib/libprotobuf.dylib /usr/local/lib/libmysqlclient.dylib /usr/local/lib/libevent_openssl.dylib /usr/local/lib/liblz4.1.dylib /usr/local/lib/libmysqlrouter_http_auth_realm.1.dylib /usr/local/lib/libbrotlicommon.dylib /usr/local/lib/libnghttp2.dylib /usr/local/lib/libzstd.1.dylib /usr/local/lib/libbrotlidec.dylib /usr/local/lib/libmysqlrouter.1.dylib /usr/local/lib/libbrotlidec.1.0.9.dylib /usr/local/lib/libevent_core.dylib /usr/local/lib/libbrotlicommon.1.0.9.dylib /usr/local/lib/libev.4.dylib /usr/local/lib/libzstd.dylib /usr/local/lib/libjemalloc.dylib /usr/local/lib/libprotobuf-lite.28.dylib /usr/local/lib/libevent_core-2.1.7.dylib /usr/local/Cellar/nghttp2/1.43.0/lib/libnghttp2.14.dylib /usr/local/Cellar/nghttp2/1.43.0/lib/libnghttp2.dylib /usr/local/Cellar/mysql-client/8.0.25/lib/libssl.dylib /usr/local/Cellar/mysql-client/8.0.25/lib/libssl.1.1.dylib /usr/local/Cellar/mysql-client/8.0.25/lib/plugin/libssl.1.1.dylib /usr/local/Cellar/mysql-client/8.0.25/lib/plugin/libcrypto.1.1.dylib /usr/local/Cellar/mysql-client/8.0.25/lib/libmysqlclient.21.dylib /usr/local/Cellar/mysql-client/8.0.25/lib/libcrypto.dylib /usr/local/Cellar/mysql-client/8.0.25/lib/libmysqlclient.dylib /usr/local/Cellar/mysql-client/8.0.25/lib/libcrypto.1.1.dylib /usr/local/Cellar/libuv/1.41.0/lib/libuv.dylib /usr/local/Cellar/libuv/1.41.0/lib/libuv.1.dylib /usr/local/Cellar/jemalloc/5.2.1_1/lib/libjemalloc.2.dylib /usr/local/Cellar/jemalloc/5.2.1_1/lib/libjemalloc.dylib /usr/local/Cellar/brotli/1.0.9/lib/libbrotlidec.1.dylib /usr/local/Cellar/brotli/1.0.9/lib/libbrotlicommon.1.dylib /usr/local/Cellar/brotli/1.0.9/lib/libbrotlienc.dylib /usr/local/Cellar/brotli/1.0.9/lib/libbrotlienc.1.dylib /usr/local/Cellar/brotli/1.0.9/lib/libbrotlienc.1.0.9.dylib /usr/local/Cellar/brotli/1.0.9/lib/libbrotlicommon.dylib /usr/local/Cellar/brotli/1.0.9/lib/libbrotlidec.dylib /usr/local/Cellar/brotli/1.0.9/lib/libbrotlidec.1.0.9.dylib /usr/local/Cellar/brotli/1.0.9/lib/libbrotlicommon.1.0.9.dylib /usr/local/Cellar/icu4c/69.1/lib/libicui18n.69.dylib /usr/local/Cellar/icu4c/69.1/lib/libicuio.69.1.dylib /usr/local/Cellar/icu4c/69.1/lib/libicuio.dylib /usr/local/Cellar/icu4c/69.1/lib/libicuuc.69.dylib /usr/local/Cellar/icu4c/69.1/lib/libicutest.dylib /usr/local/Cellar/icu4c/69.1/lib/libicudata.dylib /usr/local/Cellar/icu4c/69.1/lib/libicudata.69.dylib /usr/local/Cellar/icu4c/69.1/lib/libicutu.69.1.dylib /usr/local/Cellar/icu4c/69.1/lib/libicui18n.69.1.dylib /usr/local/Cellar/icu4c/69.1/lib/libicuuc.dylib /usr/local/Cellar/icu4c/69.1/lib/libicutu.dylib /usr/local/Cellar/icu4c/69.1/lib/libicui18n.dylib /usr/local/Cellar/icu4c/69.1/lib/libicutest.69.dylib /usr/local/Cellar/icu4c/69.1/lib/libicudata.69.1.dylib /usr/local/Cellar/icu4c/69.1/lib/libicutest.69.1.dylib /usr/local/Cellar/icu4c/69.1/lib/libicuuc.69.1.dylib /usr/local/Cellar/icu4c/69.1/lib/libicuio.69.dylib /usr/local/Cellar/icu4c/69.1/lib/libicutu.69.dylib /usr/local/Cellar/lz4/1.9.3/lib/liblz4.1.9.3.dylib /usr/local/Cellar/lz4/1.9.3/lib/liblz4.dylib /usr/local/Cellar/lz4/1.9.3/lib/liblz4.1.dylib /usr/local/Cellar/c-ares/1.17.1/lib/libcares.dylib /usr/local/Cellar/c-ares/1.17.1/lib/libcares.2.dylib /usr/local/Cellar/c-ares/1.17.1/lib/libcares.2.4.2.dylib /usr/local/Cellar/zstd/1.5.0/lib/libzstd.1.5.0.dylib /usr/local/Cellar/zstd/1.5.0/lib/libzstd.1.dylib /usr/local/Cellar/zstd/1.5.0/lib/libzstd.dylib /usr/local/Cellar/mysql/8.0.25_1/lib/libmysqlrouter_http_auth_backend.1.dylib /usr/local/Cellar/mysql/8.0.25_1/lib/libmysqlharness_stdx.1.dylib /usr/local/Cellar/mysql/8.0.25_1/lib/libmysqlharness.1.dylib /usr/local/Cellar/mysql/8.0.25_1/lib/libmysqlharness_tls.1.dylib /usr/local/Cellar/mysql/8.0.25_1/lib/libmysqlclient.21.dylib /usr/local/Cellar/mysql/8.0.25_1/lib/libmysqlrouter_io_component.1.dylib /usr/local/Cellar/mysql/8.0.25_1/lib/libmysqlrouter_http.1.dylib /usr/local/Cellar/mysql/8.0.25_1/lib/libmysqlclient.dylib /usr/local/Cellar/mysql/8.0.25_1/lib/libmysqlrouter_http_auth_realm.1.dylib /usr/local/Cellar/mysql/8.0.25_1/lib/libmysqlrouter.1.dylib /usr/local/Cellar/libevent/2.1.12/lib/libevent_pthreads-2.1.7.dylib /usr/local/Cellar/libevent/2.1.12/lib/libevent_openssl-2.1.7.dylib /usr/local/Cellar/libevent/2.1.12/lib/libevent_extra.dylib /usr/local/Cellar/libevent/2.1.12/lib/libevent_extra-2.1.7.dylib /usr/local/Cellar/libevent/2.1.12/lib/libevent.dylib /usr/local/Cellar/libevent/2.1.12/lib/libevent-2.1.7.dylib /usr/local/Cellar/libevent/2.1.12/lib/libevent_pthreads.dylib /usr/local/Cellar/libevent/2.1.12/lib/libevent_openssl.dylib /usr/local/Cellar/libevent/2.1.12/lib/libevent_core.dylib /usr/local/Cellar/libevent/2.1.12/lib/libevent_core-2.1.7.dylib /usr/local/Cellar/openssl@1.1/1.1.1k/lib/libssl.dylib /usr/local/Cellar/openssl@1.1/1.1.1k/lib/libssl.1.1.dylib /usr/local/Cellar/openssl@1.1/1.1.1k/lib/libcrypto.dylib /usr/local/Cellar/openssl@1.1/1.1.1k/lib/engines-1.1/padlock.dylib /usr/local/Cellar/openssl@1.1/1.1.1k/lib/engines-1.1/capi.dylib /usr/local/Cellar/openssl@1.1/1.1.1k/lib/libcrypto.1.1.dylib /usr/local/Cellar/libev/4.33/lib/libev.dylib /usr/local/Cellar/libev/4.33/lib/libev.4.dylib /usr/local/Cellar/protobuf/3.17.3/lib/libprotobuf-lite.dylib /usr/local/Cellar/protobuf/3.17.3/lib/libprotoc.28.dylib /usr/local/Cellar/protobuf/3.17.3/lib/libprotobuf.28.dylib /usr/local/Cellar/protobuf/3.17.3/lib/libprotoc.dylib /usr/local/Cellar/protobuf/3.17.3/lib/libprotobuf.dylib /usr/local/Cellar/protobuf/3.17.3/lib/libprotobuf-lite.28.dylib /usr/lib/libobjc-trampolines.dylib /usr/lib/libLeaksAtExit.dylib /usr/lib/libstdc++.6.dylib /usr/lib/libpython2.7.dylib /usr/lib/libhunspell-1.2.0.dylib /usr/lib/libMTLCapture.dylib /usr/lib/system/libsystem_pthread.dylib /usr/lib/system/introspection/libsystem_pthread.dylib /usr/lib/system/introspection/libdispatch.dylib /usr/lib/system/libsystem_kernel.dylib /usr/lib/system/libsystem_platform.dylib /usr/lib/libgmalloc.dylib /usr/lib/swift/libswiftRemoteMirror.dylib /usr/lib/swift/libswiftCreateML.dylib /usr/lib/libiodbc.2.dylib /usr/lib/libffi-trampolines.dylib /usr/lib/ssh-keychain.dylib /usr/lib/libpython.dylib /usr/lib/libiodbcinst.2.dylib % file /usr/lib/libpython.dylib /usr/lib/libpython.dylib: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit dynamically linked shared library x86_64] [arm64e] /usr/lib/libpython.dylib (for architecture x86_64): Mach-O 64-bit dynamically linked shared library x86_64 /usr/lib/libpython.dylib (for architecture arm64e): Mach-O 64-bit dynamically linked shared library arm64e
可执行文件
这里的可执行文件,即一般是指日常项目中,编译后生成的可执行文件,可以通过 file 查看其文件类型
1 2 % file /Users/zhangjian/Desktop/demo1 /Users/zhangjian/Desktop/demo1: Mach-O 64-bit executable x86_64
dyld
dyld
(the dynamic link editor)是苹果的 动态连接器
,是苹果操作系统一个重要的组成部分,在系统内核做好程序准备工作之后,交由dyld负责余下的工作,而且它是开源的,任何人可以通过苹果官网下载它的源码来阅读理解它的运作方式,了解系统加载动态库的细节。
在iOS系统中,每个程序依赖的动态库
都需要通过dyld
(位于/usr/lib/dyld)一个一个 加载到内存,然而,很多系统库几乎是每个程序都会用到的,如果在每个程序运行的时候都重复的去加载一次,势必造成运行缓慢,为了优化启动速度和提高程序性能,共享缓存机制就应运而生。所有默认的动态链接库被合并成一个大的缓存文件,放到/System/Library/Caches/com.apple.dyld/目录
下,按照不同的架构分别保存着
查找Mac中的dyld
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 zhangjian@zhangjiandeMBP ~ % cd /usr/lib zhangjian@zhangjiandeMBP lib % ls charset.alias libstdc++.6.dylib cron log dsc_extractor.bundle pam dtrace pkgconfig dyld python2.7 groff rpcsvc libLeaksAtExit.dylib ruby libMTLCapture.dylib sasl2 libffi-trampolines.dylib sqlite3 libgmalloc.dylib ssh-keychain.dylib libhunspell-1.2.0.dylib swift libiodbc.2.dylib system libiodbcinst.2.dylib updaters libobjc-trampolines.dylib xpc libpython.dylib zsh libpython2.7.dylib zhangjian@zhangjiandeMBP lib % ls dyld dyld
查看 dyld
的文件类型: file dyld
,是一个 动态链接器
,其本身也是一个Mach-O
文件
1 2 3 4 5 zhangjian@zhangjiandeMBP lib % file dyld dyld: Mach-O universal binary with 3 architectures: [i386:Mach-O dynamic linker i386] [x86_64:Mach-O 64-bit dynamic linker x86_64] [arm64e] dyld (for architecture i386): Mach-O dynamic linker i386 dyld (for architecture x86_64): Mach-O 64-bit dynamic linker x86_64 dyld (for architecture arm64e): Mach-O 64-bit dynamic linker arm64e
dsym文件
Xcode编译项目后,我们会看到一个同名的 dSYM文件
,dSYM是 保存16进制函数地址映射信息的中转文件
,我们调试的 symbols
都会包含在这个文件中,并且每次编译项目的时候都会生成一个新的 dSYM文件,位于/Users/<用户名>/Library/Developer/Xcode/Archives
目录下,所以对于每一个发布版本我们都很有必要保存对应的 Archives 文件。
当我们软件 release
模式打包或上线后,不会像我们在xcode中那样直观的看到 崩溃
的错误日志,这个时候我们就需要 分析crash report
文件了,iOS设备中会有日志文件保存我们每个应用出错的函数内存地址,通过 xcode
的 organizer
可以将iOS设备中的 deviceLog
导出成 crash文件
,这个时候我们就可以 通过出错的函数地址去 查询dSYM
文件中程序对应的函数和文件名,但前提是我们需要有软件版本对应的dSYM文件,这也是为什么我们很有必要保存每个发布版本的 Archives
文件了
程序 真机+release
编译时,有一个 .dSYM文件
.dSYM
也是一个 mach-o
文件,是一个 符号表
,主要用于出现崩溃后可以通过这个文件 取符号
,方便 查询问题
通用二进制文件 mac系统所支持的cpu及硬件平台发生了很大的变化,为了解决软件在多个平台硬件平台上的 兼容性
问题,苹果开发了一个 通用的二进制文件格式(Universal Binary)
,又称 胖二进制(Fat Binary)
苹果公司提出的一种程序代码,能同时 适用多种架构的二进制文件
同一个程序包中同时为多种架构提供最理想的性能。
因为需要存储多种代码,通用二进制应用程序通常比单一平台二进制的程序要大
但是由于两种架构有 共通的非执行资源(代码以外的)
,所以并不会达到单一版本的两倍之多
而且由于执行中只调用一部分代码,运行起来也不需要额外的内存
演示
在日常开发的项目中,可以通过Build Setting -> Mach-O type
,指定 Mach-O文件的类型,如下图:
1 2 3 4 5 6 7 % cd /Users/zhangjian/Library/Developer/Xcode/DerivedData/Demo-edwjvapoqaffzeeiyzyhfryhfrsr/Build/Products/Release-iphoneos/Demo.app % file Demo Demo: 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] Demo (for architecture armv7): Mach-O executable arm_v7 Demo (for architecture armv7s): Mach-O executable arm_v7s Demo (for architecture arm64): Mach-O 64-bit executable arm64
ARM架构 ARM架构
过去称作 进阶精简指令集机器(Advanced RISC Machine,更早称作:Acorn RISC Machine)
,是一个 32位精简指令集(RISC)
处理器架构,ARM处理器非常适用于通讯领域,符合其主要设计目标为低耗电的特性
ARM
和Intel
处理器的第一个区别是,前者使用 精简指令集(RISC)
,而后者使用 复杂指令集(CISC)
ARM处理器指令集:是指计算机ARM操作系统指令系统
32位
ARM指令集(armv6 | armv7 | armv7s
)64位
ARM指令集(arm64
)
i386 | x86_64
是 Mac
处理器的指令集
iOS 设备支持的指令集
通用二进制源码
通过 CMD + shift + O 搜索 fat.h
找到其中通用二进制文件的头部结构 fat_header 如下所示
1 2 3 4 struct fat_header { uint32_t magic; /* magic字段被定义为常量FAT_MAGIC,表示这是一个胖二进制 */ uint32_t nfat_arch; /* 表示有多少个Mach-O文件 */ };
每个胖二进制都用 fat_arch 结构表示,在 fat_header 之后,紧接着一个活多个连续的fat_arch结构体
1 2 3 4 5 6 7 struct fat_arch { cpu_type_t cputype; /* cpu 类型 */ cpu_subtype_t cpusubtype; /* cpu的子类型 */ uint32_t offset; /* 制定了当前cpu架构数据相对于当前文件开头的偏移量 */ uint32_t size; /* 数据的大小 */ uint32_t align; /* 数据的内存对齐边界,取值必须是2的次方,它确保了当前cpu架构的目标文件在加载到内存中时,数据是经过内存优化对齐的 */ };
终端指令
可以通过tool来查看fat_header信息:otool -f 可执行文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 % cd /Users/zhangjian/Library/Developer/Xcode/DerivedData/Demo-edwjvapoqaffzeeiyzyhfryhfrsr/Build/Products/Release-iphoneos/Demo.app % otool -f Demo Fat headers fat_magic 0xcafebabe nfat_arch 3 architecture 0 cputype 12 cpusubtype 9 capabilities 0x0 offset 16384 size 79280 align 2^14 (16384) architecture 1 cputype 12 cpusubtype 11 capabilities 0x0 offset 98304 size 79280 align 2^14 (16384) architecture 2 cputype 16777228 cpusubtype 0 capabilities 0x0 offset 180224 size 80000 align 2^14 (16384) zhangjian@zhangjiandeMBP Demo.app %
lipo演示
查看二进制文件中包含的架构:lipo -info Demo
1 2 3 4 % cd /Users/zhangjian/Library/Developer/Xcode/DerivedData/Demo-edwjvapoqaffzeeiyzyhfryhfrsr/Build/Products/Release-iphoneos/Demo.app % lipo -info Demo Architectures in the fat file: Demo are: armv7 armv7s arm64
1 2 3 % lipo Demo -thin armv7 -output Demo_armv7 % file Demo_armv7 Demo_armv7: Mach-O executable arm_v7
合并:lipo -create Demo_armv7 Demo_arm64 -output Demo_v7_64
总结
Mach-O
其实是 Mach Object
文件格式的缩写,是mac以及iOS上可执行文件的格式,是一种 用于可执行文件、目标代码、动态库的文件格式
,且 Mach-O提供了更强的扩展性
常见的Mach-O格式:.o
、库文件(.a、.dylib、.framework)
、可执行文件
、dyld
、.dsym
.a + .h + sourceFile = .framwork
动态.framework:系统Framework库
静态.framwork:自定义的Framework库
查看文件类型命令:file 文件路径
查看Mach-O源文件的链接顺序:objdump --macho -d 可执行文件
dyld
(the dynamic link editor)是苹果的动态链接器,mac中路径为/usr/lib
dSYM
是保存 16 进制函数地址映射信息的中转文件,位于/Users/<用户名>/Library/Developer/Xcode/Archives
目录。可以用于通过出错的函数地址去查询 dSYM 文件中程序对应的函数名和文件名
通用二进制
文件(Universal Binary,也称为胖二进制
(Fat Binary))。主要适用于解决多个平台的兼容性问题
通过otool来查看fat_header信息:otool -f 可执行文件
lipo命令拆分、合并胖二进制文件
$lipo -info MachO
文件:使用lifo -info
可以查看MachO文件包含的架构
$lipo MachO文件 –thin 架构 –output 输出文件路径
:使用lifo –thin
拆分某种架构
$lipo -create MachO1 MachO2 -output 输出文件路径
: 使用lipo -create
合并多种架构