论证如何传递?

jay*_*lee 10 c x86 assembly calling-convention

我想知道如何将参数传递给C中的函数.存储的值在哪里以及如何检索它们?可变参数传递如何工作?此外,因为它是相关的:返回值怎么样?

我对CPU寄存器和汇编器有基本的了解,但还不足以让我彻底了解GCC向我吐出的ASM.一些简单的注释示例将非常感激.

Bla*_*iev 18

考虑这段代码:

int foo (int a, int b) {
  return a + b;
}

int main (void) {
  foo(3, 5);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译它gcc foo.c -S给出了程序集输出:

foo:
    pushl   %ebp
    movl    %esp, %ebp
    movl    12(%ebp), %eax
    movl    8(%ebp), %edx
    leal    (%edx,%eax), %eax
    popl    %ebp
    ret

main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $8, %esp
    movl    $5, 4(%esp)
    movl    $3, (%esp)
    call    foo
    movl    $0, %eax
    leave
    ret
Run Code Online (Sandbox Code Playgroud)

所以基本上调用者(在这种情况下main)首先在堆栈上分配8个字节以容纳两个参数,然后将两个参数放在堆栈上相应的偏移量(40),然后call发出指令将控制转移到foo常规.该foo程序堆栈,从相应的偏移读取它的参数,还原它,并把它的返回值的eax寄存器,所以它提供给调用者.


Ebo*_*ike 5

这是特定于平台的,也是"ABI"的一部分.事实上,一些编译器甚至允许您在不同的约定之间进行选择.

例如,Microsoft的Visual Studio提供了__fastcall调用约定,该约定使用寄存器.其他平台或调用约定仅使用堆栈.

变量参数以非常类似的方式工作 - 它们通过寄存器或堆栈传递.在寄存器的情况下,它们通常按照类型的升序排列.如果你有类似的东西(int a,int b,float c,int d),那么PowerPC ABI可能放在ar3,br4,dr5和cfp1中(我忘记了float寄存器开始的地方,但你明白了) .

返回值再次以相同的方式工作.

不幸的是,我没有很多例子,我的大部分程序集都在PowerPC中,你在程序集中看到的只是代码直接用于r3,r4,r5,并将返回值放在r3中.


Sie*_*geX 2

您的问题比任何人都可以在 SO 帖子中合理尝试回答的问题要多,更不用说它的实现也已定义。但是,如果您对 x86 答案感兴趣,我建议您观看题为“编程范式

”的斯坦福 CS107 讲座,其中您提出的问题的所有答案都将在前 6-8 个讲座中详细(而且非常雄辩地)解释。