我正在阅读关于调用约定的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
答案 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 rcx
和r11
已保存RIP和RFLAGS,因此它没有使用返回信息写入任何内存,并且不强制用户空间将其放在内核可以读取的位置(如32位sysenter
那样)。
因此系统调用约定不能与函数调用约定相同。 (或者函数调用约定必须选择不同的寄存器。)
对于具有4个或更多args的系统调用(或适用于任何系统调用的通用包装器),您需要mov r10, rcx
,但这就是全部。 (与32位约定不同,其中包装器必须从堆栈加载args,并保存/恢复ebx
,因为内核选择不当的ABI将它用于第一个arg。)
我可以使用rax在手写函数中传递第一个函数参数吗?
是的,为您不需要从C调用的私人帮助函数执行任何操作。
选择arg寄存器以使调用者(或最重要的调用者)更容易,或者您将使用任何具有固定寄存器选择的寄存器(如div
)。
注意哪些寄存器被破坏,哪些寄存器被注释保留。只需要保存/恢复调用者实际需要保存/恢复的寄存器,并选择使用哪个tmp reg来最小化推/弹。如果您的函数很短,请避免在调用者中推送/弹出保存/重新加载作为关键延迟路径一部分的寄存器。