i386 和 x86-64 上的 UNIX 和 Linux 系统调用的调用约定是什么

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2535989/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 19:54:01  来源:igfitidea点击:

What are the calling conventions for UNIX & Linux system calls on i386 and x86-64

linuxunixassemblyx86-64abi

提问by claws

Following links explain x86-32 system call conventions for both UNIX (BSD flavor) & Linux:

以下链接解释了 UNIX(BSD 风格)和 Linux 的 x86-32 系统调用约定:

But what are the x86-64 system call conventions on both UNIX & Linux?

但是 UNIX 和 Linux 上的 x86-64 系统调用约定是什么?

采纳答案by claws

Further reading for any of the topics here: The Definitive Guide to Linux System Calls

进一步阅读此处的任何主题:Linux 系统调用权威指南



I verified these using GNU Assembler (gas) on Linux.

我在 Linux 上使用 GNU Assembler (gas) 验证了这些。

Kernel Interface

内核接口

x86-32 aka i386 Linux System Call convention:

x86-32 又名 i386 Linux 系统调用约定:

In x86-32 parameters for Linux system call are passed using registers. %eaxfor syscall_number. %ebx, %ecx, %edx, %esi, %edi, %ebp are used for passing 6 parameters to system calls.

在 x86-32 中,Linux 系统调用的参数是使用寄存器传递的。%eax对于 syscall_number。%ebx, %ecx, %edx, %esi, %edi, %ebp 用于将 6 个参数传递给系统调用。

The return value is in %eax. All other registers (including EFLAGS) are preserved across the int $0x80.

返回值在%eax. 所有其他寄存器(包括 EFLAGS)在int $0x80.

I took following snippet from the Linux Assembly Tutorialbut I'm doubtful about this. If any one can show an example, it would be great.

我从Linux 汇编教程中获取了以下片段,但我对此表示怀疑。如果有人能举个例子,那就太好了。

If there are more than six arguments, %ebxmust 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.

如果有六个以上的参数,则 %ebx必须包含存储参数列表的内存位置 - 但不要担心这一点,因为您不太可能使用具有六个以上参数的系统调用。

For an example and a little more reading, refer to http://www.int80h.org/bsdasm/#alternate-calling-convention. Another example of a Hello World for i386 Linux using int 0x80: Hello, world in assembly language with Linux system calls?

有关示例和更多阅读内容,请参阅http://www.int80h.org/bsdasm/#alternate-calling-convention。另一个使用 i386 Linux 的 Hello World 示例int 0x80Hello, world in assembly language with Linux system calls?

There is a faster way to make 32-bit system calls: using sysenter. The kernel maps a page of memory into every process (the vDSO), with the user-space side of the sysenterdance, which has to cooperate with the kernel for it to be able to find the return address. Arg to register mapping is the same as for int $0x80. You should normally call into the vDSO instead of using sysenterdirectly. (See The Definitive Guide to Linux System Callsfor info on linking and calling into the vDSO, and for more info on sysenter, and everything else to do with system calls.)

有一种更快的方法来进行 32 位系统调用:使用sysenter. 内核将一页内存映射到每个进程(vDSO)中,用户空间端sysenter必须与内核合作才能找到返回地址。注册映射的 Arg 与 for 相同int $0x80。您通常应该调用 vDSO 而不是sysenter直接使用。(有关链接和调用 vDSO 的信息,以及有关系统调用的更多信息,请参阅Linux 系统调用权威指南sysenter。)

x86-32 [Free|Open|Net|DragonFly]BSD UNIX System Call convention:

x86-32 [Free|Open|Net|DragonFly]BSD UNIX 系统调用约定:

Parameters are passed on the stack. Push the parameters (last parameter pushed first) on to the stack. Then push an additional 32-bit of dummy data (Its not actually dummy data. refer to following link for more info) and then give a system call instruction int $0x80

参数在堆栈上传递。将参数(最先推送的最后一个参数)压入堆栈。然后再推送一个额外的 32 位虚拟数据(它实际上不是虚拟数据。更多信息请参考以下链接),然后给出系统调用指令int $0x80

http://www.int80h.org/bsdasm/#default-calling-convention

http://www.int80h.org/bsdasm/#default-calling-convention



x86-64 Linux System Call convention:

x86-64 Linux 系统调用约定:

x86-64 Mac OS X is similar but different. TODO: check what *BSD does.

x86-64 Mac OS X 类似但不同。待办事项:检查 *BSD 做了什么。

Refer to section: "A.2 AMD64 LinuxKernel Conventions" of System V Application Binary Interface AMD64 Architecture Processor Supplement. The latest versions of the i386 and x86-64 System V psABIs can be found linked from this page in the ABI maintainer's repo. (See also the x86tag wiki for up-to-date ABI links and lots of other good stuff about x86 asm.)

请参阅System V Application Binary Interface AMD64 Architecture Processor Supplement 的“A.2 AMD64 LinuxKernel Conventions”部分。最新版本的 i386 和 x86-64 System V psABI 可以从 ABI 维护者的 repo 中的这个页面找到链接。(另请参阅x86标签 wiki 以获取最新的 ABI 链接和许多其他关于 x86 asm 的好东西。)

Here is the snippet from this section:

这是本节的片段:

  1. 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.
  2. A system-call is done via the syscallinstruction. This clobbers %rcx and %r11as well as the %rax return value, but other registers are preserved.
  3. The number of the syscall has to be passed in register %rax.
  4. System-calls are limited to six arguments, no argument is passed directly on the stack.
  5. 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.
  6. Only values of class INTEGER or class MEMORY are passed to the kernel.
  1. 用户级应用程序使用整数寄存器来传递序列 %rdi、%rsi、%rdx、%rcx、%r8 和 %r9。内核接口使用 %rdi、%rsi、%rdx、%r10、%r8 和 %r9。
  2. 系统调用是通过syscall指令完成的。这会破坏 %rcx 和 %r11以及 %rax 返回值,但保留其他寄存器。
  3. 系统调用的编号必须在寄存器 %rax 中传递。
  4. 系统调用仅限于六个参数,没有参数直接在堆栈上传递。
  5. 从系统调用返回,寄存器 %rax 包含系统调用的结果。-4095 和 -1 之间范围内的值表示错误,它是-errno.
  6. 只有类 INTEGER 或类 MEMORY 的值被传递给内核。

Remember this is from the Linux-specific appendix to the ABI, and even for Linux it's informative not normative. (But it is in fact accurate.)

请记住,这是来自 ABI 的特定于 Linux 的附录,即使对于 Linux,它也是信息性的而非规范性的。(但实际上它是准确的。)

This 32-bit int $0x80ABI isusable in 64-bit code (but highly not recommended). What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?It still truncates its inputs to 32-bit, so it's unsuitable for pointers, and it zeros r8-r11.

这个 32 位int $0x80ABI用于 64 位代码(但强烈不推荐)。 如果在 64 位代码中使用 32 位 int 0x80 Linux ABI,会发生什么?它仍然将其输入截断为 32 位,因此它不适合指针,并将 r8-r11 置零。

User Interface: function calling

用户界面:函数调用

x86-32 Function Calling convention:

x86-32 函数调用约定:

In x86-32 parameters were passed on stack. Last parameter was pushed first on to the stack until all parameters are done and then callinstruction was executed. This is used for calling C library (libc) functions on Linux from assembly.

在 x86-32 中,参数在堆栈上传递。最后一个参数首先被压入堆栈,直到所有参数都完成,然后call执行指令。这用于从汇编调用 Linux 上的 C 库 (libc) 函数。

