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
退出功能......
mov esp, ebp ;Restoring the saved value of esp
pop ebp ;Restoring the value of ebp from the stack
(是的,我知道我可以使用进入和离开,但我更喜欢这种方式.)
我的问题是当esp恢复时,堆栈上的四字节变量是否会弹出或以某种方式神奇地消失?我看不出pop ebp如何不会从堆栈中弹出保留(并且最有可能使用)的四个字节.在我看来,如果你在函数期间把任何东西都推到了堆栈上,当pop ebp发生时它仍然存在,因此pop ebp不会产生保存的ebp,而是堆栈顶部的东西.当值恢复时,更改esp寄存器是否只是从堆栈顶部掉了下来?
"对于我的眼睛,如果你在功能期间将任何东西推到堆叠上,它会在弹出ebp发生时仍然存在[...] "
不,因为在pop ebp指令之前,你有这个:
Run Code Online (Sandbox Code Playgroud)mov esp, ebp ;Restoring the saved value of esp
请记住,这esp实际上是堆栈"顶部"的地址.从堆栈中推入和弹出会更改此寄存器.因此,如果您更改此注册表,您将更改下一个push或pop将要发生的位置.
所以上面的指令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
首先要做的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)
的sub esp, 8是像推动8个字节到堆栈中,只有没有什么将被写入到所述存储器位置; 所以我们最终得到8个未初始化的字节; 这是我们的函数可用于局部变量的内存区域.它可以通过eg [ebp-4]或者引用这些局部变量[ebp-8],它可以引用它的32位参数via [ebp+8](跳过push ebp和eip).
在您的函数期间,堆栈可能如下所示:
+------------+                         | "push" decreases "esp"
|  <arg>     |                         |
+------------+  <-- ebp+8              |
| <prev eip> |                         v
+------------+  <-- ebp+4
| <prev ebp> |
+------------+  <-- ebp
|  <locals>  |
+------------+  <-- ebp-4
|  <locals>  |                         ^
+------------+  <-- ebp-8              |
|  ...       |                         |
+------------+  <-- esp                | "pop" increases "esp"
在函数结束时,这将发生:
mov esp, ebp  ; "pop" over local variables and everything else that was pushed
pop ebp       ; restore previous stack frame
最后:
ret           ; essentially this does a "pop eip" so program execution gets
              ; transferred back to instruction after the "call fn"
(PS:调用代码必须弹出传递给函数的参数,例如通过在... add esp, 4之后执行call fn.)
我很久没有使用汇编语言,所以这一切都来自记忆.我可能会关注一些优点,但我希望你能得到一般情况.