在x86 NASM汇编语言中保留函数时,堆栈是否会自动弹出?

Sam*_*Sam 3 x86 assembly stack nasm

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

push ebp ;Saving ebp
mov ebp, esp ;Saving esp into ebp
sub esp, 4 ;Saving four bytes onto the stack
Run Code Online (Sandbox Code Playgroud)

退出功能......

mov esp, ebp ;Restoring the saved value of esp
pop ebp ;Restoring the value of ebp from the stack
Run Code Online (Sandbox Code Playgroud)

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

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

sta*_*ica 6

"对于我的眼睛,如果你在功能期间将任何东西推到堆叠上,它会在弹出ebp发生时仍然存在[...] "

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

mov esp, ebp ;Restoring the saved value of esp
Run Code Online (Sandbox Code Playgroud)

请记住,这esp实际上是堆栈"顶部"的地址.从堆栈中推入和弹出会更改此寄存器.因此,如果您更改此注册表,您将更改下一个pushpop将要发生的位置.

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

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

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

更新:

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

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

push eax      ; push argument on stack
call fn       ; call our function; this pushes `eip` onto the stack
Run Code Online (Sandbox Code Playgroud)

首先要做的fn是设置自己的堆栈框架(并确保最后一个框架可以恢复):

push ebp      ; so we can later restore the previous stack frame
mov ebp, esp  ; initialize our own function's stack frame
sub esp, 8    ; make room for 8 bytes (for local variables)
Run Code Online (Sandbox Code Playgroud)

sub esp, 8是像推动8个字节到堆栈中,只有没有什么将被写入到所述存储器位置; 所以我们最终得到8个未初始化的字节; 这是我们的函数可用于局部变量的内存区域.它可以通过eg [ebp-4]或者引用这些局部变量[ebp-8],它可以引用它的32位参数via [ebp+8](跳过push ebpeip).

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

+------------+                         | "push" decreases "esp"
|  <arg>     |                         |
+------------+  <-- ebp+8              |
| <prev eip> |                         v
+------------+  <-- ebp+4
| <prev ebp> |
+------------+  <-- ebp
|  <locals>  |
+------------+  <-- ebp-4
|  <locals>  |                         ^
+------------+  <-- ebp-8              |
|  ...       |                         |
+------------+  <-- esp                | "pop" increases "esp"
Run Code Online (Sandbox Code Playgroud)

在函数结束时,这将发生:

mov esp, ebp  ; "pop" over local variables and everything else that was pushed
pop ebp       ; restore previous stack frame
Run Code Online (Sandbox Code Playgroud)

最后:

ret           ; essentially this does a "pop eip" so program execution gets
              ; transferred back to instruction after the "call fn"
Run Code Online (Sandbox Code Playgroud)

(PS:调用代码必须弹出传递给函数的参数,例如通过在... add esp, 4之后执行call fn.)

我很久没有使用汇编语言,所以这一切都来自记忆.我可能会关注一些优点,但我希望你能得到一般情况.