如何将任意参数推送到64位asm中的函数调用

时间:2016-06-05 00:37:15

标签: c gcc assembly x86-64

我正在试图弄清楚如何在函数调用之前将任意数量的参数压入堆栈,然后通过指向函数的泛型指针调用一个使用我已经推送到堆栈的参数集的函数

现在我正在尝试将四个参数传递给64位英特尔计算机上的预定义函数foo。我没有任何asm知识。我一直在阅读的很多内容告诉我使用movq将参数放在某些寄存器中,例如rdirsi,但我不完全理解为什么我不能只需将变量推入堆栈,并让函数通过偏移量引用传入的参数。

我已经花了几天时间用大量破碎的代码试图找出它,而我却无法做到。下面的代码编译,但无法正常工作,最后会出现分段错误。

我没有经验,这是我第一个将asm集成到C程序中的项目。我想如果我有一个工作的例子我应该能够扩展它,我只是在开始使用asm时遇到麻烦。

#include "stdio.h"

void foo(int a, int b, int c, int d)
{
  printf("values = %d and %d and %d and %d\r\n", a, b, c, d);
}

int main()
{
  int counter;
  for (counter = 0; counter < 4; counter++)
  {
    __asm__("movq %0, %%rdi;"
            "pushq %%rdi;"
            :
            : "g"(counter));
  }

  __asm__("call foo;");
}

1 个答案:

答案 0 :(得分:0)

如果您正在调用的函数是由asm编写的,那么是的,您可以使用自定义调用约定来传递堆栈中的args。

然而,you can't safely push anything in GNU C inline asm for the x86-64 SysV calling convention。您必须将堆栈指针调整到红色区域下方,然后在asm块结束之前将其恢复,因为无法告诉编译器您想要修改堆栈指针。

即使在没有红区的ABI(如Windows)中,您也不能只在push语句中asm,因为没有办法告诉编译器您已修改{ {1}},所以它需要使用不同的偏移来引用堆栈上的东西。

在C / C ++中没有理智的方法在运行时生成参数列表,即使长度不是编译时常量。如果你在asm中编写了整个调用者,那么肯定, 这很简单。没理由你不能在循环中rsp。 (可能的性能影响除外:IIRC,Intel CPU上的循环流缓冲区仅适用于不包含pushpush的循环,或者可能具有平衡pop / { {1}}。堆栈引擎可能需要在uops被放入该队列的早期阶段插入同步uops,而该队列也可以作为短循环的循环缓冲区。)

您可以声明一个指向可变参数函数的指针,并从C调用它,但不能使用运行时变量数量的参数。我认为Boost CPP宏可能会为您push扩展pop。 IIRC,c ++也有功能参数列表的一些模板内容,但我的谷歌搜索只出现了4的东西。我怀疑是否有任何可以编译为asm的东西可以处理运行时变量参数列表长度。

为什么不像普通人一样传递一个数组(指针和长度)?这可能会在调用者和被调用者方面表现更好(取决于你调用的函数) )。