A calling convention 是 a binary contract 用于 参数 placement, results, preserved state, 和 stack layout. Linux x86-64 programs 使用 the System V AMD64 函数 ABI; 32-bit programs 使用 the System V i386 ABI. Linux 系统 调用 使用 separate entry ABIs 和 必须 不 be treated 作为 ordinary 函数 calls.
AMD64 函数调用 #
AMD64 ABI first classifies each argument. Integer-class values, including
ordinary integers 和 pointers, 使用 rdi, rsi, rdx, rcx, r8, 和 r9.
SSE-class values 使用 xmm0 通过 xmm7. Exhausting one class does 不 由 itself
exhaust the other; arguments cannot be assigned according 到 the classification
rules go in the stack input area.
Aggregates 是 divided 到 eightbyte classes. 一个 qualifying small structure can
therefore occupy multiple integer 或 vector registers, 同时 a memory-class
structure 是 passed 在 the stack. For a memory-class 返回 value, the caller
allocates storage 和 passes its address in hidden 参数 register rdi; the
callee returns address in rax.
Integer-class 返回 values 使用 rax 和 then rdx; SSE-class results 使用 xmm0
and then xmm1. Floating-point float 和 double results 使用 xmm0, 同时 x87
classes such 作为 long double 使用 the x87 stack 作为 specified 由 the ABI.
rbx, rbp, r12 通过 r15, 和 rsp 是 callee-saved. 一个 callee changes
one 必须 restore it 在……之前 ret. Other general-purpose registers 和 all vector
registers 是 caller-saved under the base System V ABI, so a caller preserves any
live values it needs 在……之后 a call.
Immediately 在……之前 call, rsp 是 16-byte aligned. pushed eight-byte return
address means an ordinary callee enters,使用 rsp % 16 == 8. Stack arguments are
laid out,使用 the padding required 由 their alignment; they 是 不 simply pushed
one 在 a time. ABI also reserves a 128-byte red zone below the current rsp.
Leaf 用户空间 functions 可以 使用 it without adjusting the stack because normal
signal delivery preserves it. Kernel 代码 和 代码 built,使用 -mno-red-zone must
not rely 在 area.
可变参数函数 #
Fixed arguments 到 a variadic 函数 使用 the normal classification algorithm. A
caller additionally sets %al 到 an upper bound, 来自 0 通过 8, 在 the number of
vector 参数 registers used. Only the low byte 是 defined 用于 this purpose. A
handwritten 调用 到 printf leaves %al unspecified therefore violates the
ABI even if every visible 参数 appears correct.
A variadic callee 使用 va_start 提供 a register-save area in its own
frame 和 saves the 参数 registers it may need. AMD64 va_list tracks:
gp_offset和fp_offsetwithin save area;overflow_arg_area用于 arguments passed in memory; andreg_save_area, the base 的 the saved register arguments.
va_arg applies type classification 和 alignment 同时 advancing the relevant
location. Because default 参数 promotions apply, a caller passes float as
double 和 narrow integer types 作为 int 或 unsigned int where required.
i386 函数调用 #
i386 System V ABI passes ordinary arguments in the stack input area in
right-to-left order. At entry, 0(%esp) 包含 the 返回 address 和 4(%esp)
contains the first argument. caller removes arguments 在……之后 返回 under the
standard C convention.
ebx, esi, edi, ebp, 和 esp 是 callee-saved; eax, ecx, 和 edx are
caller-saved. 一个 integer 或 pointer 结果 使用 eax, 和 a two-word integer 可以 use
edx:eax; floating-point results 使用 st(0). Structure 返回 conventions depend
on classification 和 可以 使用 a hidden destination pointer.
A conventional frame-pointer prologue is:
push ebp
mov ebp, esp
sub esp, local_sizeebp 是 不 intrinsically a frame pointer, so optimized 代码 可以 omit this sequence.
Historical i386 代码 assumed four-byte stack alignment. current GNU/Linux i386
ABI 需要 16-byte alignment 在 a 调用 boundary, 和 compilers 可以 realign entry
when interoperating,使用 legacy callers. Handwritten assembly 必须 follow the ABI
selected 由 its toolchain rather than infer alignment 来自 old examples.
Linux 系统调用 #
x86-64 Linux syscall ABI is:
| Purpose | Registers |
|---|---|
| System-call number | rax |
| Arguments 1–6 | rdi, rsi, rdx, r10, r8, r9 |
| Result | rax |
Unconditionally clobbered 由 syscall | rcx, r11 |
Hardware saves the user 返回 address in rcx 和 user flags in r11; the fourth
argument therefore 使用 r10, unlike an AMD64 函数 call. 内核 returns
either a successful 值 或 a negative 错误 in rax; values 来自 -1 through
-4095 encode -errno. 一个 libc wrapper converts such an 错误 到 -1 和 sets
thread-local errno.
; x86-64 Linux: write(1, message, message_length)
mov eax, 1
mov edi, 1
lea rsi, [rel message]
mov edx, message_length
syscallFor an i386 进程 using int $0x80, eax holds the number, arguments 使用 ebx,
ecx, edx, esi, edi, 和 ebp, 和 eax holds the result. These registers
are syscall operands even though ebx, esi, edi, 和 ebp 是 callee-saved in
the i386 函数 ABI; a 函数 containing inline entry 代码 必须 still preserve
the 函数 ABI 用于 its caller.
函数 ABI 与系统调用 ABI #
An AMD64 函数 调用 使用 rcx 用于 参数 four 和 preserves rbx, rbp, and
r12–r15; an x86-64 syscall 使用 r10 用于 参数 four 和 has explicit
rcx/r11 clobbers. 一个 函数 returns errors according 到 its declared API,
whereas the raw 内核 boundary 使用 negative 错误 codes. syscall instruction
also changes privilege level 和 does 不 push a 返回 address 或 switch stacks by
itself; Linux entry 代码 performs the required kernel-stack transition.
Application 代码 应当 prefer libc wrappers because they implement both contracts, error translation, cancellation behavior where applicable, 和 vDSO selection. Inline assembly 必须 describe register, condition-code, 和 内存 effects 到 the compiler 和 是 appropriate primarily 用于 C libraries 和 runtimes.
System V AMD64 psABI,
syscall(2), 和 Linux
entry_64.S
are the primary 参考资料用于 these contracts.
评论
在 GitHub 上阅读或评论(发表评论需要登录)