MachO文件
Mach-O是Mach Object文件格式的缩写,是mac以及iOS上可执行文件的格式。是一种用于可执行文件、目标代码、动态库的文件格式。作为a.out格式的替代,Mach-O提供了更强的扩展性。
类似于windows上的PE格式 (Portable Executable ),linux上的elf格式 (Executable and Linking Format)
Mach-O文件格式
- 目标文件.o
- 库文件
- .a
- .dylib
- framework
- 可执行文件
- dyld
- .dsym
实际开发中,MatchO文件有很多不同的类型,可以通过在Xcode上指定。
Targets
→Build Settings
→Linking
→Mach-O Type
通用二进制文件
- 苹果公司提出的一种程序代码,能同时适用多种架构的二进制文件。
- 同一个程序包中同时为多种架构提供最理想的性能。
- 因为需要储存多种代码,通常比单一平台二进制的程序要大。
- 由于执行中只调用一部分代码,运行起来也不需要额外的内存。
在Xcode编译可以指定生成哪些架构的Match-O文件,同时也可以添加其他架构
Targets
→Build Settings
→Architectures
→Architectures
设备的CPU架构(指令集)
- 模拟器:
- 4s-5: i386
- 5s-6s Plus: x86_64
- 真机(iOS设备):
- armv6: iPhone、iPhone 2、iPhone 3G、iPod Touch(第一代)、iPod Touch(第二代)
- armv7: iPhone 3Gs、iPhone 4、iPhone 4s、iPad、iPad 2
- armv7s: iPhone 5、iPhone 5c
- arm64: iPhone 5s之后机型
Mach-O架构拆分、合并
- lipo工具
- 查看MachO架构
$lipo -info 'MachO文件'
- 拆分MachO架构
$lipo 'MachO文件' –thin '架构名' –output '目标MachO文件'
- 合并MachO架构
$lipo -create '第一个MachO文件' '第二个MachO文件 -output '目标MachO文件'
- file指令:查看文件信息
$file 文件路径
MachO文件结构
Mach-O 的组成结构如图所示,主要包括三个部分:Header、Load commands、Data
Header
包含二进制文件的一般信息,架构类型、字节顺序、加载指令的数量等。
struct mach_header_64 {
uint32_t magic; /* mach magic number identifier */
cpu_type_t cputype; /* cpu specifier */
cpu_subtype_t cpusubtype; /* machine specifier */
uint32_t filetype; /* type of file */
uint32_t ncmds; /* number of load commands */
uint32_t sizeofcmds; /* the size of all the load commands */
uint32_t flags; /* flags */
uint32_t reserved; /* reserved */
};
magic:定位结构是64位还是32位 (e.g. MH_MAGIC_64)
cputype:CPU类型 (e.g. CPU_TYPE_ARM64)
cpusubtype:CPU具体类型 (e.g. CPU_SUBTYPE_ARM64_ALL)
filetype:文件类型 (e.g. MH_EXECUTE)
ncmds:Load Commands条数
sizeofcmds:Load Commands大小
flags:标志位。标识二进制文件支持的功能,主要和系统加载、链接有关
reserved:预留区(只有64位才有)
Load Commands
包含区域的位置、符号表、动态符号表等。描述了文件中数据的具体组织结构,不同的数据类型使用不同的加载命令表示。
- LC_SEGMENT_64(__PAGEZERO)
- VM Size:虚拟内存,大小4G(32位是16M)。用于区分32位及存储位置
- LC_SEGMENT_64(__TEXT)
- LC_SEGMENT_64(__DATA)
- LC_SEGMENT_64(__LINKEDIT)
- VM Address:虚拟内存地址
- VM Size:虚拟内存大小
- File Offset:数据在文件中的起始位置
- File Size:数据在文件中的大小
- LC_DYLD_INFO_ONLY(动态链接相关信息)
- Rebase:进行重定向的位置信息。当MachO加载到内存里,系统会随机分配一个内存偏移大小aslr,和rebase里面的offset,对接(位置相加)获取代码在内存中的实际位置。再根据size开辟实际内存
- Binding:绑定的位置信息
- Weak Binding:弱绑定的位置信息
- Lazy Binding:懒加载绑定的位置信息
- Export:对外的位置信息
- LC_SYMTAB(符号表地址)
- Symbol Table Offset:符号表的位置。函数名称、函数地址关联的信息
- Number of Symbol:符号数
- String Table Offset:符号名称位置
- String Table Size:符号名称大小
- LC_DYSYMTAB(动态符号表地址)
- LC_LOAD_DYLINKER(动态库连接器:dyld)
- Str Offset:动态库连接器位置
- Name:动态库连接器路径(dyld)
- LC_UUID(MachO文件唯一标识)
- LC_VERSION_MIN_IPHONESOS(MachO文件支持最低的操作系统版本)
- LC_SOURCE_VERSION(源代码版本)
- LC_MAIN(MachO程序入口:设置程序主线程的入口地址和栈大小)
- Entry Offset:入口位置
- Stacksize:堆栈大小
- Entry Point:入口点(内存地址)
- LC_ENCRYPTION_INFO_64(加密信息)
- Crypt Offset:加密信息位置
- Crypt Size:加密信息大小
- Crypt ID:加密信息ID。非加密是0,加密是1
- LC_LOAD_DYLIB(依赖库的路径,包含三方库)
- Str Offset:动态库位置
- Time Stamp:动态库时间
- Current Version:动态库版本
- LC_RPATH(frameworks信息)
- LC_FUNCTION_STARTS(函数的起始位置表)
- LC_DATA_IN_CODE(代码数据信息)
- LC_CODE_SIGNATURE(代码签名信息)
Data
由 Segment 段和 Section 节组成。存放具体数据:代码、数据、字符串常量、类、方法等。
- Segment 组成
#define SEG_PAGEZERO "__PAGEZERO" /* 当时 MH_EXECUTE 文件时,捕获到空指针 */
#define SEG_TEXT "__TEXT" /* 代码/只读数据段 */
#define SEG_DATA "__DATA" /* 数据段 */
#define SEG_OBJC "__OBJC" /* Objective-C runtime 段 */
#define SEG_LINKEDIT "__LINKEDIT" /* 包含需要被动态链接器使用的符号和其他表,包括符号表、字符串表等 */
- Segment 的数据结构
struct segment_command_64 {
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* section_64 结构体所需要的空间 */
char segname[16]; /* segment 名字,上述宏中的定义 */
uint64_t vmaddr; /* 所描述段的虚拟内存地址 */
uint64_t vmsize; /* 为当前段分配的虚拟内存大小 */
uint64_t fileoff; /* 当前段在文件中的偏移量 */
uint64_t filesize; /* 当前段在文件中占用的字节 */
vm_prot_t maxprot; /* 段所在页所需要的最高内存保护,用八进制表示 */
vm_prot_t initprot; /* 段所在页原始内存保护 */
uint32_t nsects; /* 段中 Section 数量 */
uint32_t flags; /* 标识符 */
};
- Section 数据结构
部分的 Segment (主要指的 __TEXT 和 __DATA)进一步分解为 Section。
struct section_64 {
char sectname[16]; /* Section 名字 */
char segname[16]; /* Section 所在的 Segment 名称 */
uint64_t addr; /* Section 所在的内存地址 */
uint64_t size; /* Section 的大小 */
uint32_t offset; /* Section 所在的文件偏移 */
uint32_t align; /* Section 的内存对齐边界 (2 的次幂) */
uint32_t reloff; /* 重定位信息的文件偏移 */
uint32_t nreloc; /* 重定位条目的数目 */
uint32_t flags; /* 标志属性 */
uint32_t reserved1; /* 保留字段1 (for offset or index) */
uint32_t reserved2; /* 保留字段2 (for count or sizeof) */
uint32_t reserved3; /* 保留字段3 */
};
以下列举一些常见的 Section:
__TEXT,__text: 主程序代码
__TEXT,__stubs / __stub_helper: 用于动态链接的桩
__TEXT,__objc_methname: OC方法名称
__TEXT,__objc_classname: OC类名
__TEXT,__objc_methtype: OC方法类型
__TEXT,__cstring: 程序中c语言字符串
__DATA,__got: 非懒加载符号表
__DATA,__la_symbol_ptr: 懒加载符号表
__DATA,__objc_classlist: OC类列表
__DATA,__objc_protollist: OC原型列表
__DATA,__objc_imageinfo: OC镜像信息
__DATA,__objc_const: OC常量
__DATA,__objc_selfrefs: OC类自引用(self)
__DATA,__objc_superrefs: OC类超类引用(super)
__DATA,__objc_protolrefs: OC原型引用
__DATA,__objc_data / __data: OC代码数据
Dynamic Loader Info: 动态链接器所需要使用的信息 (重定向,符号绑定,懒加载绑定等..)
Function Starts: 方法的起始位置
Symbol Table: 符号表
Dynamic Symbol Table: 动态符号表
String Table: 字符串表
Code Signature: 代码签名信息