记住 x86-64 System V arg 寄存器顺序的最佳方法是什么?

问题描述 投票:0回答:2

我经常忘记系统调用中每个参数需要使用的寄存器,每次我忘记时我都会访问this问题。

x86_64 用户空间函数调用的整数/指针参数的正确顺序是:

%rdi
%rsi
%rdx
%rcx
%r8
%r9
。 (可变参数函数取 AL = FP 参数的数量,最多 8)

或者对于系统调用,

%rax
(系统调用调用号),以及相同的参数,除了
%r10
而不是
%rcx

记住这些寄存器的最佳方法是什么,而不是每次都用谷歌搜索这个问题?

assembly x86-64 cpu-registers calling-convention abi
2个回答
10
投票

如果您还记得 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博客推荐)


0
投票

我用于

rdi rsi rdx rcx r8 r9
的助记符是“Dizzy Dixie 89。”

  • “Dizzy”听起来像“di si”,对应
    rdi rsi
  • “Dixie”听起来像“dix ci”,对应
    rdx rcx
  • “89”对应
    r8 r9

额外助记符:对于 x86-64 系统调用调用约定 (

rdi rsi rdx r10 r8 r9
),我使用“Dizzy Dicks 1089。”

  • 和之前一样,“Dizzy”听起来像“di si”,对应
    rdi rsi
  • “Dicks”听起来像“dix”,对应于
    rdx
  • “1089”对应
    r10 r8 r9
© www.soinside.com 2019 - 2024. All rights reserved.