-1

Kip Irvine 的Assembly Language, Seventh Edition for x86 Processors中,第 325 页,它在8.2.4 32-Bit Calling Conventions下说,

C 调用约定... C 调用约定以一种简单的方式解决了清理运行时堆栈的问题:当程序调用子例程时,它会在CALL指令后面加上一条语句,将值添加到堆栈指针 ( ESP) 等于到子程序参数的组合大小。这是一个示例,其中两个参数(5 和 6)在执行CALL指令之前被压入堆栈,

Example1 PROC
  push 6
  push 5
  call AddTwo
  add esp, 8
  ret
Example1 ENDP

因此,用 C/C++ 编写的程序总是在子程序返回后从调用程序的堆栈中删除参数。

它继续说

STDCALL 调用约定从堆栈中删除参数的另一种常用方法是使用名为 的约定STDCALL。在下面的AddTwo过程中,我们为指令提供一个整数参数,RET在返回调用过程后,它又将 8 加到 ESP 中。整数必须等于过程参数占用的堆栈空间字节数:

AddTwo PROC
  push ebp
  mov ebp,esp
  mov eax,[ebp+12]
  add eax,[ebp+8]
  pop ebp
  ret 8
AddTwo ENDP

应该指出的是STDCALL,像 C 一样,以相反的顺序将参数压入堆栈。通过在RET指令中包含参数,STDCALL减少了为子程序调用生成的代码量(通过一条指令),并确保调用程序永远不会忘记清理堆栈。另一方面,C 调用约定允许子例程声明可变数量的参数。调用者可以决定它将传递多少个参数。

4

1 回答 1

-4

该代码有点令人困惑,因为一个显示调用,另一个显示函数。为简单起见,它们都应该同时显示。为了调用约定,对堆栈进行了两个阶段的修改,

  • 在准备调用时,将参数和推入堆栈。
  • 在被调用的函数中,局部变量在堆栈上分配。

两种约定之间的区别不是“一条指令”,也与RET每个说法无关,而是清理发生的位置。参数在调用之前放置在堆栈上,所以它们应该

  1. 当函数清理自己(本地人)时被清理。
  2. 函数返回后进行清理。

作为导入说明,第一个选项有优势,即您声明一个具有可变数量参数的函数。

整个RET部分似乎让人分心,因为没有任何关于特定于 x86 的调用约定。事实上,Windows 10 在 ARM 上运行,甚至不支持RET此外,在第一个示例中cdecl,编译器可能已经写了,

ret 8

而不是

 add esp,8
 ret

它也会产生同样的效果。事实上,它会保存一条指令

于 2018-10-09T18:38:27.093 回答