MachO文件
Mach-O其实就是Mach Object文件格式的缩写,是mac以及iOS上可执行文件的格式,类似于windows上的PE格式(Portable Executable),linux上的elf格式(Executable and Linking Format)
Mach-O 文件格式
Mach-O为Mach Object文件格式的缩写,它是一种用于可执行文件,目标代码,动态库的文件格式。作为a.out格式的替代,Mach-O提供了更强的扩展性
属于MachO格式的常见文件
- 目标文件.o
- 库文件
- .a
- .dylib
- .Framework
- 可执行文件
- dyld
- .dsym
File指令
- 通过$file 文件路径查看文件类型
通用二进制文件(Universal binary)
- 苹果公司提出的一种程序代码,能同时使用多种架构的二进制文件
- 同一个程序包中同时为多种架构提供最理想的性能
- 因为需要存储多种代码,通用二进制的程序通常会比单一平台的二进制程序要大
- 但是由于两种架构有共通的非执行资源,所以并不会达到单一版本的两倍之多
- 而且由于执行中只调用一部分代码,运行起来也不需要额外的内存。
lipo命令
- 使用lifo -info 可以查看MachO文件包含的架构
$lipo -info MachO 文件
- 使用lifo -thin拆分某种架构
$lipo MachO 文件 -thin架构 -output 输出文件路径
- 使用lipo -create 合并多种架构
$lipo -create MachO1 MachO2 -output 输出文件路径
MachO 文件结构
因为MachO文件本身是一种文件格式,所以我们一定需要了解其文件内部结构![](https://img.haomeiwen.com/i1759682/39408263755cfeff.png)
Mach-O 的组成机构如图所示包括了
- Header 包含该二进制文件的一般信息
- 字节顺序,架构类型,加载指令的数量等
- 使得可以快速确认一些信息,比如当前文件用于32还是64位,对应的处理器是什么,文件类型是什么
- Load commands 一张包含很多内容的表
- 内容包含区域的位置,符号表,动态符号表
- Data 通常是对象文件中最大的部分
- 包含Segment的具体数据
Header的数据结构
struct mach_header_64 {
unit32_t magic; /*魔数,快速定位属于64还是32位*/
cpu_type_t cputype; /*CPU类型,比如ARM*/
cpu_subtype_t cpusubtype;/*CPU的具体类型arm64\arm7*/
uint32_t filetype;/*文件类型,比如可执行文件*/
unit32_t ncmds;/*loadCommands条数*/
uint32_t sizeofcmds;/*LoadCommands的大小*/
uint32_t flags;/*标志位标识二进制文件支持的功能,主要是和系统加载,链接有关*/
uint32_t reserved;/*reserved*/
}
LoadCommands
![](https://img.haomeiwen.com/i1759682/5a4d242d7bc3b9a0.png)
Mach-O Data
Data区域是由Segment段和Section节组成。先来看看Segment的组成,代码来自loader.h
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; /* 标识符 */
};
Segment可以说是由多个Section组成的,下面是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
Section | 账号 |
---|---|
__TEXT.__text | 主程序代码 |
__TEXT.__cstring | C 语言字符串 |
__TEXT.__const | const 关键字修饰的常量 |
__TEXT.__stubs | 用于 Stub 的占位代码,很多地方称之为桩代码。 |
__TEXT.__stubs_helper | 当 Stub 无法找到真正的符号地址后的最终指向 |
__TEXT.__objc_methname | Objective-C 方法名称 |
__TEXT.__objc_methtype | Objective-C 方法类型 |
__TEXT.__objc_classname | Objective-C 类名称 |
__DATA.__data | 初始化过的可变数据 |
__DATA.__la_symbol_ptr | lazy binding 的指针表,表中的指针一开始都指向 __stub_helper |
__DATA.nl_symbol_ptr | 非 lazy binding 的指针表,每个表项中的指针都指向一个在装载过程中,被动态链机器搜索完成的符号 |
__DATA.__const | 没有初始化过的常量 |
__DATA.__cfstring | 程序中使用的 Core Foundation 字符串(CFStringRefs) |
__DATA.__bss | BSS,存放为初始化的全局变量,即常说的静态内存分配 |
__DATA.__common | 没有初始化过的符号声明 |
__DATA.__objc_classlist | Objective-C 类列表 |
__DATA.__objc_protolist | Objective-C 原型 |
__DATA.__objc_imginfo | Objective-C 镜像信息 |
__DATA.__objc_selfrefs | Objective-C self 引用 |
__DATA.__objc_protorefs | Objective-C 原型引用 |
__DATA.__objc_superrefs | Objective-C 超类引用 |
DYLD
dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统一个重要组成部分,在系统内核做好程序准备工作之后,交由dyld负责余下的工作
参数解释
- LC_SEGMENT_64:将该段(64位)隐射到进程地址空间中
-LC_DYLD_INFO_ONLY:加载动态链接库信息(重定向地址、弱引用绑定、懒加载绑定、开放函数等的偏移值等信息)
-LC_SYMTAB:载入符号表地址 - LC_DYSYMTAB:载入动态符号表地址
- LC_LOAD_DYLINKER:加载动态加载库
- LC_UUID:确定文件的唯一标识,crash解析中也会有这个,去检测dysm文件和crash文件是否匹配
- LC_VERSION_MIN_MACOSX/LC_VERSION_MIN_IPHONEOS:确定二进制文件要求的最低操作系统版本
- LC_SOURCE_VERSION:构建该二进制文件使用的源代码版本
- LC_MAIN:设置程序主线程的入口地址和栈大小
- LC_ENCRYPTION_INFO_64:获取加密信息
- LC_LOAD_DYLIB:加载额外的动态库
- LC_FUNCTION_STARTS:定义一个函数起始地址表,使调试器和其他程序易于看到一个地址是否在函数内
-LC_DATA_IN_CODE:定义在代码段内的非指令的表
-LC_CODE_SIGNATURE:获取应用签名信息
网友评论