各位老铁们,大家好,今天由我来为大家分享深度解析:Mac OS X ABI Mach体系结构全揭秘,以及的相关问题知识,希望对大家有所帮助。如果可以帮助到大家,还望关注收藏下本站,您的支持是我们最大的动力,谢谢大家了哈,下面我们开始吧!
文件初始布局符号表在虚拟内存中的位置(用于动态链接) 程序主线程的初始执行状态包含主可执行文件导入符号定义的共享库的名称加载指令后,所有Mach- O 文件包含一个或多个数据段。每个段有零个或多个部分,并且段的每个部分包含特定类型的代码或数据。每个段定义了一个虚拟内存区域,链接器使用该区域映射到进程的地址空间。段和节的确切数量和布局由加载命令和文件类型指定。
在用户级完全链接的Mach-O 文件中,最后一部分是链接编辑部分。此部分包含链接编辑信息表,例如符号表、字符串表等,动态加载器使用这些表将可执行文件或Mach-O 包链接到其附属库。
Mach-O 文件中的各种表格按编号引用部分。节号从1(而不是0)开始并跨越段边界。因此,文件中的第一段可能包含部分1 和2,第二段可能包含部分3 和4。
使用Stabs 调试格式时,符号表还包含调试信息。使用DWARF 时,调试信息存储在映像相应的dSYM 文件中,该文件由uuid_command(第20 页)结构指定。
注释:stats 以符号表字符串命名,因为最初调试信息以字符串的形式存储在Unix a.out 目标文件的符号表中。 sats 以字符串的形式对程序信息进行编码。一开始,stabs 很简单,但后来变得越来越复杂、难以理解且不一致。另外,stabs 不规范,文档也不够详细。 Sun Microsystem在stabs的基础上做了大量的扩展; GCC在对SUn的扩展进行逆向工程的过程中还做了其他的扩展。刺刀仍然被广泛使用。
DWARF已经被广泛使用,包括GCC和LLVM。 DWARF 还存储基于嵌套结构的调试信息。
DWARF 源自Unix System V Release 4 中的C 编译器和sdb 调试器。1989 年的文档形成了DWARF 1。1900 年发布了DWARF 2 的标准草案。后来,由于Motorola 项目的失败,支持团队被解散了。随后,DWARF 2扩展激增,也有多种实现,但未能形成最终标准。直到2006年发布最终标准DWARF 3。2010年发布DWARF 4。
image.png
头部结构和加载命令
Mach-O 文件包含架构的代码和数据。 Mach-O 文件的标头结构指定了目标体系结构,这使内核能够确保为基于PowerPC 的Macintosh 计算机设计的代码不会在基于Intel 的Macintosh 计算机上执行。
可以将多个Mach-O文件组合成一个二进制文件,在《通用二进制文件和32位/64位PowerPC二进制文件》的相应格式中进行了描述。
包含多个体系结构的目标文件的二进制文件不是Mach-O 文件。他们归档一个或多个Mach-O 文件。
段落和章节通常通过名称来访问。按照惯例,节使用全部大写字母加两个下划线(例如,与字母组合)来命名;部分使用全部小写字母加两个下划线(例如,与字母组合)来命名。此命名约定是标准的,尽管工具的正确操作并不需要此命名约定。
段(segment)
段定义了Mach-O 文件中的字节范围以及地址和内存保护属性,当动态链接器加载应用程序时,这些属性将映射到虚拟内存中。因此,段始终与虚拟内存页对齐。一个段包含零个或多个部分。
运行时需要比构建时更多内存的段可以指定为比磁盘上实际可用的内存大小更大的内存大小。 PowerPC 可执行文件的链接器生成的__PAGEZERO 段的虚拟内存大小为一页,磁盘上的大小为0。它不需要占用可执行文件中的任何空间。
note:部分的末尾必须填充大小为0 的部分,否则标准工具将无法成功操作Mach-O 文件。
为了紧凑性,中间目标文件仅包含一个段。此部分没有名称。在最终的目标文件中,它包含由不同部分定义的部分。定义节的数据结构(第23 页)包含节内节的名称,静态链接器将每个节放置在最终的目标文件中。
为了获得更好的性能,段应与虚拟内存页边界对齐,对于PowerPC 和x86 处理器,每个虚拟内存页为4096b。累加每个section的大小,然后将结果四舍五入作为下一个虚拟内存页(4096b或4kb)的分割线。使用此算法,最小段为4kb,下一个增量将为4kb。
出于分页目的,标头和加载命令包含在第一段中。在可执行文件中,通常这意味着标头和加载命令__TEXT 开始的位置,因为这是包含数据的第一个部分。 __PAGEZERO段在硬盘上没有数据,所以一般会被忽略。
这些是标准Mac OS X 开发工具的片段(包含在Xcode Tools CD 中),可能包含在Mac OS X 可执行文件中:
静态链接器创建__PAGEZERO 节作为可执行文件的第一节。该段位于虚拟内存的位置0,没有分配保护权限。它们的组合会导致访问NULL,这是一种常见的C 编程错误,会导致立即崩溃。 __PAGEZERO 段是当前架构的一个虚拟内存页的大小(对于基于Intel 和Power-PC 内核的Mac 计算机,通常为4096 字节或十六进制0x1000)。因为__PAGEZERO段没有数据,所以在文件中不占用任何空间(段命令中的文件大小为0)。__TEXT段包含可执行代码和只读数据。允许内核直接从可执行文件映射到共享内存中,静态链接器将这一段虚拟内存的权限设置为不允许写入。当一个段被映射到内存中时,它可以在所有对其内容感兴趣的进程之间共享。 (这主要用在框架、捆绑包和共享库中,但在Mac OS 中可以运行同一个可执行文件的多个副本。 __TEXT 段无法写入磁盘。当内核需要释放物理内存时,它可以给静态链接器设置该虚拟内存的权限为可写,因此下次需要时,重新从磁盘读取它们。框架或其他共享库的内容在逻辑上复制到与该库链接的每个进程。当组成__DATA 段的内存页可读可写时,内核将它们标记为“写时复制”写入这些页面之一,进程接收当前进程私有的当前页面。 __OBJC 段包含OC 语言运行时支持库使用的数据。 __IMPORT 段包含指向未定义符号的符号存根和非惰性指针。可执行文件。此部分仅在IA-32 架构的目标可执行文件中生成。 __LINKEDIT 部分包含动态链接器使用的原始数据,例如符号、字符串和重定位表记录。
Sections(节)
__TEXT 和__DATA 节包含许多标准节,如表1、表2 和表3 中所列。__OBJC 节包含许多OC 编译器专用节。请注意,静态链接器和文件分析工具使用节类型和属性(而不是节名称)来确定它们应如何处理节。节名称、类型和属性在节数据类型的描述(第23 页)中有进一步描述。
表1 __TEXT段的各个部分
节和节名内容__TEXT,__text可执行机器代码。编译器通常只放入可执行代码,不放入其他类型的表和数据__TEXT、__cstringC字符串常量。 c 字符串是以“ ”结尾的非空字节序列。静态链接器在构建最终结果时将合并c 常量字符串值并删除重复的__TEXT、__picsymbol_stub 位置无关的间接符号存根。有关更多信息,请参阅Mach-O 编程主题中的“动态代码生成”。 __TEXT, __symbol_stub 符号存根简介,更多信息请参见Mach-O 编程主题中的“动态代码生成” __TEXT, __const 初始化的常量变量。编译器将在本节中放置用const 修改的不可重定位数据,__TEXT、__litera144 字节文字。编译器将单精度浮点常量放置在此部分中。构建最终文件时,静态链接器会合并这些值,删除重复项。对于某些体系结构,编译器使用立即加载指令比添加到此部分更有效。 __TEXT,__litera188 字节文字。编译器将双精度浮点常量放置在本节中。构建最终文件时,静态链接器会合并这些值,删除重复项。对于某些体系结构,编译器使用立即加载指令比添加到此部分更有效。表2 __DATA 部分中的部分
节和节名内容__DATA、__data 初始化的可变变量,如可写的c字符串和数据数组__DATA、__la_symbol_ptr 延迟加载符号指针,间接引用来自不同文件的重要函数。有关更多信息,请参阅Mach-O 编程主题中的“生成动态代码” __DATA, __nl_symbol_ptr 是一个非惰性符号指针,它间接引用来自不同文件的数据项。有关更多信息,请参阅Mach-O 编程主题中的“生成动态代码”。 __DATA, __Placeholder dyld 动态链接器使用的部分__DATA, __const 初始化可重定位常量变量__DATA, __mod_init_func 模块初始化函数。其中,C++ 编译器将静态构造函数__DATA、__mod_term_func 模块终止函数__DATA、__bss 未初始化静态变量的数据(例如,static int i;) __DATA、__common 导入的未初始化符号定义(例如:int i;)放在全局作用域中(表3 __IMPORT 部分的各个部分
节和节名内容__IMPORT、__jump_table是动态库中函数调用的存根。 __IMPORT、__pointers 是非延迟加载符号指针,直接引用从不同文件导入的函数。请注意, 编译器或任何创建Mach-O 文件的工具都可以定义其他节名称。这些附加名称未出现在表1 中。
数据类型
这里介绍的是组成Mach-O 文件的数据类型。除了fat_header(第56 页)和fat_arch(第56 页)之外,所有Mach-O 数据结构中整数类型的值都是使用主机CPU 的字节排序方案写入的,这些值都是大端字节序。按顺序写的。
头部数据结构
mach_header指定文件的常规属性。出现在针对32 位体系结构的目标文件的开头。在/usr/include/mach-o/loader.h中声明,还可以看到mach_header_64(第14页)
结构体mach_header
{
uint32_t 魔法;
cpu_type_t cpu类型;
cpu_subtype_t cpusubtype;
uint32_t 文件类型;
uint32_t ncmds;
uint32_t sizeofcmds;
uint32_t 标志;
};字段magic:包含一个整数值,表明该文件是32位Mach-O文件。如果文件打算在与运行编译器的计算机具有相同字节序的CPU 上使用,请使用常量MH_MAGIC。当目标计算机具有与主机CPU 相反的字节排序方案时,可以使用常量MH_CIGAM。
cputype:该整数值指示您打算在其上运行该文件的体系结构。合适的值为:
CPU_TYPE_POWERPC 面向基于PowerPC 的Mac CPU_TYPE_I386 面向基于Intel 的Mac
cpusubtype 是一个整数,指定CPU 的确切型号。在Mac OS 支持的所有PowerPC 或x86 处理器上运行该字段的有效值包括:
MH_OBJECT 文件类型是用于中间目标文件的格式。它以紧凑的格式包含段中的所有部分。编译器和汇编器通常为每个源代码文件创建一个MH_OBJECT 文件。按照惯例,该文件名的后缀格式为.o。 MH_EXECUTE 文件类型是标准可执行程序使用的格式。 MH_BUNDLE 文件类型通常由运行时加载的代码(通常称为捆绑包或插件)使用。按照惯例,此格式的文件扩展名是.bundle。 MH_DYLIB 文件类型用于动态共享库。它包含一些额外的表来支持多个模块。按照惯例,这种格式的文件扩展名是dylib,除了框架的主共享库之外,它通常没有文件扩展名。 HM_PRELOAD 文件类型是一种可执行格式,用于不由Mac OS X 内核加载的特殊用途程序,例如烧录到可编程ROM 芯片中的程序。不要将此文件类型与MH_PREBOUND 标志混淆,后者是静态链接器在标头结构中设置的标志,用于标记预绑定图像。 MH_CORE 文件类型用于存储核心文件,这些文件通常在程序崩溃时创建。核心文件存储进程崩溃时的整个地址空间。稍后您可以在core 文件上运行gdb 以找出发生崩溃的原因。 MH_DYLINKER 文件类型用于动态链接器共享库。这是dyld 文件类型。 MH_DSYM 文件类型指定存储与二进制文件相对应的符号信息的文件。 ncmds:加载命令的数量,在头结构后面指示
sizeofcmds:表示该加载命令在头结构之后占用的字节数
flags:包含一组位标志,指示Mach-O 文件格式的某些可选功能的状态。这些是可用于操纵场的掩码:
MH_NOUNDEFS——目标文件在构建时不包含未定义的引用。 MH_INCRLINK——目标文件是相对于基类文件增量链接的输出,不能重复链接。 MH_DYLDLINK——该目标文件是动态链接器的输入,不能再次静态链接。 MH_TWOLEVEL——图像与辅助命名空间绑定。 MH_BINDATLOAD——动态拦截器应该在加载文件时绑定未定义的引用。 MH_PREBOUND——对文件的未定义引用是预绑定的。 MH_PREBINDABLE——对文件的未定义引用需要预绑定MH_NOFIXPREBINDING——动态链接器不会通知此可执行文件的预绑定代理。 MH_ALLMODSBOUND——指示此二进制文件绑定到其附属库的所有二级命名空间模块。仅当设置MH_PREBINDABLE 和MH_TWOLEVEL 时使用。 MH_CANONICAL——该文件已通过清除文件中的预绑定信息进行规范化。有关详细信息,请参阅redo_prebinding 手册页。 MH_SPLIT_SEGS - 此文件分隔只读和只写段MH_FORCE_FLAT - 可执行文件强制所有映像使用平面命名空间绑定。 MH_SUBSECTIONS_VIA_SYMBOLS——目标文件的各个部分可以分为单独的块。如果这些块没有被其他代码使用,那么它们就是死区。有关详细信息,请参阅Xcode 用户指南中的“链接”。 MH_NOMULTIDEFS——这个保护伞保证其子图像中没有符号的多重定义。因此,始终可以使用两级命名空间提示。
加载命令数据结构
加载命令位于目标文件中的标头之后,指定文件的逻辑结构及其在虚拟内存中的布局。每个加载命令都以指定命令类型和命令数据大小的字段开始。
load_command包含所有加载命令通用的字段。
结构体load_command
{
uint32_t 命令;
uint32_t cmdsize;
};字段cmd:表示加载命令类型的整数。表4 列出了有效的加载命令类型。
cmdsize:该字段指定命令数据结构的总字节大小。根据加载命令的类型,每个加载命令结构体包含不同的数据集,因此每组数据的大小可能不同。这组数据的大小在32 位架构中始终是4 的倍数,在64 位架构中始终是8 的倍数。如果加载命令数据不能被4 或8 整除(取决于目标架构是32 位还是64 位),则包含0 的字节将追加到末尾,直到可被整除。
讨论表4 列出了有效的加载命令类型以及每种类型的完整数据连接。
表4 Mach-O加载命令
命令数据结构用途LC_UUIDuuid_command(第20 页) 指定图像或其相应dSYM 文件的128 位UUIDLC_SEGMENTsegment_command。加载该文件时,定义需要映射到进程地址空间的文件段。并且每个段包含所有节LC_SYMTABsymtab_command指定文件的符号表。静态链接器和动态链接器在连接文件时都需要这些信息,并且调试器也可以用于将符号映射到生成该符号的原始源代码文件。 LC_DYSYMTABdysymtab_command 指定动态链接器使用的附带符号表信息LC_THREAD。 LC_UNIXTHREADthread_command 对于可执行文件,LC_UNIXTHREAD 命令定义进程主线程的线程状态。 LC_THREAD 与LC_UNIXTHREAD 相同,但LC_THREAD 不会导致内核分配堆栈。 LC_LOAD_DYLIBdylib_command 定义链接到该文件的动态共享库的名称。 LC_ID_DYLIBdylib_command 定义动态共享库安装名称LC_PREBOUND_DYLIBprebound_dylib_command 为了使此可执行文件与预绑定共享库链接,请指定共享库中要使用的模块。 LC_LOAD_DYLINKERdylinker_command 指定内核执行加载文件所需的动态链接器。 LC_ID_DYLINKERdylinker_command 标志该文件可用作动态链接器。 LC_ROUTINESroutines_command 包含共享库初始化例程的地址(由链接器的-init 选项指定)。 LC_ROUTINES_64routines_command_64 包含共享库的64 位初始化例程的地址(由链接器的-init 选项指定)。 LC_TWOLEVEL_HINTStwolevel_hints_command 包含二级命名空间查询提示表。 LC_SUB_FRAMEWORKsub_framework_command 将此文件标识为伞形框架的子框架的实现。伞框架的名称存储在字符串参数中。 (伞框架可以包含多个子框架,Apple 不建议这样使用。) LC_SUB_UMBRELLAsub_umbrella_command 指定此文件作为伞框架的子伞。 LC_SUB_LIBRARYsub_library_command 将此文件标记为伞形框架的字体库的实现。请注意,Apple 尚未定义子库支持的位置。 LC_SUB_CLIENTsub_client_command 子框架可以通过包含包含框架名称或包的客户端名称的LC_SUB_CLIENT 加载命令来显式允许另一个框架或包链接到它。uuid_command指定图像或匹配的dSYM 文件的128 位通用唯一标识符
结构体uuid_command
{
uint32_t 命令;
uint32_t cmdsize;
uint8_t uuid[16];
};字段cmd 设置此结构的LC_UUID。
cmdsize 设置sizeof(uuid_command)
uuid128位唯一标识符
segment_command指定组成段的32 位Mach-O 文件中的字节范围。这些字节由加载程序映射到程序的地址空间。在/usr/include/mach-o/loader.h 中声明。
结构段命令
{
uint32_t 命令;
uint32_t cmdsize;
字符段名[16];
uint32_t vmaddr;
uint32_t 虚拟机大小;
uint32_t 文件关闭;
uint32_t 文件大小;
vm_prot_t 最大prot;
vm_prot_t initprot;
uint32_t nsects;
uint32_t 标志;
};场地
该字段存在于所有加载命令的cmd结构中。设置二级结构的LC_SEGMENT。
cmdsize 该字段存在于所有加载命令结构中。对于此结构,将此字段设置为sizeof(segment_command) 加上所有后续段数据结构的大小(sizeof(segment_command + (sizeof(section) * segment-nsect))。
segname 以C 字符串作为名称的段。该字段值可以是任何ASCII 字符序列。 Apple 定义的段名称以两个下划线开头并由大写字母组成(如__TEXT 和__DATA)。该字段的长度固定为16字节。
vmaddr 指向虚拟内存地址中段开始的位置。
vmsize表示该段占用的虚拟内存字节数。请参阅下面的文件大小大小说明。
-fileoff 表示要映射到虚拟内存的数据文件相对于起始位置的偏移量。
文件大小指定该段在磁盘上占用的字节数。对于这些段,它们在运行时比构建时需要更多的内存,并且vmsize 大于文件大小。例如,为MH_EXECUTABLE 文件连接器生成的__PAGEZERO 段分配的虚拟内存大小为0x1000,但文件大小为0。由于__PAGEZERO 段中没有数据,因此直到运行时才会为其分配内存。此外,静态链接器通常会在__DATA 段之后分配未初始化的数据;在这些情况下,vmsize 大于文件大小。加载程序保证这种类型的任何内存都用零初始化。
maxprot表示该段允许和受保护的最大虚拟内存。
initprot 代表这个受保护的初始化虚拟内存。
-nsects 表示此加载命令之后的节数据结构的数量。
flags定义了一组标志,本节的加载会受到这组标志的影响:
SG_HIGHVM--该段的文件内容是虚拟内存空间的高位部分;下部用零填充(用于core文件中的堆栈); SG_NORELOC——该段没有任何内容移入其中,并且内部也没有任何内容。被搬进了里面。无需搬迁即可安全更换。segment_command_64表示由段组成的Mach-O 文件的字节范围。这些字节被映射到加载器加载器的地址空间。
结构体segment_command_64
{
uint32_t 命令;
uint32_t cmdsize;
字符段名[16];
uint64_t vmaddr;
uint64_t 虚拟机大小;
uint64_t 文件关闭;
uint64_t 文件大小;
vm_prot_t 最大prot;
vm_prot_t initprot;
uint32_t nsects;
uint32_t 标志;
};教派
ion_64定义64位节使用的元素。直接跟在一个segment_command_64数据结构后面的一组section_64数据结构,segment_command_64结构中,nsects是这组数据的准确数量。 struct section_64 { char sectname[16]; char segname[16]; uint64_t addr; uint64_t size; uint32_t offset; uint32_t align; uint32_t reloff; uint32_t nreloc; uint32_t flags; uint32_t reserved1; uint32_t reserved2; };字段 secname一个字符串,指定了这个节的名字。这个字段的值可以是任意序列的ASCII字符,苹果定义的名字由两个下划线开始,并且由小写字母组成(比如__text和__data)。这个字段的长度固定为16字节。 segname表示包含此节的段的字符串名字。为了紧凑性,中间对象文件--类型是MH_OBJECT只包含一个段,并且把所有的节都放在这个一个段中。静态连接器在构建最终产品时,会将每个节放到指定的段中(任意类型文件不仅是MH_OBJECT) addr一个整数表示了节的虚拟内存地址。 size一个整数标志此节占用的虚拟内存的字节大小 offset一个整数表示此节在文件中的偏移。 align一个整数表示节的字节对齐方式。这个整数是2的幂数;比如,具有8字节对齐的节的对齐值为3 reloff指定此部分的第一个重定位项的文件偏移量的整数。 nreloc一个整数,用于指定位于本节的重定位项的数目。 flags这个整数被分为两部分。最低有效8位包含节类型,最多的有效24位包含了一组标记,这些标记表示节的其他属性。这些类型和标记主要是静态连接器和文件分析工具在使用,比如otool,决定了要怎么修改和显示这些节。下面是有效类型: S_REGULAR--这个节没有特殊的类型。标准工具会创建这个类型的__TEXT的__text节。S_ZEROFILL--按需填充零的节--当这个section第一次读取或者写入的会后,每页都会自动的被包含0的字节填充。S_CSTRING_LITERALS--这个节仅包含C的常量字符串。标准工具会创建此类型的__TEXT段__cstringsection。S_4BYTE_LITEBALS--这个节仅包含4字节长的常量值。标准工具会创建此类型的__TEXT的__literal4节。S_8BYTE_LITERALS--这个节仅包含8字节长的常量值。标准工具会创建此类型的__TEXT的__literal8节。S_LITERAL_POINTERS--这个section仅包含常量值的指针。S_NON_LASY_SYMBOL_POINTERS--这个section仅包含非懒加载符号指针。标准工具会创建此类型的__DATA段的__nl_symbol_ptrs节S_SYMBOL_STUBS--这个section包含了符号桩(存根),标准工具会创建此类型的__TEXT的__symbol_stub节和__TEXT的__picsymbol_stub节。详情请看Mach-O编程主题中的“动态代码生成”。S_MOD_INIT_FUNC_POINTERS--这个section包含了模块初始化函数的指针。标准工具会此类型的__DATA的__mod_init_func节。S_MOD_TERM_FUNC_POINTERS--这个section包含了模块终止函数的指针。标准工具会创建此类型的__DATA的__mod_term_func节。S_COALESCED--本节包含由静态链接器(可能还有动态链接器)合并的符号。多个文件可以包含同一符号的合并定义,而不会导致多个定义符号错误。S_GB_ZEROFILL--这是一个零填充随需应变的section。他可能大于4GB。这个section一定要被放在仅包含零填充section的段中。如果你将一个零填充section放到一个非零填充的段中,会导致那些section用31位偏移访问不到。这个结果源于一个事实,即一个零填充的部分的大小可以大于4 GB(在32位地址空间中)。正如结果一样,静态连接器是不能构建输出文件的。讨论在Mach-O文件中的每个section都包含类型和一组属性标记。在中间对象文件中,这个类型和属性决定了静态连接器怎么将section拷贝到最终产品中。对象文件分析工具(例如otool)用类型和属性决定怎么读取和现实这些section。有些section类型和属性是动态连接器用到的。 这些是符号类型和属性的重要静态链接变体:Regular sections.在常规部分中,中间对象文件中只能存在外部符号的一个定义。如果发现任何重复的外部符号定义,静态链接器将返回一个错误。Coalesced sections.在最终产品中,静态连接器在每个合并section符号定义中只有一个实例。为了支持复杂的语言(比如C++的vtables和RTTI)编译器会为每个中间对象文件创建一个特别的符号定义。然后,静态连接器和动态连接器将多个定义减少到程序用的单个定义。Coalesced sections with weak definitions弱引用符号定义可能仅显示在合并section中。当用静态连接器查找一个符号的多个定义时,它将忽略一些合并符号定义中被设置为弱定义。如果这里没有非弱定义,第一个弱引用定义将被替换。这样设计是为了支持C++模板;它允许一个明确的模板实例去复写模糊的另一个模板。C++编译器会将明确的定义放到工整的section中,而且会把模糊的定义放到合并section中,标记为若定义。用弱定义构建的中间目标文件(以及静态存档库)只能在Mac OS X v10.2及更高版本的静态链接器中使用。如果最终产品不会被用到macOS更好版本中,就不会包含若定义。section_64定义了64位section用到的元素。是一个section_64数据结构的数组,直接放在segment_command_64数据结构后面,这个数组的数量由segment_command_64机构中的nsects字段决定。 struct section_64 { char sectname[16]; char segname[16]; uint64_t addr; uint64_t size; uint32_t offset; uint32_t align; uint32_t reloff; uint32_t nreloc; uint32_t flags; uint32_t reserved1; uint32_t reserved2; };字段sectname一个字符串指定了section的名称。这个字段值可以是任意序列的ASCII字符,然而苹果规定section名字的开始是两个下划线,后面由小写字母组成。这个字段最多16个字节。 segname一个字符串指定了最终包含此section的段的名字。为了紧凑性,类型为MH_OBJECT的中间对象文件仅包含一个段,并将所有的section放到里面。静态连接器。在静态连接器构建最终产品时,回见每个section放到指定的段中。(任意文件不仅限于类型是MH_OBJECT)。 addr整数,指定了section在虚拟内存中的地址。 size整数,指定了section在虚拟内存中所占大小。 offset整数,指定了section在文件中的偏移。 align整数,指定了section的字节对齐方式。这个整数是2的幂次方;比如,一个八字节对齐方式的section的align值是3. reloff整数,指定了此section第一个重定位入口的文件偏移。 nreloc整数,指定了此section重定位入口的数量,位于reloff。 flags这个整数被分为两部分。最低有效8位包含节类型,最多的有效24位包含了一组标记,这些标记表示节的其他属性。这些类型和标记主要是静态连接器和文件分析工具在使用,比如otool,决定了要怎么修改和显示这些节。下面是有效类型: S_REGULAR--这个节没有特殊的类型。标准工具会创建这个类型的__TEXT的__text节。S_ZEROFILL--按需填充零的节--当这个section第一次读取或者写入的会后,每页都会自动的被包含0的字节填充。S_CSTRING_LITERALS--这个节仅包含C的常量字符串。标准工具会创建此类型的__TEXT段__cstringsection。S_4BYTE_LITEBALS--这个节仅包含4字节长的常量值。标准工具会创建此类型的__TEXT的__literal4节。S_8BYTE_LITERALS--这个节仅包含8字节长的常量值。标准工具会创建此类型的__TEXT的__literal8节。S_LITERAL_POINTERS--这个section仅包含常量值的指针。S_NON_LASY_SYMBOL_POINTERS--这个section仅包含非懒加载符号指针。标准工具会创建此类型的__DATA段的__nl_symbol_ptrs节S_SYMBOL_STUBS--这个section包含了符号桩(存根),标准工具会创建此类型的__TEXT的__symbol_stub节和__TEXT的__picsymbol_stub节。详情请看Mach-O编程主题中的“动态代码生成”。S_MOD_INIT_FUNC_POINTERS--这个section包含了模块初始化函数的指针。标准工具会此类型的__DATA的__mod_init_func节。S_MOD_TERM_FUNC_POINTERS--这个section包含了模块终止函数的指针。标准工具会创建此类型的__DATA的__mod_term_func节。S_COALESCED--本节包含由静态链接器(可能还有动态链接器)合并的符号。多个文件可以包含同一符号的合并定义,而不会导致多个定义符号错误。S_GB_ZEROFILL--这是一个零填充随需应变的section。他可能大于4GB。这个section一定要被放在仅包含零填充section的段中。如果你将一个零填充section放到一个非零填充的段中,会导致那些section用31位偏移访问不到。这个结果源于一个事实,即一个零填充的部分的大小可以大于4 GB(在32位地址空间中)。正如结果一样,静态连接器是不能构建输出文件的。以下是一个部分可能的属性: S_ATTR_PURE_INSTRUCTIONS--这个section仅包含可执行机器指令。标准工具会为__TEXT的__text、__TEXT的__symbol_stub和__TEXT的__picsymbol_stub设置此flag。S_ATTR_SOME_INSTRUCTIONS--这个section包含了可执行机器指令。S_ATTR_NO_TOC--section包含了合并符号,而且它是不会被放到静态归档库内容的表中的。S_ATTR_EXT_RELOC--section包含了一定被重定位的引用。这些引用涉及到了其他文件存在的数据(未定义的符号)。为了支持重定位扩展,段中最大受保护的虚拟内存包含的section必须允许被读写。S_ATTR_LOC_RELOC--section包含了一定被重定位的引用。这些引用涉及到了文件中的数据。S_ATTR_STRIP_STATIC_SYMS--如果镜像mach_header头部中的结构设置了MH_DYLDLINK标志,那么这个section的静态符号是可以被去掉的。S_ATTR_NO_DEAD_STRIP--这部分不能拆装。详情可以参考Xcode用户指南中的"Linking"。S_ATTR_LIVE_SUPPORT--如果引用的代码是活跃的,但引用是不可检测的,则此部分不能被拆装。讨论在Mach-O文件中的每个section都包含类型和一组属性标记。在中间对象文件中,这个类型和属性决定了静态连接器怎么将section拷贝到最终产品中。对象文件分析工具(例如otool)用类型和属性决定怎么读取和现实这些section。有些section类型和属性是动态连接器用到的。 这些是符号类型和属性的重要静态链接变体:Regular sections.在常规部分中,中间对象文件中只能存在外部符号的一个定义。如果发现任何重复的外部符号定义,静态链接器将返回一个错误。Coalesced sections.在最终产品中,静态连接器在每个合并section符号定义中只有一个实例。为了支持复杂的语言(比如C++的vtables和RTTI)编译器会为每个中间对象文件创建一个特别的符号定义。然后,静态连接器和动态连接器将多个定义减少到程序用的单个定义。Coalesced sections with weak definitions弱引用符号定义可能仅显示在合并section中。当用静态连接器查找一个符号的多个定义时,它将忽略一些合并符号定义中被设置为弱定义。如果这里没有非弱定义,第一个弱引用定义将被替换。这样设计是为了支持C++模板;它允许一个明确的模板实例去复写模糊的另一个模板。C++编译器会将明确的定义放到工整的section中,而且会把模糊的定义放到合并section中,标记为若定义。用弱定义构建的中间目标文件(以及静态存档库)只能在Mac OS X v10.2及更高版本的静态链接器中使用。如果最终产品不会被用到macOS更好版本中,就不会包含若定义。twolevel_hints_command定义了LC_TWOLEVEL_HINTS加载命令的一些属性。 struct twolevel_hints_command { uint32_t cmd; uint32_t cmdsize; uint32_t offset; uint32_t nhints; };字段cmd 所有加载命令的结构都有,次结构设置为LC_TWOLEVEL_HINTS。 cmdsize 所有下载命令的结构都有。次结构,设置为sizeof(twoevel_hints_command)。 offset 表示从文件开始到twolevel_hint数据结构的数组的偏移量,称为二级命名空间提示表。 nhints 位于偏移处二级命名数据结构的数量。讨论当静态连接器构建一个二级命名空间镜像时,会增加LC_TWOLEVEL_HINTS加载命令和二级命名空间提示表在输出文件中。特殊注意事项默认,在HM_BUNDLE文件中ld是不包含LC_TWOLEVEL_HINTS命令或者耳机命名空间提示表的,因为这样命令的存在会导致Mac OS X v10.0装载动态连接器时会崩溃。如果你的代码仅运行在Mac OS X v10.1及之后版本,明确二级命名空间提示表可以被使用。twolevel_hint指定二级命名空间提示变的一个入口 struct twolevel_hint { uint32_t isub_image:8, itoc:24; };字段isub_image 定义符号中的子镜像。它是构成伞形镜像的镜像列表的索引。如果该字段为0,则符号在伞镜像本身中。如果镜像不是伞形框架或库,则此字段为0。 itoc 将符号索引放入由isub_image字段指定的镜像内容表中。讨论两级名称空间提示表为动态链接器提供了建议的位置,以便开始在当前镜像所链接的库中搜索符号。 在两级名称空间映像中,每个未定义的符号(即N_UNDF或N_PBUD类型的每个符号)在同一索引处的两级提示表中都有相应的条目。 当构建二级命名空间镜像时,静态连接器会添加LC_TWOLEVEL_HINTS加载命令和二级命名空间提示表到输出文件中。 默认情况下,连接器是不会将LC_TWOLEVEL_HINTS加载命令或者二级命名空间提示表加到MH_BUNDLE文件中的,因为这个load命令的存在会导致Mac OS X v10.0附带的动态链接器版本崩溃。lc_str定义一个可变长度字符串。 union lc_str { uint32_t offset; #ifndef __LP64__ char *ptr; #endifSymbol Table and Related Data Structure
两个加载指令LC_SYMTAB和LC_DYSYMTAB,描述了符号表的大小和位置以及其他元数据。本节中列出的其他数据结构表示符号表本身。symtab_command定义了LC_SYMTAB加载指令的属性。描述了符号表数据结构的大小和位置。 struct symtab_command { uint_32 cmd; uint_32 cmdsize; uint_32 symoff; uint_32 nsyms; uint_32 stroff; uint_32 strsize; };字段cmd 设置为LC_SYMTAB cmdsize 设置为sizeof(symtab_command) symoff 从文件开始到符号表入口位置的偏移量。符号表是nlist数据结构的数组。 nsyms 符号表入口数量的值 stroff 从镜像开始处到字符串表位置的偏移量。 strsize 字符串表的大小(用字节表示)讨论LC_SYMTAB应该同时存在于静态链接和动态链接的文件类型中。nlist_64描述了64位架构的符号表中的入口 struct nlist_64 { union { uint32_t n_strx; } n_un; uint8_t n_type; uint8_t n_sect; uint16_t n_desc; uint64_t n_value;【深度解析:Mac OS X ABI Mach体系结构全揭秘】相关文章:
用户评论
一直想知道Mach这东西是怎么工作的,看完这篇文章应该能有所了解吧!
有18位网友表示赞同!
MacOS的底层机制确实比较复杂,这个ABI Mach真是个大东西啊。
有8位网友表示赞同!
刚开始学C编程,对系统调用的理解真的很模糊,希望这篇文章能给我一些启示。
有15位网友表示赞同!
以前都是用Windows,后来开始接触苹果系统,发现MacOS的底层架构和其它系统确实不一样。
有11位网友表示赞同!
Mach这个名字听起来很有科幻的感觉,很期待看看它的具体功能。
有20位网友表示赞同!
想要更好地理解iOS系统,应该先搞清楚Mac OS X的体系结构吧!
有6位网友表示赞同!
这篇文章能深入浅出地讲解Mach吗?希望不是太晦涩难懂。
有19位网友表示赞同!
学习操作系统编程需要扎实的基础,这篇文章是不是一个不错的入门选择呢?
有15位网友表示赞同!
最近在研究苹果系统的安全模型,这个ABI Mach应该跟安全策略有关吧?
有17位网友表示赞同!
对嵌入式系统开发比较感兴趣,感觉了解Mach也能帮助我更好地理解不同平台的运作方式。
有11位网友表示赞同!
作为一名硬件工程师,也需要知道软件层面的架构设计才能把东西做更好啊!
有6位网友表示赞同!
这篇文章介绍的Mach有什么实际应用场景吗?能不能给我一些例子?
有9位网友表示赞同!
感觉Mach是个很重要的技术底层支持,学习它能让我更加了解计算机系统的运作方式。
有15位网友表示赞同!
我最近在想,会不会有更好的方案替代Mach呢?
有12位网友表示赞同!
Mach的历史发展历程怎么样?有没有相关的文献可以推荐?
有20位网友表示赞同!
MacOS的系统设计思想和其它开源的Linux非常不一样吗?
有14位网友表示赞同!
这篇文章能比较一下Mach和其它类似的技术架构吗?
有19位网友表示赞同!
学习一个新的技术体系真的需要很长时间,希望这篇文章能给我一些方向和目标!
有12位网友表示赞同!