前言:本文旨在介绍iOS中的Mach-O文件。
一、介绍
Mach-O是一种文件的格式(可执行文件的格式,比如图片的格式是png,可执行文件的格式是Mach-O),是iOS/Mac OS上存储程序以及库的标准格式。
二、基本结构
Mach-O文件的基本结构:
- Header:头部,包含可以执行的CPU架构,比如x86,arm64。
- Load commands:加载命令,也就是告诉系统如何去处理不同的加载信息。包含文件的组织架构和在虚拟内存中的布局方式。
- Data:数据,包含Load commands中需要的各个段(segment)的数据,每个Segment的大小都是Page的整数倍。
三、Load Commands
Load Commands从上图可知 Load Commands 主要包含了有多个 Segment 段,每个 Segment 中又包含了多个 Section 段。每一部分都是系统执行指令。可以看到主要包含了以下几个Segment段:
- __TEXT段:主要包含程序代码和只读的常量,这个段的内容如果是系统动态库的内容,那么所有进程公用。只读可执行。
- __DATA段:主要包含全局变量和静态变量,这个段的内容每个进程单独进行维护。可读写。
- __LINKEDIT:主要包含链接器使用的符号和其他的表(比如函数名称、地址等)这个段的内容也是可以多进程公用。只读。
四、常见的Mach-O文件类型
1、MH_OBJECT
- 目标文件(.o)
- 静态库文件(.a),静态库其实就是N个.o合并在一起
2、MH_EXECUTE:可执行文件
- .app/xx
3、MH_DYLIB:动态库文件
- .dylib
- .framework/xx
4、MH_DYLINKER:动态链接编辑器
- /usr/lib/dyld
5、MH_DSYM:存储着二进制文件符号信息的文件
- .dSYM/Contents/Resources/DWARF/xx(常用于分析APP的崩溃信息)
五、可以查看Mach-O文件的工具
1、MachOView工具
MachOView工具可以在Mac平台中查看MachO文件格式信息。
1.1、MachOView工具下载
MachOView- MachOView下载地址:http://sourceforge.net/projects/machoview/
- MachOView源码地址:https://github.com/gdbinit/MachOView
1.2、MachOView工具使用
-
直接启动MachOView,在状态栏中点击file,打开对应的MachO文件即可。如下图:
-
打开后效果如下图,可以查看文件中的信息:
2、Hopper Disassmbler工具
Hopper Disassmbler 可以加载Mach-O文件,显示汇编代码和OC伪代码。
2.1、工具下载
Hopper Disassmbler工具Hopper Disassmbler下载地址:https://www.hopperapp.com/download.html?
2.2、工具使用
-
直接启动Hopper Disassmbler,在状态栏中点击file,打开对应的MachO文件即可。如下图:
-
打开后效果如下图,可以查看文件中的信息:
3、ottol命令可查询Mach-O文件内容
otool可查询需要的内容:
比如输入:otool
-f print the fat headers
-a print the archive header
-h print the mach header
-l print the load commands
-L print shared libraries used
终端输入:
otool -h Example
终端输出:
Example:
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
0xfeedfacf 16777223 3 0x00 2 79 8640 0x00218085
4、size命令可查询Mach-O文件的内存分布
终端输入:
size -l -m -x ZUX
终端输出:
Segment __PAGEZERO: 0x100000000 (zero fill) (vmaddr 0x0 fileoff 0)
Segment __TEXT: 0x5c8000 (vmaddr 0x100000000 fileoff 0)
Section __text: 0x522010 (addr 0x100002cc0 offset 11456)
Section __stubs: 0xca8 (addr 0x100524cd0 offset 5393616)
Section __stub_helper: 0x1514 (addr 0x100525978 offset 5396856)
Section __const: 0x84b8 (addr 0x100526e90 offset 5402256)
Section __cstring: 0x28aed (addr 0x10052f350 offset 5436240)
Section __objc_methname: 0x479c3 (addr 0x100557e3d offset 5602877)
Section __ustring: 0xfc8e (addr 0x10059f800 offset 5896192)
Section __objc_classname: 0x5ac7 (addr 0x1005af48e offset 5960846)
Section __objc_methtype: 0x9b08 (addr 0x1005b4f55 offset 5984085)
Section __gcc_except_tab: 0x6630 (addr 0x1005bea60 offset 6023776)
Section __entitlements: 0x16e (addr 0x1005c5090 offset 6049936)
Section __unwind_info: 0x2da8 (addr 0x1005c5200 offset 6050304)
Section __eh_frame: 0x58 (addr 0x1005c7fa8 offset 6061992)
total 0x5c532f
Segment __DATA: 0x134000 (vmaddr 0x1005c8000 fileoff 6062080)
Section __nl_symbol_ptr: 0x8 (addr 0x1005c8000 offset 6062080)
Section __got: 0x598 (addr 0x1005c8008 offset 6062088)
Section __la_symbol_ptr: 0x10e0 (addr 0x1005c85a0 offset 6063520)
Section __mod_init_func: 0x10 (addr 0x1005c9680 offset 6067840)
Section __const: 0x7c50 (addr 0x1005c9690 offset 6067856)
Section __cfstring: 0x22ce0 (addr 0x1005d12e0 offset 6099680)
Section __objc_classlist: 0x1e90 (addr 0x1005f3fc0 offset 6242240)
Section __objc_nlclslist: 0x40 (addr 0x1005f5e50 offset 6250064)
Section __objc_catlist: 0x250 (addr 0x1005f5e90 offset 6250128)
Section __objc_nlcatlist: 0x78 (addr 0x1005f60e0 offset 6250720)
Section __objc_protolist: 0x3e0 (addr 0x1005f6158 offset 6250840)
Section __objc_imageinfo: 0x8 (addr 0x1005f6538 offset 6251832)
Section __objc_const: 0xc4dc8 (addr 0x1005f6540 offset 6251840)
Section __objc_selrefs: 0xe008 (addr 0x1006bb308 offset 7058184)
Section __objc_protorefs: 0xb0 (addr 0x1006c9310 offset 7115536)
Section __objc_classrefs: 0x1ef0 (addr 0x1006c93c0 offset 7115712)
Section __objc_superrefs: 0x13c8 (addr 0x1006cb2b0 offset 7123632)
Section __objc_ivar: 0x5ce0 (addr 0x1006cc678 offset 7128696)
Section __objc_data: 0x1f730 (addr 0x1006d2358 offset 7152472)
Section __data: 0x4bf0 (addr 0x1006f1a90 offset 7281296)
Section __bss: 0x24b8 (addr 0x1006f6680 zerofill)
Section __common: 0x8d0 (addr 0x1006f8b40 zerofill)
total 0x131400
Segment __LINKEDIT: 0x474000 (vmaddr 0x1006fc000 fileoff 7307264)
total 0x100b70000
六、Dyld 动态链接器
1、Dyld 动态链接器作用
App开始启动后,系统首先加载可执行文件(Mach-O文件),然后加载动态链接器Dyld。Dyld是一个专门用来加载动态链接库的库。执行从Dyld开始,Dyld从可执行文件依赖的动态库开始,递归加载所有的依赖动态库链接库。
2、Load Dylibs 加载动态库
Dyld会首先读取Mach-O文件的Header和load commonds。然后就知道了这个可执行文件依赖的动态库。例如加载动态库A到内存,接着检查A所依赖的动态库,就这样的递归加载,直到所有的动态库加载完毕。通常一个App所依赖的动态库在100~400个左右,其中大多数都是系统的动态库,它们会被缓存到(共享缓存)dyld share cache,这样读取的效率会很高。
查看Mach-O文件所依赖的动态库,可以通过MachOView的图形化界面(展开Load Command就能看到),也可以通过命令行otool。
3、Dyld用于加载以下类型的Mach-O文件:
- MH_EXECUTE(可执行文件)
- MH_DYLIB(动态库)
- MH_BUNDLE(bundle类型的mach-o文件)
注意:APP的可执行文件、动态库都是由Dyld负责加载的。
网友评论