UNIX 和 Linux 的调用约定是什么? i386 和 x86-64 上的 Linux 系统调用(和用户空间函数)

发布于 2024-08-26 22:01:19 字数 473 浏览 15 评论 0 原文

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

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

吾性傲以野 2024-09-02 22:01:19

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


我在 Linux 上使用 GNU 汇编器 (gas) 验证了这些内容。

内核接口

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

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

返回值位于%eax中。所有其他寄存器(包括 EFLAGS)都在 int $0x80 中保留。

我从 Linux 汇编教程,但我对此表示怀疑。如果有人能举一个例子,那就太好了。

如果有超过六个参数,
%ebx 必须包含内存
参数列表所在的位置
已存储 - 但不用担心这个
因为你不太可能会使用
超过六个的系统调用
论据。

有关示例和更多阅读内容,请参阅 http://www.int80h.org/bsdasm/#alternate-calling-convention。使用 int 0x80 的 i386 Linux 的 Hello World 的另一个示例:Hello,使用 Linux 系统调用的汇编语言世界?

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

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

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

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


x86-64 Linux 系统调用约定:(

注:x86-64 Mac OS X 与 Linux 相似但不同 TODO:检查 *BSD 的作用)

请参阅 Linux 内核约定”部分。 ="http://refspecs.linuxfoundation.org/elf/x86_64-abi-0.99.pdf" rel="noreferrer">System V 应用程序二进制接口 AMD64 架构处理器补充。最新版本的 i386 和 x86-64 System V psABI 可以在 链接中找到从 ABI 维护者存储库中的此页面。 (另请参阅 标签 wiki,获取最新的 ABI 链接和许多其他有关 x86 asm 的好东西。)

以下是本节的片段:

  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 类的值传递给内核。

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

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

用户界面:函数调用

x86-32 函数调用约定:

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

现代版本的 i386 System V ABI(在 Linux 上使用)要求在调用之前对 %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 来说更好。 (i386 ABI 已经很老了)。

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

有关完整信息,请参阅:System V 应用程序二进制接口 AMD64 的“3.2 函数调用序列”架构处理器补充,部分内容如下:

一旦参数被分类,寄存器就会被分配(在
从左到右的顺序)传递如下:

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

因此%rdi,%rsi,%rdx,%rcx,%r8和%r9是寄存器in order 用于将整数/指针(即 INTEGER 类)参数从程序集传递到任何 libc 函数。 %rdi 用于第一个 INTEGER 参数。 %rsi 代表第二个,%rdx 代表第三个,依此类推。然后应该给出call指令。当 call 执行时,堆栈 (%rsp) 必须是 16B 对齐。

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

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

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

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


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

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


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

Kernel Interface

x86-32 aka i386 Linux System Call convention:

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

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

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

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.

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?

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 sysenter dance, 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 sysenter directly. (See The Definitive Guide to Linux System Calls for info on linking and calling into the vDSO, and for more info on sysenter, and everything else to do with system calls.)

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

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

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


x86-64 Linux System Call convention:

(Note: x86-64 Mac OS X is similar but different from Linux. TODO: check what *BSD does)

Refer to section: "A.2 AMD64 Linux Kernel 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 tag wiki for up-to-date ABI links and lots of other good stuff about 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 syscall instruction. This clobbers %rcx and %r11 as 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.

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.)

This 32-bit int $0x80 ABI is usable 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.

User Interface: function calling

x86-32 Function Calling convention:

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

Modern versions of the i386 System V ABI (used on Linux) require 16-byte alignment of %esp before 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 double or something.

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


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

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).

In this new mechanism: 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 Supplement which reads, in part:

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

So %rdi, %rsi, %rdx, %rcx, %r8 and %r9 are the registers in order used 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 call instruction should be given. The stack (%rsp) must be 16B-aligned when call executes.

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.)

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.)

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

