type
Post
status
Published
slug
2022/12/11/wiki-osdev-org-System-V-ABI
summary
tags
osdev
category
osdev
icon
password
new update day
Property
Oct 22, 2023 01:31 PM
created days
Last edited time
Oct 22, 2023 01:31 PM
System V ABI(System V应用二进制接口)是一组规范,用于描述符合X/Open Common Application Environment Specification和System V Interface Definition的系统的调用约定、对象文件格式、可执行文件格式、动态链接语义等等。它是当今主要的Unix操作系统(如Linux,BSD系统等)所使用的标准ABI。可执行链接文件格式(ELF)是System V ABI的一部分。
ABI被组织成一个便携式的基本文档和平台特定的补充文档,用于填补空白。在新平台(如X86-64)中采用该格式时,未经官方批准的新架构处理器补充规范已被公布。该标准是可扩展的,随着Unix供应商添加新功能,该格式也在不断发展。由于许多非官方补充规范和Unix操作系统的混乱历史,目前的情况是 System V ABI 已成为一个非官方草案规范家族,没有真正的中央管理机构。
许多高级功能(例如动态链接)都是可选的,加载简单的静态链接 ELF 程序也很简单。该标准的早期版本更加雄心勃勃,试图标准化软件包安装格式和 X11 细节,而这些过时的细节在今天被忽视了。 常见的操作系统开发工具(如Binutils和GCC)很好地理解了ABI。 诸如i686-elf-gcc的工具链根据这个ABI生成代码和可执行文件。

1 Executable and Linkable Format

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

2 Calling Convention

这是对主要 System V ABI 架构的重要调用约定细节的简短概述。这是一个不完整的帐户,您应该查阅相关的处理器补充文件以了解详细信息。此外,您可以使用 -S 编译器选项在调用汇编器之前停止编译过程,这样您就可以研究编译器如何按照相关调用约定将代码转换为汇编程序。

2.1 i386

这是一个 32 位平台。堆栈向下增长。函数的参数以相反的顺序在堆栈上传递,这样第一个参数是最后一个压入堆栈的值,然后它将成为堆栈中的最低值。在堆栈上传递的参数可能会被调用的函数修改。使用 call 指令调用函数,该指令将下一条指令的地址压入堆栈并跳转到操作数。函数使用 ret 指令返回调用者,该指令从堆栈中弹出一个值并跳转到该值。在调用指令被调用之前,堆栈将保持 16 字节对齐
函数保留寄存器 ebx、esi、edi、ebp 和 esp;而 eax、ecx、edx 是临时寄存器。返回值存储在 eax 寄存器中,或者如果它是 64 位值,则高 32 位进入 edx。函数压入 ebp,使 caller-return-eip(调用者返回EIP) 在其上方 4 个字节,并将 ebp 设置为保存的 ebp 的地址。这允许遍历现有的堆栈帧。这可以通过指定 -fomit-frame-pointer GCC 选项来消除它。
作为一个特殊的例外,GCC 假设堆栈没有正确对齐,并在进入 main 或设置函数的属性 ((force_align_arg_pointer)) 时重新对齐它。

2.2 x86-64

这是一个 64 位平台。堆栈向下增长。函数的参数在寄存器 rdi、rsi、rdx、rcx、r8、r9 中传递,更多的值以相反的顺序传递到堆栈中。在堆栈上传递的参数可能会被调用的函数修改。使用 call 指令调用函数,该指令将下一条指令的地址压入堆栈并跳转到操作数。函数使用 ret 指令返回调用者,该指令从堆栈中弹出一个值并跳转到该值。在调用指令被调用之前,堆栈将保持16字节对齐。
函数保留寄存器 rbx、rsp、rbp、r12、r13、r14 和 r15;而 rax、rdi、rsi、rdx、rcx、r8、r9、r10、r11 是临时寄存器。返回值存储在 rax 寄存器中,或者如果它是 128 位值,则较高的 64 位进入 rdx。可选地,函数将 rbp 入栈,使得 caller-return-rip 在其上方 8 个字节,并将 rbp 设置为保存的 rbp 的地址。这允许遍历现有的堆栈帧。这可以通过指定 -fomit-frame-pointer GCC 选项来消除。
信号处理程序在同一个堆栈上执行,但在将任何内容压入堆栈之前,会从堆栈中减去称为红色区域的 128 个字节。这允许小叶函数使用 128 字节的堆栈空间,而无需通过从堆栈指针中减去来保留堆栈空间。众所周知,红色区域会给 x86-64 内核开发人员带来问题,因为 CPU 本身在调用中断处理程序时不遵守红色区域。由于 ABI 与 CPU 行为相矛盾,这会导致微妙的内核损坏。解决方案是使用 -mno-red-zone 构建所有内核代码,或者在另一个堆栈上处理内核模式中的中断(从而实现ABI)。

3 See Also

3.1 Documents

TODO: Ensure whether these are the latest official links. These documents are simply what I could find through a quick online search.

3.2 External Links

 
欢迎加入喵星计算机技术研究院,原创技术文章第一时间推送。
notion image
 
wiki.osdev.org 系列之 - Symbol Table一个适合新手学习 ebpf 的循序渐进的学习方案