Modern versions of the i386 System V ABI (used on Linux) require 16-byte alignment of %espbefore a call, like the x86-64 System V ABI has always required. Callees are allowed to assume that and use SSE 16-byte loads/stores that fault on unaligned. But historically, Linux only required 4-byte stack alignment, so it took extra work to reserve naturally-aligned space even for an 8-byte doubleor something.

i386 System V ABI 的现代版本(在 Linux 上使用)需要 16 字节的%espa 之前对齐call,就像 x86-64 System V ABI 一直需要的那样。允许被调用者假设并使用在未对齐时出错的 SSE 16 字节加载/存储。但是从历史上看,Linux 只需要 4 字节的堆栈对齐,因此即使为 8 字节double或其他东西保留自然对齐的空间也需要额外的工作。

Some other modern 32-bit systems still don't require more than 4 byte stack alignment.

其他一些现代 32 位系统仍然不需要超过 4 字节的堆栈对齐。



x86-64 System V user-space Function Calling convention:

x86-64 System V 用户空间函数调用约定:

x86-64 System V passes args in registers, which is more efficient than i386 System V's stack args convention. It avoids the latency and extra instructions of storing args to memory (cache) and then loading them back again in the callee. This works well because there are more registers available, and is better for modern high-performance CPUs where latency and out-of-order execution matter. (The i386 ABI is very old).

x86-64 System V 在寄存器中传递 args,这比 i386 System V 的堆栈 args 约定更有效。它避免了将 args 存储到内存(缓存)然后在被调用者中再次加载它们的延迟和额外指令。这很有效,因为有更多可用的寄存器,并且更适合延迟和乱序执行很重要的现代高性能 CPU。(i386 ABI 很旧了)。

In this newmechanism: First the parameters are divided into classes. The class of each parameter determines the manner in which it is passed to the called function.

在这种机制中:首先将参数分为几类。每个参数的类决定了它传递给被调用函数的方式。

For complete information refer to : "3.2 Function Calling Sequence" of System V Application Binary Interface AMD64 Architecture Processor Supplementwhich reads, in part:

有关完整信息,请参阅:System V Application Binary Interface AMD64 Architecture Processor Supplement 的“3.2 Function Calling Sequence” ,部分内容如下:

Once arguments are classified, the registers get assigned (in left-to-right order) for passing as follows:

  1. If the class is MEMORY, pass the argument on the stack.
  2. If the class is INTEGER, the next available register of the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9 is used

一旦参数被分类,寄存器将被分配(按从左到右的顺序)传递,如下所示:

  1. 如果类是 MEMORY,则在堆栈上传递参数。
  2. 如果类是整数,则使用序列 %rdi、%rsi、%rdx、%rcx、%r8 和 %r9 的下一个可用寄存器

So %rdi, %rsi, %rdx, %rcx, %r8 and %r9are the registers in orderused to pass integer/pointer (i.e. INTEGER class) parameters to any libc function from assembly. %rdi is used for the first INTEGER parameter. %rsi for 2nd, %rdx for 3rd and so on. Then callinstruction should be given. The stack (%rsp) must be 16B-aligned when callexecutes.

用于将整数/指针(即 INTEGER 类)参数从汇编传递给任何 libc 函数%rdi, %rsi, %rdx, %rcx, %r8 and %r9的寄存器也是如此。%rdi 用于第一个 INTEGER 参数。%rsi 表示第二个,%rdx 表示第三个,依此类推。然后call应该给出指示。堆栈 ( %rsp) 在call执行时必须是 16B 对齐的。

If there are more than 6 INTEGER parameters, the 7th INTEGER parameter and later are passed on the stack. (Caller pops, same as x86-32.)

如果有超过 6 个 INTEGER 参数,则第 7 个 INTEGER 参数及之后的参数将在堆栈上传递。(来电弹出,与 x86-32 相同。)

The first 8 floating point args are passed in %xmm0-7, later on the stack. There are no call-preserved vector registers. (A function with a mix of FP and integer arguments can have more than 8 total register arguments.)

