assembly - 为什么这段代码在调用函数后两次弹出到同一个寄存器?

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/

相关文章:

java - 为什么 Double::compareTo 可以作为 Stream.max(Compa

django - 模型中的用户 OneToOneField

java - 如何将充满 if 语句的 for 循环更改为更优雅/高效的代码?

coq - Coq 分支和回溯的多次成功?

java - 检查字符串中是否存在模式

firebase - iOS 离线时的 FCM 行为

r - 如何原型(prototype)(启动)从其他插槽派生的 S4 插槽?

intellij-idea - IdeaVim:如何在不使用箭头键的情况下循环浏览列表项?

r - 从列值比较中确定 R 数据框行值

angularjs - 为什么模块 'ui.bootstrap' 不可用?