system V ABI
译自 system V ABI
System V应用程序二进制接口是一组规范,详细说明了调用约定,目标文件格式,可执行文件格式,动态链接语义,以及符合X / Open通用应用程序环境规范和System V接口定义的系统的更多规范。现在,它已成为主要的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生成代码和可执行文件。
可执行和可链接格式
可执行和可链接格式在System V ABI中被标准化为可修改的文件格式。处理器补充通过声明ELF格式结构中使用的抽象类型的大小以及字节序,可以巧妙地更改文件格式。 这使框架文件格式适用于多种处理器体系结构,其中通过简单地增加各种标头字段的大小来处理32位和64位系统之间的差异。 该格式足够强大,可以包含辅助信息,例如调试信息,动态库的重定位以及其他特定于供应商的其他信息。 这允许对目标文件和链接的可执行文件使用相同的格式。
调用约定
这是System V ABI主要体系结构的重要调用约定详细信息的简短描述。 所列出的信息并不完整,您应查阅相关的处理器补充文件(psABI)以了解详细信息。 此外,您可以使用编译器的-S选项在调用汇编器之前停止编译过程,这使您可以研究编译器如何按照相关的调用约定将代码转换为汇编。
-
i 386
这是一个32位平台。 堆栈向下生长。 函数的参数以相反的顺序传递到堆栈上,以便第一个参数是被压入堆栈的最后一个值,然后将成为堆栈上的最小值(译注:这里的值指地址)。 可以通过修改被调函数的参数来修改在堆栈上传递的参数。 使用call
指令来调用函数,该指令将下一条指令的地址压入堆栈并跳转到操作数。 函数使用ret
指令返回调用者,该指令从堆栈中弹出一个值并跳转到该值。 在调用call
指令之前,堆栈是16
字节对齐的。函数保留寄存器
ebx
,esi
,edi
,ebp
和esp
; 而eax
,ecx
,edx
是暂存器。 返回值存储在eax
寄存器中,或者如果返回值是64
位的,则高32
位进入edx
,低32
位进入eax
。 被调函数将ebp
推入堆栈,这样紧挨着主调函数栈帧的栈顶,即此时caller-return-eip
位于ebp
上方4
个字节处,然后将ebp
设置为已保存ebp
的地址。 这允许遍历现有堆栈帧。 通过指定-fomit-frame-pointer
GCC选项可以消除此问题。作为特殊的例外,GCC假定堆栈未正确对齐,并在输入main或在函数上设置了属性
((force_align_arg_pointer))
时将其重新对齐。 -
x86-64
这是一个64位平台。 堆栈向下生长。 函数的参数在寄存器rdi,rsi,rdx,rcx,r8,r9中传递,并且其他值以相反的顺序在堆栈中传递。译注前6个从左到右依次放入rdi,rsi,rdx,rcx,r8,r9
,超出6个的参数从右向左放入栈中
。可以通过修改被调用函数的参数来修改在堆栈上传递的参数。 使用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)。
叶子函数参考