There are rules for when to pack structs into registers (rdx:rax on 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.


Note that the Windows x64 function calling convention has multiple significant differences from x86-64 System V, like shadow space that must be 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.

画尸师 2024-09-02 22:01:19

Linux 内核 5.0 源代码注释

我知道 x86 细节位于 arch/x86 下,而系统调用内容位于 arch/x86/entry 下。因此,在该目录中快速执行 git grep rdi 会将我引导至 arch/x86/entry/entry_64.S

/*
 * 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 位,位于 arch/x86/entry/entry_32.S

/*
 * 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
 */

glibc 2.29 Linux x86_64系统调用实现

现在让我们通过查看主要的 libc 实现来作弊,看看它们在做什么。

当我写这个答案时,还有什么比研究我现在正在使用的 glibc 更好的呢? :-)

glibc 2.29 在 sysdeps/unix/sysv/linux/x86_64/sysdep.h 并且包含一些有趣的代码,例如:

/* 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.  */

和:

/* Registers clobbered by syscall.  */
# define REGISTERS_CLOBBERED_BY_SYSCALL "cc", "r11", "cx"

#undef internal_syscall6
#define internal_syscall6(number, err, arg1, arg2, arg3, arg4, arg5, arg6) \
({                                  \
    unsigned long int resultvar;                    \
    TYPEFY (arg6, __arg6) = ARGIFY (arg6);              \
    TYPEFY (arg5, __arg5) = ARGIFY (arg5);              \
    TYPEFY (arg4, __arg4) = ARGIFY (arg4);              \
    TYPEFY (arg3, __arg3) = ARGIFY (arg3);              \
    TYPEFY (arg2, __arg2) = ARGIFY (arg2);              \
    TYPEFY (arg1, __arg1) = ARGIFY (arg1);              \
    register TYPEFY (arg6, _a6) asm ("r9") = __arg6;            \
    register TYPEFY (arg5, _a5) asm ("r8") = __arg5;            \
    register TYPEFY (arg4, _a4) asm ("r10") = __arg4;           \
    register TYPEFY (arg3, _a3) asm ("rdx") = __arg3;           \
    register TYPEFY (arg2, _a2) asm ("rsi") = __arg2;           \
    register TYPEFY (arg1, _a1) asm ("rdi") = __arg1;           \
    asm volatile (                          \
    "syscall\n\t"                           \
    : "=a" (resultvar)                          \
    : "0" (number), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4),     \
      "r" (_a5), "r" (_a6)                      \
    : "memory", REGISTERS_CLOBBERED_BY_SYSCALL);            \
    (long int) resultvar;                       \
})

我觉得这是不言自明的。请注意,这似乎是如何设计为与常规 System V AMD64 ABI 函数的调用约定完全匹配的: https ://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions

快速提醒一下:

  • cc 表示标志寄存器。但是Peter Cordes 评论 此处没有必要。
  • memory 意味着可以在程序集中传递指针并用于访问内存有关

从头开始的显式最小可运行示例,请参阅此答案:如何在内联汇编中通过 syscall 或 sysenter 调用系统调用?

Make手动汇编中的一些系统调用

不太科学,但很有趣:

  • x86_64.S

    <前><代码>.文本
    .global_start
    _开始:
    asm_main_after_prologue:
    /* 写 */
    mov $1, %rax /* 系统调用号 */
    mov $1, %rdi /* 标准输出 */
    mov $msg, %rsi /* 缓冲区 */
    mov $len, %rdx /* len */
    系统调用

    /* 出口 */
    mov $60, %rax /* 系统调用号 */
    mov $0, %rdi /* 退出状态 */
    系统调用
    消息:
    .ascii“你好\n”
    长度 = . - 味精

    GitHub 上游

从 C 进行系统调用

以下是带有寄存器约束的示例:如何在内联汇编中通过 syscall 或 sysenter 调用系统调用?

aarch64

我展示了一个最小的可运行程序用户态示例位于: https://reverseengineering.stackexchange.com/questions/16917/arm64- syscalls-table/18834#18834 TODO grep 内核代码在这里,应该很容易。

Linux kernel 5.0 source comments

I knew that x86 specifics are under arch/x86, and that syscall stuff goes under arch/x86/entry. So a quick git grep rdi in that directory leads me to arch/x86/entry/entry_64.S:

/*
 * 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.
 */

and for 32-bit at arch/x86/entry/entry_32.S:

