x86汇编程序通常以以下序言开头:
push ebp ; Save ebp
mov ebp, esp ; Set stack frame pointer
sub esp, localbytes ; Allocate space for locals
push <registers> ; Save registers
我已经看到函数push
在1到4个寄存器中。
除了推送所需的ebp
之外,还有什么决定您是否推送edi
,esi
和/或ebx
?
你应该总是推他们吗?我无法找到解决此问题的参考资料。
答案 0 :(得分:3)
推送你不会修改的寄存器是没有意义的。除此之外,您可以选择保留哪些寄存器。采用一些约定,以便调用代码和被调用代码都知道它负责的内容。
答案 1 :(得分:1)
您正在查看caller vs callee save registers。基本思想是,如果要将值存储在被调用者保存的寄存器中,则需要将其当前值保存在某处,以便在函数调用结束时将其恢复。
答案 2 :(得分:1)
此Wikipedia部分可能有所帮助: http://en.wikipedia.org/wiki/X86_calling_conventions#Register_preservation
一般来说,除了明确告知您不必保留的寄存器外,您保留每个寄存器。如果使用32位代码并且被调用,则不必保留EAX,ECX或EDX,但是您必须保留其他所有内容。在64位代码中有更多寄存器,因此您也必须保留它们。
在您的示例中,您必须推送EBP,因为您更改了它。在编写程序集以使用EBP索引参数时,这很常见。参数被压入堆栈,然后返回地址。因此,要获得参数,您必须访问堆栈。如果ESP正在改变它,那么记住你的参数相对于ESP的位置会非常痛苦。所以我们保存一个&#34;书签&#34;使用EBP。
但如果你不需要这样做,那就不要了!例如,Microsoft使用的__fastcall调用约定将传递ECX中的第一个参数。我有简单的代码,从不将任何东西推送到堆栈,只使用EAX和ECX。