C-使用64位拱门中的寄存器访问参数



我已经聘请了华盛顿大学的硬件/软件接口课程。在那个课程中,讲师对x86-32 vs x86-64进行了解释。他显示出一个简单的拆卸功能,进行了交换。

void swap(int *xp, int *yp)
{
    int t0 = *xp;
    int t1 = *yp;
    *xp = t1;
    *yp = t0;
}

在x86-32中,我可以说参数通过堆栈传递,但是在x86-64中,参数通过寄存器传递。

我可以说,在x86-32中,参数是在cdecl呼叫约定的情况下传递的,而不是x86-64,这是一个快速通话。

为什么会发生?一直这样吗?如果我选择传递超过寄存器参数的数量,会发生什么?

x86处理器比x64大得多。X86的呼叫大会格局通过不同的操作系统和选择不同方法的不同编译器有机地生长。如今,只有这些调用约定仍被广泛使用:

  • cdecl
  • stdcall
  • Borland FastCall
  • MS FastCall

多年来,使用了更多的呼叫惯例。您可以想象,尝试任何类型的二进制互动都因存在许多不同的调用惯例而变得复杂。

当主要操作系统采用X64时,已经学习了课程。操作系统设计师非常热衷于作为一个明确的ABI的一部分进行单个呼叫惯例。这有很多优势。它不仅使开发人员变得更轻松,而且使开发编译器工具更加容易,并且还使得更容易保护代码。

有趣的是,X64上仍然存在多个呼叫约定,但是在一个平台上,只有一个X64呼叫约定。有MS X64调用惯例和系统v X64呼叫约定,如UNIX所使用的操作系统。这两个X64呼叫约定都是通过寄存器传递的参数的快速通道。

如果我选择传递超过寄存器参数的数量,会发生什么?

在这种情况下,对于我曾经遇到过的所有调用约定,参数都在堆栈上传递。

所使用的调用约定将取决于您的编译器。编译器之间的默认设置也将有所不同。CDECL和FASTCALL是两个最广泛采用的召集惯例。FastCall通过寄存器中的前两个参数,休息在堆栈上。您通常可以将调用约定用作编译器标志。另外,您可以将调用约定指定为函数属性

void __attribute__((cdecl)) swap(int *xp, int *yp);
void __attribute__((fastcall)) swap(int *xp, int *yp);

,甚至指定您需要特定寄存器中的参数:

void swap(int *xp asm ("rax"), int *yp asm ("rbx"));

在任何情况下,编译器都可以忽略任何指令和/或发出错误/警告,如果不可能。

相关内容

  • 没有找到相关文章