需要帮助了解如何使用 add esp, 12 弹出堆栈

Need help understanding how to pop stack with add esp, 12

我对这个汇编程序有疑问:

extern printf                ; the C function, to be called

SECTION .data                ; Data section, initialized variables
a: dd 5                      ; int a=5;
fmt: db "a=%d, eax=%d",10,0  ; The printf format, '\n','0'

SECTION .text                ; Code section.

global main                  ; the standard gcc entry point
main:                        ; the program label for the entry point

push ebp                  ; calling convention
mov ebp, esp

mov eax, [a]              ; put a from store into register
add eax, 2                ; a+2
push eax                  ; value of a+2
push dword [a]            ; value of variable a
push dword fmt            ; address of ctrl string
call printf               ; Call C function
add  esp, 12              ; pop stack 3 times = 4 bytes

mov esp, ebp              ; returning convention
pop ebp                   ; same as "leave" op

mov eax,0                 ; normal (no error) return value
ret                       ; return

我看到接近尾声时弹出堆栈 3 次,等于 4 个字节。但是我不明白为什么要在最后出栈,12怎么等同于“3次=4个字节” 还有,stacked是不是一定要在最后出栈呢?

当你调用一个(子)函数时,CPU需要保存要跳回的值;它为此使用堆栈

所以当你进入函数时,栈顶的值是return地址

此代码将另外 12 个字节添加到堆栈

push eax                  ; value of a+2
push dword [a]            ; value of variable a
push dword fmt            ; address of ctrl string

这些推送中的每一个都向[ESP]写入4个字节,并减去ESP的4个字节(减法是因为堆栈向下增长)。在你可以 ret 返回给你的被调用者之前,你必须去掉堆栈上的这 12 个字节。最简单的方法是,他在代码中做了什么:

add  esp, 12              ; pop stack 3 times = 4 bytes

另一方面,该函数将当前 ESP 保存在函数顶部的 EBP 中(这会创建一个新的 'stack frame')

push ebp                  ; calling convention
mov ebp, esp

这样做是为了能够处理被调用者的函数参数和您可能为 [EBP +/- offset] 保留位置的局部变量,并且它使您能够通过加载以前的值来恢复整个堆栈再次:

mov esp, ebp              ; returning convention
pop ebp                   ; same as "leave" op

注意:在这段代码中,堆栈被清理了两次。 (将 12 添加到 ESP(它从系统调用作为参数获得的值中清除堆栈),然后再次从 EBP 加载先前的值。在这种情况下,不需要其中之一