实际上,这正是该leave
指令的作用,它是为了支持高级语言而引入的。但是,它并不经常使用。大多数编译器只是做显式mov esp, ebp; pop ebp
序列。另请参阅此问题。
但是,有时您可以进行“省略帧指针”优化。这释放了 EBP 以用作通用寄存器,但您(或编译器)随后必须在整个函数期间跟踪 ESP 更改,并使用可能更改的偏移量来处理局部变量或传入参数。如果这样做,则必须使用 pops 或显式 add 将 ESP 恢复为其原始值,然后再返回。
请注意,以上涉及整个功能(即序言/结语);当您需要在函数中间调用特定__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
指令通常比推送要长得多,因此如果代码大小是一个问题,您可能需要考虑这一点。