/*
 * 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
 */

glibc 2.29 Linux x86_64 system call implementation

Now let's cheat by looking at a major libc implementations and see what they are doing.

What could be better than looking into glibc that I'm using right now as I write this answer? :-)

glibc 2.29 defines x86_64 syscalls at sysdeps/unix/sysv/linux/x86_64/sysdep.h and that contains some interesting code, e.g.:

/* 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.  */

and:

/* Registers clobbered by syscall.  */
# define REGISTERS_CLOBBERED_BY_SYSCALL "cc", "r11", "cx"

#undef internal_syscall6
#define internal_syscall6(number, err, arg1, arg2, arg3, arg4, arg5, arg6) \
({                                  \
    unsigned long int resultvar;                    \
    TYPEFY (arg6, __arg6) = ARGIFY (arg6);              \
    TYPEFY (arg5, __arg5) = ARGIFY (arg5);              \
    TYPEFY (arg4, __arg4) = ARGIFY (arg4);              \
    TYPEFY (arg3, __arg3) = ARGIFY (arg3);              \
    TYPEFY (arg2, __arg2) = ARGIFY (arg2);              \
    TYPEFY (arg1, __arg1) = ARGIFY (arg1);              \
    register TYPEFY (arg6, _a6) asm ("r9") = __arg6;            \
    register TYPEFY (arg5, _a5) asm ("r8") = __arg5;            \
    register TYPEFY (arg4, _a4) asm ("r10") = __arg4;           \
    register TYPEFY (arg3, _a3) asm ("rdx") = __arg3;           \
    register TYPEFY (arg2, _a2) asm ("rsi") = __arg2;           \
    register TYPEFY (arg1, _a1) asm ("rdi") = __arg1;           \
    asm volatile (                          \
    "syscall\n\t"                           \
    : "=a" (resultvar)                          \
    : "0" (number), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4),     \
      "r" (_a5), "r" (_a6)                      \
    : "memory", REGISTERS_CLOBBERED_BY_SYSCALL);            \
    (long int) resultvar;                       \
})

which I feel are pretty self explanatory. Note how this seems to have been designed to exactly match the calling convention of regular System V AMD64 ABI functions: https://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions

Quick reminder of the clobbers:

  • cc means flag registers. But Peter Cordes comments that this is unnecessary here.
  • memory means that a pointer may be passed in assembly and used to access memory

For an explicit minimal runnable example from scratch see this answer: How to invoke a system call via syscall or sysenter in inline assembly?

Make some syscalls in assembly manually

Not very scientific, but fun:

  • x86_64.S

    .text
    .global _start
    _start:
    asm_main_after_prologue:
        /* write */
        mov $1, %rax    /* syscall number */
        mov $1, %rdi    /* stdout */
        mov $msg, %rsi  /* buffer */
        mov $len, %rdx  /* len */
        syscall
    
        /* exit */
        mov $60, %rax   /* syscall number */
        mov $0, %rdi    /* exit status */
        syscall
    msg:
        .ascii "hello\n"
    len = . - msg
    

    GitHub upstream.

Make system calls from C

Here's an example with register constraints: How to invoke a system call via syscall or sysenter in inline assembly?

aarch64

I've shown a minimal runnable userland example at: https://reverseengineering.stackexchange.com/questions/16917/arm64-syscalls-table/18834#18834 TODO grep kernel code here, should be easy.

怪我鬧 2024-09-02 22:01:19

也许您正在寻找 x86_64 ABI?

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

Perhaps you're looking for the 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.

一抹微笑 2024-09-02 22:01:19

调用约定定义了调用或被其他程序调用时参数如何在寄存器中传递。这些约定的最佳来源是为每个硬件定义的 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 参数):

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

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

sstatic.net/x1Rdn.png ARM 有各种 ABI:

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

ARM64 约定:

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

对于 PowerPC 上的 Linux:

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

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

对于嵌入式,有 PPC EABI:

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

本文档是对所有不同约定的良好概述:

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

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):

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)

For ARM there is various ABI:

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

ARM64 convention:

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

For Linux on PowerPC:

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

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

And for embedded there is the PPC EABI:

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文