【问题标题】:How are values returned from a function using the stack如何使用堆栈从函数返回值
【发布时间】:2020-03-03 14:15:46
【问题描述】:

假设这段代码:

int add(int a, int b){
    int c = a+b;
    return c;
}

int main(){
  printf("%d\n", add(3,4));
}

以下通常是在汇编中的实现方式:

- push 4 to stack
- push 3 to stack
- push return address which is the address of the next instruction, `print()` to stack
- call add
- do addition and push c on the stack
- pop c from stack ??
- return to main

那么返回值会发生什么,它不能在add 帧上,因为它会在最后被清除。它会被放入main 的堆栈中吗?

让我们假设这些值被推送到大头钉而不是寄存器。

【问题讨论】:

  • 返回值通常存放在寄存器中。但当然取决于 ABI..
  • 对于此类类型,传入一个隐藏指针。有关详细信息,请参阅 ABI 文档和/或检查编译器生成的汇编代码。
  • 如果你正在设计一个函数如何被调用以及如何返回值的协议,并且你不使用寄存器来返回值,那么逻辑选择包括在调用者的堆栈帧中指定一个位置,其中返回值将被写入或让调用者传递将写入返回值的地址。例如,如果期望返回一个四字节的int,调用者可能会在堆栈上为其腾出空间,然后执行调用指令。被调用的例程会将其返回值写入该保留空间,高于堆栈指针。
  • 在常见的 ABI 中,当函数返回类型是大对象时,可能是几百字节的结构,调用函数会传递一个指向空间的指针。这个指针没有出现在源代码的显式参数中;它以对常规源代码“隐藏”的方式传递。被调用函数使用该指针写入结果。
  • 哪个汇编程序?请添加相关的系统标签,例如 x86。请参阅stackoverflow.com/tags/assembly/info 了解标签用法。在混合 C 和汇编程序时,ABI 调用约定也很重要,因此您还必须指定编译器和操作系统(如果有)。

标签: c assembly calling-convention


【解决方案1】:

这取决于架构和调用约定。在 x86-32 中,对于 64 位结果,几乎每个调用约定都有 eaxedx:eax 中的返回值。所以你的add 函数可能有说明:

mov eax, dword ptr [esp+4] ; put 1st arg in eax
add eax, dword ptr [esp+8] ; add eax with 2nd arg
ret                        ; return

不需要额外的工作,因为返回值应该在 eax 中。

也就是说,除非您询问特定架构,否则您不会找到“一般情况”的答案,即使这样,也可能有多种不同的调用约定。

【讨论】:

    猜你喜欢
    • 2014-09-06
    • 2019-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-01
    • 1970-01-01
    相关资源
    最近更新 更多