System V ABI

来自osdev
Zhang3讨论 | 贡献2022年3月30日 (三) 02:41的版本 (创建页面,内容为“'''System V Application Binary Interface''' 是一组规范,详细介绍了 调用约定 目标文件格式 可执行文件格式、动态链接语义,对于符合 “X/Open通用应用程序环境规范” 和 “系统V接口定义” 的系统,以及还有更多内容。 如今,它是主要Unix操作系统(如Linux、BSD系统和许多其他系统)使用的标准ABI。 Execut…”)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳到导航 跳到搜索

System V Application Binary Interface 是一组规范,详细介绍了 调用约定 目标文件格式 可执行文件格式、动态链接语义,对于符合 “X/Open通用应用程序环境规范” 和 “系统V接口定义” 的系统,以及还有更多内容。 如今,它是主要Unix操作系统(如Linux、BSD系统和许多其他系统)使用的标准ABI。 Executable and Linkable Format(ELF)是System V ABI的一部分。

ABI被组织为可移植的基础文档,以特定于平台的补充填补空白。 非官方的新体系结构处理器补充已经发布,因为该格式已经适应了X86-64等新平台。 该标准是可扩展的,并且随着Unix供应商添加新功能,该格式也在不断发展。 由于许多非官方的补充规范和Unix操作系统的混乱历史,目前的情况是System V ABI已经成为一个非官方的草案规范家族,而没有真正的中央管理机构。

动态链接等许多高级功能都是可选的,加载简单的静态链接ELF程序非常简单。 该标准的早期版本更加雄心勃勃,试图标准化软件包安装格式和X11细节,而这些过时的细节今天已被忽略了。 常见的操作系统开发工具 (例如 BinutilsGCC) 可以很好地理解ABI。 诸如i686 elf gcc之类的工具链根据这个ABI生成代码和可执行文件。

Executable and Linkable Format

正文: Executable and Linkable Format

在System V ABI中,Executable and Linkable Format被标准化为一种可适应的文件格式。 每个处理器补充通过声明ELF格式结构中使用的抽象类型的大小以及大小端约定(endianess)来巧妙地更改文件格式。 这使得骨架文件格式能够适应多处理器体系结构,其中32位和64位系统之间的差异可以通过简单地增加各种头字段的大小来处理。 该格式功能强大,足以包含辅助信息,如调试信息、动态库的重新定位和其他供应商特定的杂项信息。 这允许对目标文件和链接的可执行文件使用相同的格式。

调用约定

本章节是对主要System V ABI架构的重要调用约定细节的简短概述。 这里是一个不完整的叙述,您应该咨询相关的处理器补充文档以获取详细信息。 此外,您可以使用-S编译器选项在调用汇编程序之前停止编译过程,这可以让您研究编译器如何按照相关调用约定将代码翻译为汇编程序。

i386

i386是一个32位平台。 栈向下生长。(译者注:内存地址由高到低使用) 函数的参数以相反的顺序在堆栈上传递,这样第一个参数就是推送到堆栈的最后一个值,它将是堆栈上的最低值。 传递到堆栈上的参数可以由调用的函数修改。 使用 call 指令调用函数,该指令将下一条指令的地址推送到堆栈并跳转到操作数。 函数使用ret指令返回调用者,该指令从堆栈中弹出一个值并跳转到它。 在调用CALL指令之前,堆栈是16字节对齐的。

函数保留寄存器 ebxesiediebpesp; 而 eaxecxedx 是暂存寄存器。 返回值存储在eax寄存器中,或者如果是64位值,则较高的32位进入edx。 函数推送ebp,使得caller-return-eip在其上方4个字节,并将ebp设置为保存的ebp的地址。 这允许遍历现有的堆栈帧。 这可以通过指定-fomit frame pointerGCC选项来消除。

作为一个特殊的例外,GCC假定堆栈没有正确对齐,并在进入main或在函数上设置了attribute ((force_align_arg_pointer)) 时重新对齐堆栈。

x86-64

x86-64是一个64位的平台。 堆栈向下生长。 函数的参数在rdirsirdxrcxr8r9寄存器中传递,并以相反的顺序在堆栈上传递其他值。 传递到堆栈上的参数可以由被调用的函数修改。 使用call指令调用函数,该指令将下一条指令的地址压入堆栈并跳转到操作数。 函数使用 ret 指令返回给调用者,该指令从堆栈中弹出一个值并跳转到该值。 在调用call指令之前,堆栈是16字节对齐的。

函数保存寄存器rbxrsprbpr12r13r14r15;而raxrdirsirdxrcxr8r9r10r11都是暂存寄存器。 返回值存储在 rax 寄存器中,或者如果它是128位值,则更高的64位进入 rdx。 可选,函数推送rbp,使调用方返回的rip在其上方8个字节,并将rbp设置为保存的rbp的地址。 这允许对现有堆栈帧进行迭代。 这可以通过指定 -fomit-frame-pointer GCC选项来消除。

信号处理程序在相同的堆栈上执行,但在将任何内容推送到堆栈之前,会从堆栈中减去128个字节,称为红色区域。 这允许leaf函数使用128字节的堆栈空间,而无需通过从堆栈指针中减去来保留堆栈空间。 众所周知,红色区域会给x86-64内核开发人员带来问题,因为在调用中断处理程序时,CPU本身不遵守红色区域。 这会导致微妙的内核中断,因为ABI与CPU行为相矛盾。 解决方案是使用 -mno-red-zone 构建所有内核代码,或者通过在除当前堆栈之外的另一个堆栈中以内核模式处理中断 (从而实现ABI)。

另见

文档

TODO: 确保这些是否是最新的官方链接。 以下列出了一些我可以通过快速在线搜索找到的文档。”

外部链接