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

UNIX&;的调用约定是什么;i386和x86-64上的Linux系统调用(和用户空间函数),linux,assembly,x86-64,calling-convention,abi,x86,Linux,Assembly,X86 64,Calling Convention,Abi,X86,以下链接解释了UNIX(BSD风格)和Linux的x86-32系统调用约定: 但是UNIX和Linux上的x86-64系统调用约定是什么呢?也许您正在寻找x86\u 64 ABI (2018年11月24日第404页) (2018年11月24日通过回程机) -保持最新(由ABI维护人员之一HJ Lu提供),并提供官方最新版本PDF的链接 如果您的目标并非如此,请在首选搜索引擎中使用“x86_64 abi”查找其他参考资料。有关此处任何主题的进一步阅读: 我在Linux上使用GNU汇编

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


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

也许您正在寻找x86\u 64 ABI

  • (2018年11月24日第404页)
  • (2018年11月24日通过回程机)
  • -保持最新(由ABI维护人员之一HJ Lu提供),并提供官方最新版本PDF的链接

如果您的目标并非如此,请在首选搜索引擎中使用“x86_64 abi”查找其他参考资料。

有关此处任何主题的进一步阅读:


我在Linux上使用GNU汇编程序(gas)验证了这些

内核接口 x86-32又名i386 Linux系统调用约定:

在x86-32中,Linux系统调用的参数使用寄存器传递<代码>%eax用于系统调用号。%ebx、%ecx、%edx、%esi、%edi、%ebp用于向系统调用传递6个参数

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

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

如果有六个以上的论点,
%ebx
必须包含内存 参数列表所在的位置 已存储—但不要担心这一点 因为你不太可能用 超过六个的系统调用 争论

有关示例和更多阅读,请参阅。使用
int 0x80
的i386 Linux Hello World的另一个示例:

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

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

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


x86-64 Linux系统调用约定: (注意:来自Linux。TODO:检查*BSD的功能)

请参阅的“A.2 AMD64Linux内核约定”一节。可以找到i386和x86-64 System V psABIs的最新版本。(有关最新的ABI链接和许多其他关于x86 asm的好东西,请参见tag wiki。)

以下是本节的片段:

  • 用户级应用程序用作整数寄存器,用于传递 序列%rdi、%rsi、%rdx、%rcx、, %r8和%r9内核接口使用%rdi、%rsi、%rdx、%r10、%r8和%r9。
  • 系统调用通过
    syscall
    指令完成。这与%rax返回值一样,但保留了其他寄存器
  • 必须在寄存器%rax中传递系统调用的编号
  • 系统调用限制为六个参数,不传递任何参数 直接在堆栈上
  • 从系统调用返回的寄存器%rax包含 系统调用。介于-4095和-1之间的值表示 一个错误,它是
    -errno
  • 只有类整数或类内存的值被传递到内核
  • 请记住,这是ABI的Linux特定附录,即使对于Linux,它也是信息性的,而不是规范性的。(但事实上这是准确的。)

    此32位
    int$0x80
    ABI可用于64位代码(但强烈不推荐)。它仍然将其输入截断为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非常古老)

    在这种新机制中:首先将参数划分为类。每个参数的类确定将其传递给被调用函数的方式

    有关完整信息,请参阅“3.2函数调用序列”,其中部分内容如下:

    一旦参数是c
    /*
     * 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.  */
    
    /* 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;                       \
    })
    
    .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