栈指针 | 栈内容 | ebp间接寻址 |
---|---|---|
frame 1 | old ebp value | |
local variable 1 | ||
local variable 2 | ||
local variable 3 | ||
… | ||
arg2 | 12(%ebp) | |
arg1 | 8(%ebp) | |
ret addr | 4(%ebp) | |
frame 0 | old ebp value | (%ebp) |
local variable 1 | -4(%ebp) | |
local variable 2 | -8(%ebp) | |
… | ||
arg2 | 4(%esp) | |
esp -> | arg1 | (%esp) |
说明
esp总是指向栈顶
ebp指向的位置开启一个新栈帧
所以ebp又称为stack frame pointerebp往上的栈帧是calling函数的栈帧
ebp往下的栈帧是called函数的栈帧called函数的输入参数是放在calling函数的栈帧里
called函数返回后,calling函数负责清理栈帧里的输入参数。ret addr 由call指令自动push到栈上
对于frame 0函数,它获取参数是通过ebp间接寻址来获取的
对于frame 0函数,它为即将被调用的函数准备参数是通过esp间接寻址来设置的
代码示例
.text
.globl _add_and_print
.p2align 4, 0x90
_add_and_print:
pushl %ebp
movl %esp, %ebp
subl $40, %esp # 分配足够的栈空间
movl 8(%ebp), %eax
movl 12(%ebp), %ebx
addl %ebx, %eax
call L_0
L_0:
popl %ebx
lea L_str-L_0(%ebx), %ebx
movl %ebx, (%esp)
movl %eax, 4(%esp)
call _printf
movl %ebp, %esp
popl %ebp
ret
.cstring
L_str:
.asciz "sum = %d\n"
.text
.globl _main
.p2align 4, 0x90
_main:
pushl %ebp
movl %esp, %ebp
subl $40, %esp
movl $2, (%esp)
movl $3, 4(%esp)
call _add_and_print
movl %ebp, %esp
popl %ebp
ret
这里printf函数对应frame 0栈帧
它的输入参数是add_and_print函数通过esp间接寻址设置的
add_and_print函数的输入参数是通过ebp间接寻址获取的
subl $40, %esp
该指令为函数扩大栈帧空间,为函数内部可能的局部变量预留空间,以及为下一个要调用的函数的参数预留空间,局部变量从栈帧开始处向下存储,下一个要调用的函数的参数是从栈顶处向上存储,如下图所以: