在一篇关于 _cdecl 调用约定的文章中,作者提到:
我的问题是:我是否可以简单地将 ESP 设置为当前的 EBP 值,而不是“将减去的相同数量添加到堆栈指针”或“一系列 POP 指令”?
喜欢:
mov esp, ebp
对我来说似乎是一种更好的方法,因为如果我稍后更改此函数的局部变量的数量,我就不必费心事后增加该值。
最佳答案
实际上,这正是 leave
instruction 所做的,它被引入以支持高级语言。然而,它并不经常使用;大多数编译器只执行显式的 mov esp, ebp; pop ebp
序列。另见 this question 。
但是,有时您可以进行“省略帧指针”优化。这释放了 EBP 以用作通用寄存器,但您(或编译器)随后必须在整个函数期间跟踪 ESP 更改,并使用可能更改的偏移量来寻址局部变量或传入参数。如果你这样做,你将 必须在 使用 pops 或显式添加在返回之前将 ESP 恢复到其原始值。
请注意,以上内容涉及整个功能(即 prolog/epilog);当您需要在函数中间调用特定的 __cdecl
函数时,您不能仅将 ESP 恢复为 EBP 值,因为该值仅在函数的最开始有效,在为局部变量分配任何空间之前。这里有两种方法:
1)推送参数,调用后恢复ESP:
push offset msg
call _printf
pop ecx ; clobbers ECX but shorter than add esp, 4
2) 将参数移动到保留的堆栈槽;在这种情况下,您不需要恢复 ESP:
mov dword ptr [esp+0], offset msg
call _printf
; no need to change ESP
如果选择第二个选项,则需要确保不在这些插槽中存储任何局部变量。此外,此类
mov
指令通常比 pushes 长得多,因此您可能需要考虑是否需要考虑代码大小。关于assembly - _cdecl 调用约定,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9478317/