Linux 64 abi,调用约定

Linux 64 abi,调用约定,linux,assembly,x86-64,calling-convention,Linux,Assembly,X86 64,Calling Convention,我正在阅读有关调用约定以及哪个寄存器具有哪个目的的文章。以下是图3.4:寄存器用法中指定的内容: %rax temporary register; with variable arguments passes information about the number of vector registers used; 1st return register 但在中,我们使用rax传递函数号。它与英特尔手册中的规定一致吗?实际上,我希望(根

我正在阅读有关调用约定以及哪个寄存器具有哪个目的的文章。以下是
图3.4:寄存器用法中指定的内容:

%rax       temporary register; with variable arguments
           passes information about the number of vector
           registers used; 1st return register
但在中,我们使用
rax
传递函数号。它与英特尔手册中的规定一致吗?实际上,我希望(根据手册)我们将函数号传递到
rdi
(它用于第一个参数)。等等

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

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

这段话是关于函数调用约定的,它是由x86-64 System V ABI doc标准化的

您正在考虑Linux的系统调用约定,该约定在ABI文档的附录中有描述,但该部分并不规范。无论如何,系统调用ABI将调用号码放入
rax
,因为它不是系统调用的参数。或者,您可以将其视为第0个参数,与变量函数调用在
al
中传递FP寄存器参数的数量的方式相同。(有趣的事实:如果调用方愿意,甚至可以传递堆栈上的第一个FP arg。)

但更重要的是,因为RAX中的调用号产生了更好的ABI,而且因为传统:i386系统调用ABI也是这样做的。而i386系统V函数调用ABI完全不同,只使用堆栈参数

这意味着系统调用包装函数只需设置
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
rcx
r11
与保存的RIP和RFLAGS一起使用,因此它不必写入任何包含返回信息的内存,也不必强制用户空间将其放在内核可以读取的位置(如32位
sysenter
那样)

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

对于具有4个或更多参数的系统调用(或适用于任何系统调用的通用包装器),您确实需要
movr10、rcx
,但仅此而已。(与32位约定不同,在32位约定中,包装器必须从堆栈中加载arg,并保存/还原
ebx
,因为内核选择不当的ABI将其用于第一个arg。)


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

是的,对不需要从C调用的私有助手函数执行任何操作

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


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

系统调用使用的约定与用户空间稍有不同。是的,你可以在手写函数中做任何你想做的事情。@Jester做任何我想做的事情听起来有点混乱。我想了解并遵守调用约定。请注意,32位和64位linux的系统调用接口非常不同。对于32位,%ebx,%ecx,%edx;对于64位%rdi、%rsi、%rdx。但是系统调用号码总是由%eax(或%rax)传递,“做我想做的事情听起来有点乱。”您特别询问是否可以。。。我刚才回答说:@St.Antario真的!里面说的一切都是正确的。请注意,§3.3为系统调用指定了单独的调用约定,其中包括使用您已经观察到的
rax
。之前没有注意到带有内核约定的附录。