我经常忘记系统调用中每个参数需要使用的寄存器,每次我忘记时我都会访问this问题。
x86_64 用户空间函数调用的整数/指针参数的正确顺序是:
%rdi
、%rsi
、%rdx
、%rcx
、%r8
和 %r9
。 (可变参数函数取 AL = FP 参数的数量,最多 8)
或者对于系统调用,
%rax
(系统调用调用号),以及相同的参数,除了 %r10
而不是 %rcx
。
记住这些寄存器的最佳方法是什么,而不是每次都用谷歌搜索这个问题?
如果您还记得 C
memcpy
的参数顺序,以及 rep
movsb
的工作原理,这就是记住 x86-64 System V 的大部分方法。
该设计使得
memcpy(dst, src, size)
使用 rep movsb
实现起来很便宜,除了在更多函数中未使用 RCX 之外,因为变量计数移位需要它的频率比任何需要 RDX 的频率都高。
那么R8和R9是前两个“高”寄存器。使用它们需要一个 REX 前缀,这会在指令中花费一个额外字节的代码大小,否则不需要。因此,对于最后 2 个参数来说,它们是一个明智的选择。 (Windows x64 同样选择使用 R8、R9 作为最后 2 个寄存器参数)。
实际的设计过程涉及最小化指令数和代码大小的成本权衡,以便使用当时的 GCC AMD64 端口编译某些东西(可能是 SPECcpu)。我不知道 inlined memcpy as
rep movsb
是否相关,或者当时的 glibc 是否真的以这种方式实现,或者什么。
我对 为什么 Windows64 使用与 x86-64 上所有其他操作系统不同的调用约定? 的回答引用了一些调用约定设计决策的来源。 (来自 GCC 开发人员的早期 x86-64.org 邮件列表帖子,特别是 Jan Hubicka,他在提出这个命令之前尝试了一些寄存器命令。)
特别要记住订单中的 RDX、RCX 部分是以下引用:
我们试图在序列的早期避免 RCX,因为它是寄存器 通常用于特殊用途,如 EAX,因此具有相同的用途 序列中缺失。它也不能用于系统调用和 我们想让系统调用序列与函数调用序列相匹配 尽可能多。
R10在系统调用约定中取代了RCX,因为
syscall
指令本身会破坏RCX(用它来保存RIP,避免使用用户空间堆栈,并且它不能使用内核堆栈,因为它使堆栈切换向上)到软件)。就像它如何使用 R11 来保存 RFLAGS 一样。
保持它尽可能相似允许 libc 包装器只是
mov %rcx, %r10
,而不是通过多个参数来填补空白。 R10 是 R8 和 R9 之后的下一个可用寄存器。
Di
ane 的 si
lk d
ress c
osts $89
(由CS:APP博客推荐)
我用于
rdi rsi rdx rcx r8 r9
的助记符是“Dizzy Dixie 89。”
rdi rsi
rdx rcx
r8 r9
额外助记符:对于 x86-64 系统调用调用约定 (
rdi rsi rdx r10 r8 r9
),我使用“Dizzy Dicks 1089。”
rdi rsi
rdx
r10 r8 r9