为什么Windows64使用与x86-64上所有其他操作系统不同的调用约定?

为什么Windows64使用与x86-64上所有其他操作系统不同的调用约定?,windows,x86-64,calling-convention,Windows,X86 64,Calling Convention,AMD有一个ABI规范,描述在x86-64上使用的调用约定。除了有自己的x86-64调用约定的Windows之外,所有操作系统都遵循它。为什么? 有人知道造成这种差异的技术、历史或政治原因吗,还是这纯粹是一个尼希德综合征的问题 我知道不同的操作系统可能对更高级别的东西有不同的需求,但这并不能解释为什么例如Windows上的寄存器参数传递顺序是rcx-rdx-r8-r9-rest-on-stack,而其他所有操作系统都使用rdi-rsi-rdx-rcx-r8-r9-rest-on-stack 另外

AMD有一个ABI规范,描述在x86-64上使用的调用约定。除了有自己的x86-64调用约定的Windows之外,所有操作系统都遵循它。为什么?

有人知道造成这种差异的技术、历史或政治原因吗,还是这纯粹是一个尼希德综合征的问题

我知道不同的操作系统可能对更高级别的东西有不同的需求,但这并不能解释为什么例如Windows上的寄存器参数传递顺序是
rcx-rdx-r8-r9-rest-on-stack
,而其他所有操作系统都使用
rdi-rsi-rdx-rcx-r8-r9-rest-on-stack

另外,我知道这些通话约定通常有什么不同,如果需要,我知道在哪里可以找到详细信息。我想知道的是为什么


编辑:有关如何操作,请参见此处的和链接

Win32对ESI和EDI有自己的用途,并且要求不修改它们(或者至少在调用API之前恢复它们)。我想象64位代码对RSI和RDI也有同样的作用,这就解释了为什么它们不用于传递函数参数

但是,我无法告诉您为什么要切换RCX和RDX。

在x64上选择四个参数寄存器-UN*X/Win64通用 关于x86,需要记住的一点是,“regnumber”编码的寄存器名并不明显;就指令编码而言(MOD R/M字节,请参阅),寄存器号0…7按顺序为:AX、
、CX
、DX
、BX
、SP
、BP
、SI
、DI

因此,选择A/C/D(regs 0..2)作为返回值和前两个参数(这是“经典的”32位
\uu fastcall
约定)是一个合乎逻辑的选择。就64位而言,订购了“更高”的regs,微软和UN*X/Linux都选择了
R8
/
R9

记住这一点,如果您选择四个寄存器作为参数,那么微软选择的
RAX
(返回值)和
RCX
RDX
R8
R9
(arg[0..3])是可以理解的选择

我不知道为什么AMD64 UN*X ABI在
RCX
之前选择了
RDX

在x64上选择六个参数寄存器-UN*X特定 RISC架构上的UN*X传统上在寄存器中传递参数——特别是对于前六个参数(至少在PPC、SPARC、MIPS上是这样)。这可能是AMD64(UN*X)ABI设计人员选择在该体系结构上使用六个寄存器的主要原因之一

因此,如果希望六个寄存器传入参数,并且为其中四个寄存器选择
RCX
RDX
R8
R9
,那么您应该选择另外两个

“更高”的regs需要一个额外的指令前缀字节来选择它们,因此具有更大的指令大小占用空间,因此如果有选项,您不想选择其中任何一个。在经典寄存器中,由于
RBP
RSP
的隐含含义,这些寄存器不可用,
RBX
传统上在UN*X(全局偏移表)上有特殊用途,AMD64 ABI设计者似乎不希望不必要地与之不兼容。
因此,唯一的选择是RSI/
RDI

所以,如果你必须把RSI/
RDI
作为参数寄存器,它们应该是哪些参数

使它们成为
arg[0]
arg[1]
有一些好处。见赵的评论。
?SI
?DI
是字符串指令源/目标操作数,如cHao所述,它们用作参数寄存器意味着使用AMD64 UN*X调用约定,例如,最简单的
strcpy()
函数只包含两条CPU指令
repz movsb;ret
,因为调用者已将源/目标地址放入正确的寄存器中。特别是在低级编译器生成的“胶粘”代码中(例如,在构建中,一些C++堆分配器零填充对象,或者内核零填充堆页在 SBRKE)(<代码> >或复制写页错误)中大量的块复制/填充,因此,对于频繁使用的代码来说,它将非常有用,因为这些代码用于保存两个或三个CPU指令,否则这些指令会将这些源/目标地址参数加载到“正确”的寄存器中

因此,在某种程度上,UN*X和Win64的不同之处在于,UN*X在故意选择的
RSI
/
RDI
寄存器中“预先”了两个附加参数,而在
RCX
RDX
R8
R9
寄存器中自然选择了四个参数

除此之外。。。 UN*X和Windows x64 ABI之间的区别不仅仅是参数到特定寄存器的映射。有关Win64的概述,请选中:


Win64和AMD64 UN*X在stackspace的使用方式上也有显著差异;例如,在Win64上,调用方必须为函数参数分配堆栈空间,即使参数0…3在寄存器中传递。另一方面,在UN*X上,叶函数(即不调用其他函数的函数)如果需要的堆栈空间不超过128字节,则根本不需要分配堆栈空间(是的,您拥有并可以使用一定数量的堆栈,而无需分配它……除非您是内核代码,这是一个漂亮的bug源)。所有这些都是特定的优化选择,这些选择的大部分原理都在原始海报的维基百科所指向的完整ABI参考文献中进行了解释。

IDK为什么Windows会这样做。请参阅此答案的结尾部分以了解猜测。我很好奇这个系统是如何运作的