push EAX
push 8
call malloc
pop EBX
pop EBX
mov [EAX], 0
mov [EAX+4], EBX
为什么我们需要做2次pop EBX? EBX 每次会得到什么值?
最佳答案
基本规则是,无论您插入什么,都必须弹出。否则,您将不平衡堆栈并导致代码崩溃或更糟。该规则意味着您需要弹出与您推送的值相同大小(以字节为单位)的值。
所以在这种情况下,您在调用 malloc 之前将 8 个字节压入堆栈:
push EAX ; push a DWORD-sized register (4 bytes)
push 8 ; push a DWORD immediate (4 bytes)
要在函数调用后清理栈(malloc
需要,因为它使用cdecl
调用约定,即caller-cleanup),需要pop 8 个字节。碰巧一个方便的方法是弹出一个寄存器大小的值两次:
pop EBX ; pop 4 bytes
pop EBX ; pop 4 bytes
栈是LIFO .
第一个 pop 将 8
放入 EBX
(因为这是您推送的最后一个东西),您并不关心。下一个 pop 将 EAX
的原始值放回 EBX
(您压入的第一个东西),稍后您将继续使用它。
如果您不关心保留任何从堆栈弹出的值,您可以简单地使用 ADD 指令,将 8 个字节添加到堆栈指针:
add esp, 8
这可能比两次 pops 稍快,但实际上稍大(3 个字节而不是 2 个字节,as Jester points out)有时优化代码大小与优化代码速度一样重要,因为当代码是更小,更多的可以放入缓存。但在这种情况下,我怀疑更重要的问题是获得被推送的第一个值。由于 malloc 只接受一个参数,第一次推送的唯一原因是保留 EAX
的原始值,因为它被函数调用破坏了(函数在 EAX中返回它们的结果)
)。因此,另一种编写代码的方法是:
; Save EAX by moving it into a caller-save register
; (that will not get clobbered by the malloc function).
mov EBX, EAX
; Call the malloc function by pushing a 4-byte parameter and then rebalancing the stack.
push 8
call malloc
add esp, 4
; EAX contains malloc's return value, and EBX contains the original value of EAX
; that we saved before calling malloc.
mov [EAX], 0
mov [EAX+4], EBX
https://stackoverflow.com/questions/38246596/