请参阅 系统应用二进制接口 AMD64架构处理器补充的“ A. 2 AMD64 Linux核心约定”一节。可以找到 i386和 x86-64 System V psABI 的最新版本 链接到 ABI 维护者的回购。(请参阅 X86标签 wiki 获取最新的 ABI 链接以及许多其他关于 x86 asm 的好东西。)
请记住,这是从特定于 Linux 的 ABI 附录中摘录的,即使对于 Linux 来说,它也是信息性的,而不是规范性的。(但事实上它是准确的。)
这个32位 int $0x80 ABI 是可用于64位代码(但强烈不推荐)。它仍然将其输入截断为32位,因此它不适合指针,并且它的零是 r8-r11。
用户界面: 函数调用
X86-32函数调用约定:
在 x86-32中,参数在堆栈上传递。最后一个参数被首先推送到堆栈上,直到所有参数都完成,然后执行 call指令。这用于从汇编调用 Linux 上的 C 库(libc)函数。
现代版本的 i386 System V ABI (在 Linux 上使用)需要在 call之前对 %esp进行16字节的对齐,就像 x86-64 System V ABI 一直需要的那样。被调用方可以假定这一点,并使用 SSE 16字节加载/存储未对齐时出错的内容。但是从历史上看,Linux 只需要4字节的堆栈对齐,所以即使为8字节的 double或其他东西保留自然对齐的空间也需要额外的工作。
其他一些现代32位系统仍然不需要超过4字节的堆栈对齐。
X86-64 System V 用户空间函数调用约定:
X86-64 System V 在寄存器中传递参数,这比 i386 System V 的堆栈参数约定更有效。它避免了将参数存储到内存(缓存)然后在被调用方中再次加载它们的延迟和额外指令。这种方法工作得很好,因为有更多的寄存器可用,而且更适用于现代高性能 CPU,因为它的延迟和乱序执行很重要。(i386ABI 非常古老)。
调用约定定义了当其他程序调用或被其他程序调用时参数在寄存器中的传递方式。这些约定的最佳来源是为每个硬件定义的 ABI 标准。为了便于编译,用户空间和内核程序也使用相同的 ABI。Linux/Freebsd 对 x86-64采用相同的 ABI,对32位采用另一套 ABI。但是 x86-64的 ABI 不同于 Linux/FreeBSD。而且通常 ABI 不区分系统调用和普通的“函数调用”。
比如,下面是一个特殊的 x86 _ 64调用约定示例,它适用于 Linux 用户空间和内核: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/(注意参数的序列 a,b,c,d,e,f) :
/*
* 64-bit SYSCALL instruction entry. Up to 6 arguments in registers.
*
* This is the only entry point used for 64-bit system calls. The
* hardware interface is reasonably well designed and the register to
* argument mapping Linux uses fits well with the registers that are
* available when SYSCALL is used.
*
* SYSCALL instructions can be found inlined in libc implementations as
* well as some other programs and libraries. There are also a handful
* of SYSCALL instructions in the vDSO used, for example, as a
* clock_gettimeofday fallback.
*
* 64-bit SYSCALL saves rip to rcx, clears rflags.RF, then saves rflags to r11,
* then loads new ss, cs, and rip from previously programmed MSRs.
* rflags gets masked by a value from another MSR (so CLD and CLAC
* are not needed). SYSCALL does not save anything on the stack
* and does not change rsp.
*
* Registers on entry:
* rax system call number
* rcx return address
* r11 saved rflags (note: r11 is callee-clobbered register in C ABI)
* rdi arg0
* rsi arg1
* rdx arg2
* r10 arg3 (needs to be moved to rcx to conform to C ABI)
* r8 arg4
* r9 arg5
* (note: r12-r15, rbp, rbx are callee-preserved in C ABI)
*
* Only called from user space.
*
* When user can change pt_regs->foo always force IRET. That is because
* it deals with uncanonical addresses better. SYSRET has trouble
* with them due to bugs in both AMD and Intel CPUs.
*/
/*
* 32-bit SYSENTER entry.
*
* 32-bit system calls through the vDSO's __kernel_vsyscall enter here
* if X86_FEATURE_SEP is available. This is the preferred system call
* entry on 32-bit systems.
*
* The SYSENTER instruction, in principle, should *only* occur in the
* vDSO. In practice, a small number of Android devices were shipped
* with a copy of Bionic that inlined a SYSENTER instruction. This
* never happened in any of Google's Bionic versions -- it only happened
* in a narrow range of Intel-provided versions.
*
* SYSENTER loads SS, ESP, CS, and EIP from previously programmed MSRs.
* IF and VM in RFLAGS are cleared (IOW: interrupts are off).
* SYSENTER does not save anything on the stack,
* and does not save old EIP (!!!), ESP, or EFLAGS.
*
* To avoid losing track of EFLAGS.VM (and thus potentially corrupting
* user and/or vm86 state), we explicitly disable the SYSENTER
* instruction in vm86 mode by reprogramming the MSRs.
*
* Arguments:
* eax system call number
* ebx arg1
* ecx arg2
* edx arg3
* esi arg4
* edi arg5
* ebp user stack
* 0(%ebp) arg6
*/
/* The Linux/x86-64 kernel expects the system call parameters in
registers according to the following table:
syscall number rax
arg 1 rdi
arg 2 rsi
arg 3 rdx
arg 4 r10
arg 5 r8
arg 6 r9
The Linux kernel uses and destroys internally these registers:
return address from
syscall rcx
eflags from syscall r11
Normal function call, including calls to the system call stub
functions in the libc, get the first six parameters passed in
registers and the seventh parameter and later on the stack. The
register use is as follows:
system call number in the DO_CALL macro
arg 1 rdi
arg 2 rsi
arg 3 rdx
arg 4 rcx
arg 5 r8
arg 6 r9
We have to take care that the stack is aligned to 16 bytes. When
called the stack is not aligned since the return address has just
been pushed.
Syscalls of more than 6 arguments are not supported. */