以下链接解释了 UNIX(BSD 风格)和 Linux 的 x86-32 系统调用约定:
但是 UNIX 和 Linux 上的 x86-64 系统调用约定是什么?
最佳答案
进一步阅读此处的任何主题:The Definitive Guide to Linux System Calls
我在 Linux 上使用 GNU Assembler (gas) 验证了这些。
内核接口(interface)
x86-32 又名 i386 Linux 系统调用约定:
在 x86-32 中,Linux 系统调用的参数是使用寄存器传递的。 %eax
对于 syscall_number。 %ebx, %ecx, %edx, %esi, %edi, %ebp 用于将 6 个参数传递给系统调用。
返回值在 %eax
.所有其他寄存器(包括 EFLAGS)都保留在 int $0x80
中。 .
我从 Linux Assembly Tutorial 中获取了以下片段但我对此表示怀疑。如果有人能举个例子,那就太好了。
If there are more than six arguments,
%ebx
must contain the memory location where the list of arguments is stored - but don't worry about this because it's unlikely that you'll use a syscall with more than six arguments.
有关示例和更多阅读内容,请参阅 http://www.int80h.org/bsdasm/#alternate-calling-convention .另一个用于 i386 Linux 的 Hello World 示例,使用
int 0x80
:Hello, world in assembly language with Linux system calls?有一种更快的方法来进行 32 位系统调用:使用
sysenter
.内核将一页内存映射到每个进程(vDSO),用户空间端为 sysenter
舞蹈,它必须与内核合作才能找到返回地址。注册映射的参数与 int $0x80
相同.您通常应该调用 vDSO 而不是使用 sysenter
直接地。 (有关链接和调用 vDSO 的信息,以及有关 sysenter
的更多信息,以及与系统调用有关的所有其他信息,请参阅 The Definitive Guide to Linux System Calls。)x86-32 [Free|Open|Net|DragonFly]BSD UNIX 系统调用约定:
参数在堆栈上传递。将参数(最先推送的最后一个参数)压入堆栈。然后再推送一个额外的 32 位虚拟数据(它实际上不是虚拟数据。请参阅以下链接了解更多信息),然后给出系统调用指令
int $0x80
http://www.int80h.org/bsdasm/#default-calling-conventionx86-64 Linux 系统调用约定:
(注意:x86-64 Mac OS X is similar but different 来自 Linux。TODO:检查 *BSD 做了什么)
请参阅 System V Application Binary Interface AMD64 Architecture Processor Supplement 的“A.2 AMD64 Linux 内核约定”部分.可以找到 i386 和 x86-64 System V psABI 的最新版本 linked from this page in the ABI maintainer's repo . (另请参阅 x86 标签 wiki 以获得最新的 ABI 链接和许多其他关于 x86 asm 的好东西。)
这是本节的片段:
- User-level applications use as integer registers for passing the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9. The kernel interface uses %rdi, %rsi, %rdx, %r10, %r8 and %r9.
- A system-call is done via the
syscall
instruction. This clobbers %rcx and %r11 as well as the %rax return value, but other registers are preserved.- The number of the syscall has to be passed in register %rax.
- System-calls are limited to six arguments, no argument is passed directly on the stack.
- Returning from the syscall, register %rax contains the result of the system-call. A value in the range between -4095 and -1 indicates an error, it is
-errno
.- Only values of class INTEGER or class MEMORY are passed to the kernel.
请记住,这是来自 ABI 的特定于 Linux 的附录,即使对于 Linux,它也是信息性的而非规范性的。 (但实际上它是准确的。)
这个 32 位
int $0x80
ABI 可用于 64 位代码(但强烈不推荐)。 What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?它仍然将其输入截断为 32 位,因此它不适合指针,并将 r8-r11 置零。用户界面:函数调用
x86-32 函数调用约定:
在 x86-32 中,参数在堆栈上传递。最后一个参数首先被压入堆栈,直到所有参数都完成,然后
call
指令被执行。这用于从汇编调用 Linux 上的 C 库 (libc) 函数。i386 System V ABI 的现代版本(在 Linux 上使用)需要
%esp
的 16 字节对齐。之前 call
,就像一直需要的 x86-64 System V ABI。允许被调用者假设并使用在未对齐时出错的 SSE 16 字节加载/存储。但从历史上看,Linux 只需要 4 字节堆栈对齐,因此即使为 8 字节 double
保留自然对齐的空间也需要额外的工作。或者其他的东西。其他一些现代 32 位系统仍然不需要超过 4 字节的堆栈对齐。
x86-64 System V 用户空间函数调用约定:
x86-64 System V 在寄存器中传递 args,这比 i386 System V 的堆栈 args 约定更有效。它避免了将 args 存储到内存(缓存)然后在被调用者中再次加载它们的延迟和额外指令。这很有效,因为有更多可用的寄存器,并且更适合延迟和乱序执行很重要的现代高性能 CPU。 (i386 ABI 很旧了)。
在这种新机制中:首先将参数分为几类。每个参数的类决定了它传递给被调用函数的方式。
有关完整信息,请参阅:System V Application Binary Interface AMD64 Architecture Processor Supplement 的“3.2 函数调用序列”部分内容如下:
Once arguments are classified, the registers get assigned (in left-to-right order) for passing as follows:
- If the class is MEMORY, pass the argument on the stack.
- If the class is INTEGER, the next available register of the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9 is used
所以
%rdi, %rsi, %rdx, %rcx, %r8 and %r9
是用于将整数/指针(即 INTEGER 类)参数从汇编传递给任何 libc 函数的寄存器。 %rdi 用于第一个 INTEGER 参数。 %rsi 表示第二个,%rdx 表示第三个,依此类推。然后call
应给予指示。当 %rsp
时,堆栈 ( call
) 必须是 16B 对齐的执行。如果有超过 6 个 INTEGER 参数,则第 7 个 INTEGER 参数及之后的参数将在堆栈上传递。 (来电弹出,与 x86-32 相同。)
前 8 个浮点参数在 %xmm0-7 中传递,稍后在堆栈中。没有调用保留的向量寄存器。 (混合了 FP 和整数参数的函数可以有 8 个以上的寄存器参数。)
可变参数函数 ( like
printf
) 总是需要 %al
= FP 寄存器 args 的数量。何时将结构打包到寄存器(返回时
rdx:rax
)与内存中是有规则的。有关详细信息,请参阅 ABI,并检查编译器输出以确保您的代码与编译器就应如何传递/返回某些内容达成一致。请注意 the Windows x64 function calling convention与 x86-64 System V 有多个显着差异,例如调用者必须保留的阴影空间(而不是红色区域)和调用保留的 xmm6-xmm15。并且对于哪个 arg 进入哪个寄存器非常不同的规则。
关于linux - i386 和 x86-64 上的 UNIX 和 Linux 系统调用(和用户空间函数)的调用约定是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2535989/