Linux 64-abi,调用约定

时间:2018-02-05 13:56:25

标签: linux assembly x86-64 calling-convention

我正在阅读关于调用约定的intel manual以及哪个寄存器具有哪个目的。以下是Figure 3.4: Register Usage

中指定的内容
%rax       temporary register; with variable arguments
           passes information about the number of vector
           registers used; 1st return register

但在linux api中,我们使用rax来传递函数编号。它是否与intel手册中指定的一致?实际上我期望(根据手册)我们将函数号传递给rdi(它用于第一个参数)。等等......

我可以使用rax在手写函数中传递第一个函数参数吗? E.g。

mov rax, [array_lenght_ptr]
mov rdi, array_start_ptr
callq _array_sum

1 个答案:

答案 0 :(得分:2)

该引言讨论的是 function -calling约定,该约定由x86-64 System V ABI doc标准化。

您正在考虑Linux的系统调用约定,这在ABI文档的附录中有所描述,但该部分不是规范性的。无论如何,系统调用ABI将电话号码放在rax中,因为它不是系统调用的arg 。或者,你可以把它想象成第0个arg,就像可变函数调用传递al中FP寄存器args的数量一样。 (有趣的事实是:如果他们愿意,调用者甚至可以通过堆栈中的第一个FP arg。)

但更重要的是,因为RAX中的电话号码使ABI更好,而且由于传统:它也是i386系统调用ABI所做的。和i386 System V 功能 -call ABI完全不同,只使用堆栈args。

这意味着系统调用包装器函数可以设置eax并运行syscall而不需要执行类似

的操作
libc_write_wrapper_for_your_imagined_syscall_convention:
   ; copy all args to the next slot over
    mov r10, rdx   ; size_t count
    mov rdx, rsi   ; void *buf
    mov esi, edi   ; int fd
    mov edi, 1     ; SYS_write
    syscall
    cmp  rax, -4095
    jae  set_errno
    ret

而不是

actual_libc_write_wrapper:   ; glibc's actual code I think also checks for pthread cancellation points or something...
    mov eax, 1     ; SYS_write
    syscall
    cmp  rax, -4095
    jae  set_errno
    ret

请注意使用r10代替rcx,因为syscall clobbers rcxr11已保存RIP和RFLAGS,因此它没有使用返回信息写入任何内存,并且不强制用户空间将其放在内核可以读取的位置(如32位sysenter那样)。

因此系统调用约定不能与函数调用约定相同。 (或者函数调用约定必须选择不同的寄存器。)

对于具有4个或更多args的系统调用(或适用于任何系统调用的通用包装器),您需要mov r10, rcx,但这就是全部。 (与32位约定不同,其中包装器必须从堆栈加载args,并保存/恢复ebx,因为内核选择不当的ABI将它用于第一个arg。)

  

我可以使用rax在手写函数中传递第一个函数参数吗?

是的,为您不需要从C调用的私人帮助函数执行任何操作。

选择arg寄存器以使调用者(或最重要的调用者)更容易,或者您将使用任何具有固定寄存器选择的寄存器(如div)。

注意哪些寄存器被破坏,哪些寄存器被注释保留。只需要保存/恢复调用者实际需要保存/恢复的寄存器,并选择使用哪个tmp reg来最小化推/弹。如果您的函数很短,请避免在调用者中推送/弹出保存/重新加载作为关键延迟路径一部分的寄存器。