新生代农民工的主页

了解 Mach-O文件

字数统计: 1.5k阅读时长: 6 min
2022/02/26

Mach-O文件

想要一个应用程序运行起来,那么它的可执行文件格式一定要被操作系统所理解。在Windows系统的可执行文件是PE,而在OS XiOS 中的可执行文件是Mach-O

那么Mach-O是怎么生成的呢?苹果公司目前使用的编译器是LLVM,在程序编译时,编译器会对每个文件进行编译,然后生成Mach-O文件,而后链接器会将项目中的多个 Mach-O 文件合并成一个,最终的这个就是我们的可执行Mach-O文件.

那么Mach-O 文件里面有哪些内容呢?其实主要还是数据和代码,其中数据是一些初始值的定义,代码就是一些是函数的定义。下面我们一起了解下Mach-O文件。

Mach-O文件简介

Mach-O 是Mach Object文件格式的缩写,是运用于mac以及iOS上;它是一种用于可执行文件、目标代码、动态库的文件格式;

Mach-O文件类型

  • Executable:应用可执行的二进制文件
  • Dylib Library:动态链接库
  • Static Library:静态链接库
  • Bundle:不能被链接 Dylib,只能在运行使用dlopen()加载
  • Relocatable Object File:可重定向文件

Mach-O文件结构

Mach-O文件主要由三部分组成:HeaderLoad commandsData

Header部分描述当前Mach-O文件的基本信息 (架构,是否Fat二进制文件,CUP类型等等);

Load commands部分主要描述:1.Mach-O文件中在虚拟内存中空间是如何分配,从哪个内存地址开始到哪个内存地址结束。 2.不同段在Mach-O文件中的位置,大小分布。

Data部分是描述内存如何被分配的内容。包括__TEXT, __DATA等

图片来源于苹果文档

类型:区分32位、64位

结构

1
2
3
4
5
6
7
8
9
10
// 32位
struct mach_header {
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 */
};
1
2
3
4
5
6
7
8
9
10
11
// 64位
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:确定Mach-O文件运行框架,如64位/32位
cputype:CPU类型,如arm
cpusubtype:对应CPU类型的具体型号
filetype:文件类型
ncmds:加载命令条数
sizeofcmds:所有加载命令的大小
flags:保留字段
reserved:标志位

LoadCommand

  • cmd:指令类型
  • cmdsize: 指令长度

以下是load_command的结构:

1
2
3
4
 struct load_command {
uint32_t cmd; /* type of load command */
uint32_t cmdsize; /* total size of command in bytes */
};

command 指令类型说明

  • LC_SEGMENT/LC_SEGMENNT_64 :将对应段中的数据加载并映射到进程的内存空间

  • LC_SEGMENT_TEXT :代码段,其中_stub_helper用于关联函数bind/rebind

  • LC_SEGMENT_DATA :可读/可写的数据段,函数指针,其中_la_symbol_ptr动态函数个数,及相对动态符号表的偏移量

  • LC_SEGMENT_LINKEDIT : :动态链接加载指令,支持动态链接dyld,该段长度覆盖符号表等数据(计算链接时程序的基址),符号表,动态符号表,字符串表段中定义的offset偏移量都是基于_LINKEDIT的vm_add

  • LC_SYMTAB :符号表信息,解析函数名

  • LC_DYSYMTAB :动态符号表信息,地址值为动态函数相对符号表的索引,_la_symbol_ptr对应的cmd可以换算出第一个动态函数对应动态符号表的初始地址,其次存储是连续,结构长度固定的,可以通过遍历获取所有动态函数的对应的符号表索引

LoadCommand

Data

Data中就是由Segment组成的,每一个Segment定义了一些Mach-O文件的数据、地址和内存保护属性,这些数据在动态链接器加载程序时被映射到了虚拟内存中。每个段都有不同的功能。

Segment一般包含下列功能:

  1. __PAGEZERO: 空指针陷阱段,映射到虚拟内存空间的第一页,用于捕捉对NULL指针的引用;
  2. __TEXT: 包含了执行代码以及其他只读数据。 为了让内核将它 直接从可执行文件映射到共享内存, 静态连接器设置该段的虚拟内存权限为不允许写。当这个段被映射到内存后,可以被所有进程共享。(这主要用在frameworks, bundles和共享库等程序中,也可以为同一个可执行文件的多个进程拷贝使用)
  3. __DATA: 包含了程序数据,该段可写;
  4. __LINKEDIT: 含有为动态链接库使用的原始数据,比如符号,字符串,重定位表条目等等。

一般Segment又会按不同的功能划分为几个区(Section),即段所有字母大小,加两个下横线作为前缀,而区则为小写,同样加两个下横线作为前缀,

Segment结构体:

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
struct segment_command { /* for 32-bit architectures */
uint32_t cmd; /* LC_SEGMENT */
uint32_t cmdsize; /* includes sizeof section structs */
char segname[16]; /* segment name */
uint32_t vmaddr; /* memory address of this segment */
uint32_t vmsize; /* memory size of this segment */
uint32_t fileoff; /* file offset of this segment */
uint32_t filesize; /* amount to map from the file */
vm_prot_t maxprot; /* maximum VM protection */
vm_prot_t initprot; /* initial VM protection */
uint32_t nsects; /* number of sections in segment */
uint32_t flags; /* flags */
};


struct segment_command_64 { /* for 64-bit architectures */
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* includes sizeof section_64 structs */
char segname[16]; /* segment name */
uint64_t vmaddr; /* memory address of this segment */
uint64_t vmsize; /* memory size of this segment */
uint64_t fileoff; /* file offset of this segment */
uint64_t filesize; /* amount to map from the file */
vm_prot_t maxprot; /* maximum VM protection */
vm_prot_t initprot; /* initial VM protection */
uint32_t nsects; /* number of sections in segment */
uint32_t flags; /* flags */
};

Section结构体:

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
struct section { /* for 32-bit architectures */
char sectname[16]; /* name of this section */
char segname[16]; /* segment this section goes in */
uint32_t addr; /* memory address of this section */
uint32_t size; /* size in bytes of this section */
uint32_t offset; /* file offset of this section */
uint32_t align; /* section alignment (power of 2) */
uint32_t reloff; /* file offset of relocation entries */
uint32_t nreloc; /* number of relocation entries */
uint32_t flags; /* flags (section type and attributes)*/
uint32_t reserved1; /* reserved (for offset or index) */
uint32_t reserved2; /* reserved (for count or sizeof) */
};

struct section_64 { /* for 64-bit architectures */
char sectname[16]; /* name of this section */
char segname[16]; /* segment this section goes in */
uint64_t addr; /* memory address of this section */
uint64_t size; /* size in bytes of this section */
uint32_t offset; /* file offset of this section */
uint32_t align; /* section alignment (power of 2) */
uint32_t reloff; /* file offset of relocation entries */
uint32_t nreloc; /* number of relocation entries */
uint32_t flags; /* flags (section type and attributes)*/
uint32_t reserved1; /* reserved (for offset or index) */
uint32_t reserved2; /* reserved (for count or sizeof) */
uint32_t reserved3; /* reserved */
};
CATALOG
  1. 1. Mach-O文件
    1. 1.1. Mach-O文件简介
    2. 1.2. Mach-O文件类型
    3. 1.3. Mach-O文件结构
      1. 1.3.1. Header
      2. 1.3.2. LoadCommand
        1. 1.3.2.1. command 指令类型说明
      3. 1.3.3. Data