Linux 64-abi,调用约定
Linux 64-abi, 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
来传递手写函数中的第一个函数参数吗?例如
mov rax, [array_lenght_ptr]
mov rdi, array_start_ptr
callq _array_sum
那句话是在谈论 函数 调用约定,它由 x86-64 System V ABI 文档标准化。
您正在考虑 Linux 的系统调用调用约定,它在 ABI 文档的附录中进行了描述,但那部分不是规范的。无论如何,系统调用 ABI 将调用号放在 rax
中,因为它不是系统调用 的参数 。或者,您可以将其视为第 0 个 arg,就像可变参数函数调用在 al
中传递 FP 寄存器参数的数量一样。 (有趣的事实:这使得调用者可以根据需要传递堆栈上的第一个 FP arg。)
但更重要的是,因为 RAX 中的调用编号会产生更好的 ABI,并且由于传统:这也是 i386 系统调用 ABI 所做的。 和 i386 系统V function-call 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,所以它不会'不必用 return 信息写入任何内存,也不会强制用户 space 将其放在内核可以读取的地方(就像 32 位 sysenter
那样)。
所以系统调用约定不能与函数调用约定相同。 (或者函数调用约定将不得不选择不同的寄存器。)
对于具有 4 个或更多参数的系统调用(或适用于任何系统调用的通用包装器),您确实需要 mov r10, rcx
,仅此而已。 (与包装器必须从堆栈加载参数的 32 位约定不同,并且 save/restore ebx
因为内核选择不当的 ABI 将其用于第一个参数。)
Can I use rax to pass the first function argument in my hand-written functions?
是的,对不需要从 C 调用的私有辅助函数做任何您想做的事。
选择 arg 寄存器以使呼叫者(或最重要的呼叫者)更容易,或者您将使用具有固定寄存器选择的任何寄存器(如 div
)。
注意哪些寄存器被破坏,哪些通过注释保留。只需费心 save/restore 注册您的调用者实际需要保存/恢复,并选择您使用哪个 tmp regs 来最小化 push/pop。如果您的函数很短,请避免 push/pop save/reload 寄存器,它们是调用者中关键延迟路径的一部分。
我正在阅读 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
来传递手写函数中的第一个函数参数吗?例如
mov rax, [array_lenght_ptr]
mov rdi, array_start_ptr
callq _array_sum
那句话是在谈论 函数 调用约定,它由 x86-64 System V ABI 文档标准化。
您正在考虑 Linux 的系统调用调用约定,它在 ABI 文档的附录中进行了描述,但那部分不是规范的。无论如何,系统调用 ABI 将调用号放在 rax
中,因为它不是系统调用 的参数 。或者,您可以将其视为第 0 个 arg,就像可变参数函数调用在 al
中传递 FP 寄存器参数的数量一样。 (有趣的事实:这使得调用者可以根据需要传递堆栈上的第一个 FP arg。)
但更重要的是,因为 RAX 中的调用编号会产生更好的 ABI,并且由于传统:这也是 i386 系统调用 ABI 所做的。 和 i386 系统V function-call 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,所以它不会'不必用 return 信息写入任何内存,也不会强制用户 space 将其放在内核可以读取的地方(就像 32 位 sysenter
那样)。
所以系统调用约定不能与函数调用约定相同。 (或者函数调用约定将不得不选择不同的寄存器。)
对于具有 4 个或更多参数的系统调用(或适用于任何系统调用的通用包装器),您确实需要 mov r10, rcx
,仅此而已。 (与包装器必须从堆栈加载参数的 32 位约定不同,并且 save/restore ebx
因为内核选择不当的 ABI 将其用于第一个参数。)
Can I use rax to pass the first function argument in my hand-written functions?
是的,对不需要从 C 调用的私有辅助函数做任何您想做的事。
选择 arg 寄存器以使呼叫者(或最重要的呼叫者)更容易,或者您将使用具有固定寄存器选择的任何寄存器(如 div
)。
注意哪些寄存器被破坏,哪些通过注释保留。只需费心 save/restore 注册您的调用者实际需要保存/恢复,并选择您使用哪个 tmp regs 来最小化 push/pop。如果您的函数很短,请避免 push/pop save/reload 寄存器,它们是调用者中关键延迟路径的一部分。