离开 x86 NASM 汇编语言中的函数时,堆栈是否会自动弹出?堆栈、汇编语言、是否会、函数

2023-09-07 03:39:30 作者:被时间冲淡的感情

例如让我们进入一个函数...

push ebp ;保存 ebpmov ebp, esp ;将esp存入ebpsub esp, 4 ;将四个字节保存到堆栈中

然后退出函数...

mov esp, ebp ;恢复esp的保存值pop ebp ;从栈中恢复 ebp 的值
vc6.0 c中运行汇编取地址的值程序自动退出

(是的,我知道我可以使用进入和离开,但我更喜欢这种方式.)

我的问题是,当 esp 恢复时,堆栈上的四字节变量是被弹出还是以某种方式神奇地消失了?我看不出 pop ebp 如何不只是从堆栈中弹出保留的(并且最有可能使用的)四个字节.在我看来,如果你在函数期间将任何东西压入堆栈,当 pop ebp 发生时它仍然存在,因此 pop ebp 不会产生保存的 ebp,而是堆栈顶部的东西.当esp寄存器的值被恢复时,更改esp寄存器是否会从栈顶跳出?

解决方案

在我看来,如果你在函数期间将任何东西压入堆栈,当 pop ebp 发生时它仍然会在那里[…]"

不,因为在 pop ebp 指令之前,你有这个:

mov esp, ebp ;恢复esp的保存值

请记住,esp 本质上是堆栈顶部"的地址.压入和弹出堆栈会更改此寄存器.因此,如果您更改此寄存器,您将更改下一次 pushpop 发生的位置.

所以上面的指令 mov esp, ebp 实质上将堆栈指针重置到它在初始 push ebp 之后的位置.(该位置通过 mov ebp, esp 指令直接保存到 ebp 寄存器中.)

这就是为什么 pop ebp 会弹出正确的东西.

但这确实假设您的函数没有更改 ebp 寄存器.

更新:

我在这里假设一个特定的调用约定,但让我们举个例子.假设我们有一个接受一个 32 位参数的函数,该参数通过调用堆栈传递给函数.

为了调用我们的函数,我们这样做:

push eax ;压栈参数调用 fn ;调用我们的函数;这会将 `eip` 推入堆栈

fn 做的第一件事就是建立自己的栈帧(并确保最后能恢复之前的栈帧):

push ebp ;所以我们可以稍后恢复之前的堆栈帧移动 ebp, esp ;初始化我们自己函数的栈帧子 esp, 8 ;为 8 个字节腾出空间(用于局部变量)

sub esp, 8 就像将 8 个字节压入堆栈,只是不会将任何内容写入内存位置;所以我们基本上得到了 8 个未初始化的字节;这是我们的函数可以用于局部变量的内存区域.它可以通过例如引用这些局部变量.[ebp-4][ebp-8],它可以通过 [ebp+8] 引用它的 32 位参数(跳过推送的 ebpeip).

在您的函数期间,堆栈可能如下所示:

+------------+ |"push" 减少 "esp"|<arg>||+------------+ <-- ebp+8 ||<prev eip>|v+------------+