【问题标题】:STDCALL vs CDECL: `ret` vs `sub esp` have anything to do with the calling convention?STDCALL vs CDECL:`ret` vs `sub esp`与调用约定有什么关系?
【发布时间】:2018-10-09 18:38:27
【问题描述】:

Assembly Language, Seventh Edition for x86 Processors by Kip Irvine 的第 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 指令提供了一个整数参数,该参数在返回调用过程后又向ESP 加8。整数必须等于过程参数占用的堆栈空间:

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 调用约定允许子例程声明可变数量的参数。调用者可以决定它将传递多少个参数。

【问题讨论】:

  • 答:嗯,显然是的?调用约定确实定义了子程序应该如何返回(即使用什么机制来给子程序返回点)以及 CPU 应该处于什么状态,因此子程序末尾的清理代码与调用约定相关(尽管您可以如果您发现一些更适合您的情况的方法,例如跳到其他子程序的尾部而不是调用它等,也可以以替代方式实现它。)=所以我不确定问题是什么,确切地说是什么预期答案?
  • 问题是什么? STDCALL 和 CDECL 在概念上 之间的主要区别在于 CDECL 允许使用 var args 并且 STDCALL 按 调用者 预期 处理参数,而 CDECL 处理caller 使用的它们(是的,早期的 C 允许这样做)。

标签: x86 x86-64 calling-convention stdcall cdecl


【解决方案1】:

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

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

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

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

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

整个RET 似乎让人分心,因为没有任何关于特定于 x86 的调用约定。事实上,Windows 10 在 ARM 上运行,doesn't even support RET 此外,在第一个带有cdecl 的示例中,编译器可以编写,

ret 8

而不是

 add esp,8
 ret

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

【讨论】:

  • ret 8add esp,8 ret 不一样。 ret 8first 将返回地址从堆栈中弹出,然后通过向其添加立即值来调整 ESP
  • stdcall 约定也强制您在每次调用后“释放”本地堆栈空间,而使用 C 约定您可以保持 esp 修改,并在原始调用之后将该本地区域用于其他目的返回的函数,例如为下一次调用准备参数或简单地在其中存储一些本地变量,或者在使用单个sub esp,... 等多次调用后释放所有参数,等等......就像在汇编中一样,您给予指令的上下文/解释/逻辑越多组,您实际上错过一些微妙的细节或优化可能性的风险越大,您会不会想太多。
猜你喜欢
  • 1970-01-01
  • 2023-03-21
  • 2011-08-23
  • 2020-02-15
  • 1970-01-01
  • 2012-03-17
  • 2023-03-28
  • 2017-03-14
相关资源
最近更新 更多