【问题标题】:Accessing parameters using registers in 64 bit arch使用 64 位架构中的寄存器访问参数
【发布时间】:2014-01-24 13:14:17
【问题描述】:

我参加了华盛顿大学的硬件/软件接口课程。 在那门课程中,讲师解释了 x86-32 与 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 是 fastcall 调用约定。

为什么会这样?一直都是这样吗?如果我选择传递多个寄存器参数会怎样?

【问题讨论】:

  • 我认为寄存器的使用取决于所使用的编译器的实现——与操作系统架构无关。
  • 所有这些不仅是特定于平台的,而且是非常特定于编译器的,并且很大程度上取决于优化。如果你有比可用寄存器更多的参数,那么一些(或全部)可能会在堆栈上传递。
  • @suspectus 处理器制造商通常会定义大多数编译器提供商遵循的约定,这允许互操作性(链接到使用不同编译器编译的库)。在给定硬件上存在不同约定的情况下,操作系统供应商倾向于指定一个约定,即使只是通过系统库隐式地指定。
  • @Gilles 有趣 - 感谢您的信息。
  • @Gilles 除了当他们像 ms 对无窗口 rtf 一样出错时!

标签: c assembly operating-system


【解决方案1】:

x86 处理器比 x64 旧得多。 x86 的调用约定环境随着不同的操作系统和不同的编译器选择不同的方法而有机地发展。这些天来,只有这些调用约定仍然被广泛使用:

  • cdecl
  • 标准调用
  • Borland 快速呼叫
  • MS 快速调用

但多年来,已经使用了更多的调用约定。您可以想象,尝试任何类型的二进制互操作都会因为存在许多不同的调用约定而变得复杂。

当主要操作系统采用 x64 时,已经吸取了教训。并且操作系统设计者非常希望有一个单一的调用约定作为明确指定的 ABI 的一部分。这有很多优点。它不仅使开发人员的工作更轻松,而且使开发编译器工具变得更容易,而且还使保护代码更容易。

有趣的是,x64 上仍然存在多种调用约定,但在单个平台上,只有一种 x64 调用约定。类 Unix 操作系统使用 MS x64 调用约定和 System V x64 调用约定。这两种 x64 调用约定都是 fastcall,参数通过寄存器传递。

如果我选择传递多个寄存器参数会怎样?

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

【讨论】:

    【解决方案2】:

    使用的调用约定取决于您的编译器。编译器之间的默认设置也会有所不同。 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"));
    

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

    【讨论】:

      猜你喜欢
      • 2014-01-05
      • 2016-01-05
      • 2014-12-17
      • 2019-04-21
      • 1970-01-01
      • 2022-01-04
      • 2016-01-18
      • 2011-10-26
      • 1970-01-01
      相关资源
      最近更新 更多