栈帧statck frame
- 用栈帧来分配给每个函数调用分配一段内存
- ebp为帧指针
- ebp与esp之间的部分为当前帧
在gdb
调试中info frame
该命令会依次打印出
(gdb) info frame
Stack level 0, frame at 0xbffff640:
当前栈帧的编号,以及栈帧的地址;
eip = 0x80483ad in main (second.c:16);
eip即程序计数器,要执行的下一条指令的汇编地址,在second.c的16行执行,即0x80483ad <main+25> leave
saved eip 0x237d28
被保存的eip,他是一个返回地址,即从该被调用者堆栈返回后在调用者堆栈帧中来恢复指令,根据CALL指令
将其压入堆栈(保存以供返回)。
source language c.
编写此栈帧所用的编程语言;
Arglist at 0xbffff638, args:
函数参数的存储地址以及值,这里为空
Locals at 0xbffff638,
函数中局部变量的存储地址;
Previous frame's sp is 0xbffff640
前一帧的sp为0xbffff640,这是前一帧的堆栈指针指向(调用者帧)的位置,在调用时,它也是被调用堆栈帧的起始内存地址
Saved registers:
ebp at 0xbffff638, eip at 0xbffff63c
栈帧中存储的寄存器变量,例如指令寄存器(64位环境中用 rip 表示,32为环境中用 eip 表示)、 堆栈基指针寄存器(64位环境用 rbp 表示,32位环境用 ebp 表示)等。
上面显示的是两个保存的寄存器在当前帧上即被调用者堆栈上的两个地址。在0xbffff638处的ebp是保存调用者堆栈帧的ebp寄存器
的地址(它是寄存器用来存值的地址,而不是调用者的堆栈地址,即对应于 push %ebp
)
ebp
是通常被视为此堆栈帧本地地址的起始地址的寄存器,使用偏移量
进行寻址。换句话说,局部变量的操作都使用此ebp,因此会有诸如mov -0x4(%ebp),%eax
等的内容。
同前面的ebp存储一样,在堆栈地址0xbffff63c处存eip
的值(其中保存的值为0x1a7d28)
called by frame at 0xbffff624, caller of frame at 0xbffff5ec
当前函数的调用者,对应的栈帧的地址;
只有main的函数的栈帧分析
debug栈内容查看
(gdb) x/8xw 0xbffff628
0xbffff628: 0x080483cb 0x00388ff4 0x00000003 0x00000005
0xbffff638: 0xbffff6b8 0x001a7d28 0x00000001 0xbffff6e4
(gdb) info frame
Stack level 0, frame at 0xbffff640:
eip = 0x80483a1 in main (second.c:14); saved eip 0x1a7d28
source language c.
Arglist at 0xbffff638, args:
Locals at 0xbffff638, Previous frame's sp is 0xbffff640
Saved registers:
ebp at 0xbffff638, eip at 0xbffff63c
push %ebp //保存上一个栈帧的帧指针,并设置当前的指针
movl %esp, %ebp
subl $16, %esp
第一行保存旧的 %ebp
,此时新的栈空间还没有创建,但保存旧的 %ebp 的这一行空间将作为新栈帧的栈底,也就是帧指针,
因此第二行将栈指针 %esp
(永远指向栈顶
)的值设置到 %ebp 上。 第三行将 %esp 下移 16 个字节,这一行其实就是为函数main开辟栈空间了。(有部分空间未使用,这个是为了地址对齐,不影响我们的分析,可以忽略。)
每个小方格表示4个字节的内存单元
movl $3, -8(%ebp)
movl $5, -4(%ebp)
movl $0, %eax
以%ebp为基址进行偏移寻址写入局部变量a=3,b=5
leave
ret
leave指令相当于下面两条:
movl %ebp ,%esp //撤销栈空间,回滚%esp。
popl %ebp //恢复上一个栈帧的%ebp。
ret指令相当于下面的指令
popl %eip //恢复指令指针寄存