前 8 个浮点参数在 %xmm0-7 中传递,稍后在堆栈中。没有调用保留的向量寄存器。(混合了 FP 和整数参数的函数可以有 8 个以上的寄存器参数。)

Variadic functions (like printf) always need %al= the number of FP register args.

可变参数函数(printf)总是需要%al= FP 寄存器参数的数量。

There are rules for when to pack structs into registers (rdx:raxon return) vs. in memory. See the ABI for details, and check compiler output to make sure your code agrees with compilers about how something should be passed/returned.

何时将结构打包到寄存器(rdx:rax返回时)与内存中是有规则的。有关详细信息,请参阅 ABI,并检查编译器输出以确保您的代码与编译器就应如何传递/返回某些内容达成一致。



Note that the Windows x64 function calling conventionhas multiple significant differences from x86-64 System V, like shadow space that mustbe reserved by the caller (instead of a red-zone), and call-preserved xmm6-xmm15. And very different rules for which arg goes in which register.

请注意,Windows x64 函数调用约定与 x86-64 System V 有多个显着差异,例如调用者必须保留的阴影空间(而不是红色区域)和调用保留的 xmm6-xmm15。对于哪个 arg 进入哪个寄存器的非常不同的规则。

回答by Jonathan Leffler

Perhaps you're looking for the x86_64 ABI?

也许您正在寻找 x86_64 ABI?

If that's not precisely what you're after, use 'x86_64 abi' in your preferred search engine to find alternative references.

如果这不是您想要的,请在您首选的搜索引擎中使用“x86_64 abi”来查找替代参考。

回答by Peter Teoh

Calling conventions defines how parameters are passed in the registers when calling or being called by other program. And the best source of these convention is in the form of ABI standards defined for each these hardware. For ease of compilation, the same ABI is also used by userspace and kernel program. Linux/Freebsd follow the same ABI for x86-64 and another set for 32-bit. But x86-64 ABI for Windows is different from Linux/FreeBSD. And generally ABI does not differentiate system call vs normal "functions calls". Ie, here is a particular example of x86_64 calling conventions and it is the same for both Linux userspace and kernel: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/(note the sequence a,b,c,d,e,f of parameters):

调用约定定义了在调用或被其他程序调用时如何在寄存器中传递参数。这些约定的最佳来源是为每个这些硬件定义的 ABI 标准。为了便于编译,用户空间和内核程序也使用相同的 ABI。Linux/Freebsd 对 x86-64 遵循相同的 ABI,对 32 位遵循相同的 ABI。但是用于 Windows 的 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):

A good rendering of calling conventions vs registers usage

调用约定与寄存器使用的良好呈现

Performance is one of the reasons for these ABI (eg, passing parameters via registers instead of saving into memory stacks)

性能是这些 ABI 的原因之一(例如,通过寄存器传递参数而不是保存到内存堆栈中)

For ARM there is various ABI:

对于 ARM,有各种 ABI:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.swdev.abi/index.html

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.swdev.abi/index.html

https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/iPhoneOSABIReference.pdf

https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/iPhoneOSABIReference.pdf

ARM64 convention:

ARM64 约定:

http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf

http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf

For Linux on PowerPC:

对于 PowerPC 上的 Linux:

http://refspecs.freestandards.org/elf/elfspec_ppc.pdf

http://refspecs.freestandards.org/elf/elfspec_ppc.pdf

http://www.0x04.net/doc/elf/psABI-ppc64.pdf

http://www.0x04.net/doc/elf/psABI-ppc64.pdf

And for embedded there is the PPC EABI:

对于嵌入式,有 PPC EABI:

http://www.freescale.com/files/32bit/doc/app_note/PPCEABI.pdf

http://www.freescale.com/files/32bit/doc/app_note/PPCEABI.pdf

This document is good overview of all the different conventions:

本文档很好地概述了所有不同的约定:

http://www.agner.org/optimize/calling_conventions.pdf

http://www.agner.org/optimize/calling_conventions.pdf