关于汇编中push ebp和pop ebp指令的解释
Posted
技术标签:
【中文标题】关于汇编中push ebp和pop ebp指令的解释【英文标题】:explanation about push ebp and pop ebp instruction in assembly 【发布时间】:2011-04-07 23:23:10 【问题描述】:我在汇编中使用了堆栈,但我不知道 push ebp 和 pop ebp。
.intel_syntax noprefix
.include "console.i"
.text
askl: .asciz "Enter length: "
askb: .asciz "Enter breadth: "
ans: .asciz "Perimeter = "
_entry:
push ebp # establishing stack-frame
mov ebp, esp
sub esp, 12
Prompt askl
GetInt [ebp-4] # length
Prompt askb
GetInt [ebp-8] # breadth
mov eax, [ebp-4] # eax = l
add eax, [ebp-8] # eax = l + b
add eax, eax # eax = 2 * (l + b)
mov [ebp-12], eax
Prompt ans
PutInt [ebp-12]
PutEoL
mov esp, ebp
pop ebp # unwinding stack-frame
ret
.global _entry
.end
【问题讨论】:
提供更多上下文总是最好的。也就是说,实际问题确实很模糊,需要更多解释。 【参考方案1】:也许您对此感到疑惑:
push ebp
mov ebp, esp
sub esp, 12
这些行被称为汇编函数序言。前 2 条指令保存前一个基指针 (ebp) 并将 EBP 设置为指向堆栈上的该位置(在返回地址的正下方)。这会将 EBP 设置为 frame pointer。
sub esp,12
行正在为函数中的局部变量节省空间。可以使用[ebp - 4]
等寻址模式来寻址该空间。函数 args 的任何推入/弹出,或 call
指令本身推入返回地址,或我们调用的函数的堆栈帧,都将在当前 ESP 的此保留空间下方发生。
最后你有:
mov esp, ebp ; restore ESP
pop ebp ; restore caller's EBP
ret ; pop the return address into EIP
这是序言的反面(即尾声),因此可以恢复之前的上下文。这有时被称为“拆除”堆栈框架。
(EBP 在所有标准 x86 调用约定中都是非易失性的,也就是调用保留:如果您修改它,则必须恢复调用者的值。)
leave
指令与这两条指令完全一样,并且被一些编译器用来节省代码大小。 (enter 0,0
非常慢并且从未使用过 (https://agner.org/optimize/);leave
的效率与 mov + pop 差不多。)
请注意,使用 EBP 作为帧指针是可选,编译器不会对优化代码中的大多数函数这样做。相反,它们保存单独的元数据以允许堆栈展开/回溯。
【讨论】:
另请注意,您有一个名为leave
的指令,这正是 mov esp, ebp
和 pop ebp
所做的。【参考方案2】:
ebp
被称为基指针或帧指针。在进入您的函数时,您推送它(以保存调用函数的值)。然后,将堆栈指针esp
复制到ebp
,这样ebp
现在就指向函数的堆栈帧。然后在函数结束时弹出ebp
以便恢复调用函数的值。
为了澄清到底发生了什么——push
指令将指定寄存器(在本例中为ebp
)中的值放入堆栈,并将堆栈指针递减适当的量。 pop
操作是相反的——它递增堆栈指针并从堆栈中取出一个值并将其放入指定的寄存器中。
【讨论】:
以上是关于关于汇编中push ebp和pop ebp指令的解释的主要内容,如果未能解决你的问题,请参考以下文章