【问题标题】:Beginner learning assembly preserving esp after function calls函数调用后保留 esp 的初学者学习汇编
【发布时间】:2011-01-26 15:31:18
【问题描述】:

我是学习一些汇编的初学者,在函数调用之前保留 ESP 寄存器时,通过加法还是减法来做这件事有关系吗?很难解释,考虑以下

mov esi, esp
sub esp, 12 // on 32bit OS this would mean that there are 3 arguments to the function
// push, function call etc
cmp esi, esp // should be the same

mov esi, esp
// push, function call etc
add esp, 12
cmp esi, esp // should be the same

此外,如果由于某种原因 cmp 失败,是否可以安全地执行 mov esp, esi 来重新对齐堆栈?

谢谢

编辑:为什么我需要为 sprintf 之类的调用执行此操作,但 MessageBox 似乎为我修复了 ESP?我怎么知道什么功能需要这个,什么不需要?

【问题讨论】:

    标签: assembly stack


    【解决方案1】:

    是的,在使用 esp 时获取正确的符号很重要(在这种情况下,减法,但有时在引用堆栈中已有的内容时需要添加,例如函数内的参数)。原因是堆栈在内存中向下增长。这与我们通常对堆栈的看法相反(您将事物放在顶部并从顶部移除它们),在内存中,随着堆栈的增长,这将是越来越高的地址。但是 X86(和大多数其他)处理器上的调用堆栈实际上是向下增长的。这就像将盘子添加到堆栈的底部,然后将它们从底部移除......在内存中,随着更多的东西被添加到堆栈中,地址会越来越低。

    是的,将值更改为 esp 是安全的,只要您确定将其设置为堆栈内的有效位置,并且您确定不需要任何这样做会丢失您的信息。在这种情况下,您将 esp 保存在 esi 中正是出于这个原因……您可以从 esi 恢复 esp,因此无论之前的函数调用做了什么,您都知道 esp 正是您想要的位置。

    sprint 和 MessageBox 的区别在于“调用约定”。这告诉高级语言 (C) 在调用堆栈帧和寄存器时如何处理它们。 sprintf 是 cdecl,而 MessageBox 是 stdcall

    【讨论】:

      【解决方案2】:

      另外,为什么我需要为 sprintf 之类的调用执行此操作,但 MessageBox 似乎为我修复了 ESP?

      这是因为MessageBox() 函数在返回时会从堆栈中清除参数。因为参数个数是固定的,所以可以做到这一点。

      但是,sprintf() 函数是使用可变数量的参数调用的。该函数不知道调用者可能已经压入堆栈的数量,因此调用者有责任在调用返回时清理它们。

      【讨论】: