通过bp寄存器查看调用栈关系
首先介绍下几个概念:
1)栈
栈是向下生长的,所谓向下生长是指从内存高地址->低地址的路径延伸,栈有栈底和栈顶,栈顶的地址要比栈底低。对x86体系的CPU而言,其中
寄存器ebp(basepointer )可称为“帧指针”或“基址指针”,64位机器为rbp。
寄存器esp(stackpointer)可称为“ 栈指针”,64位机器为rsp。
ebp 在未受改变之前始终指向栈帧的开始,也就是栈底,所以ebp的用途是在堆栈中寻址用的。
esp是会随着数据的入栈和出栈移动的,esp始终指向栈顶。
见下图,假设函数A调用函数B,我们称A函数为"调用者",B函数为“被调用者”则函数调用过程可以这么描述:
(1)先将调用者(A)的堆栈的基址(ebp)入栈,以保存之前任务的信息。
(2)然后将调用者(A)的栈顶指针(esp)的值赋给ebp,作为新的基址(即被调用者B的栈底)。
(3)然后在这个基址(被调用者B的栈底)上开辟(一般用sub指令)相应的空间用作被调用者B的栈空间。
(4)函数B返回后,从当前栈帧的ebp即恢复为调用者A的栈顶(esp),使栈顶恢复函数B被调用前的位置;然后调用者A再从恢复后的栈顶可弹出之前的ebp值。这样,ebp和esp就都恢复了调用函数B前的位置,也就是栈恢复函数B调用前的状态。
2)堆栈格式
=====参数n======
=====参数n-1=====
=======......======
=====参数1======
=====返回地址====== +4/+8(32位&($ebp)+4 ==>+4; 64位&($rbp)+8==>+8)
==入栈保存的bp===32位机器为ebp寄存器,64位为rbp寄存器
====被保存的寄存器====
====临时变量=======
由于优化、调用方式、编译器的不同,上述布局部可能有所不同,但一般来说,ebp前(&($ebp)+4 )是函数返回后下一条指令的地址,ebp存的是上一级函数的ebp的地址。
3)实际调用关系例子
bt.c源码
gcc-g -o bt bt.c 编译
#include <stdio.h>
int f1(int i)
{
int a1 = 0;
int b1 = 1;
return a1+b1+f2(i);
}
int f2(int i)
{
int a2 = 1;
int b2 = 2;
return a2*b2+f3(i);
}
int f3(int j)
{
int a3 = 3;
int b3 = 4;
return a3+b3+j-3;
}
int main()
{
f1(4);
return 0;
}
$ gdb bt
GNU gdb (GDB) 7.6.1
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later<http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistributeit.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "mingw32".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from d:\test\gdb\bt.exe...done.
(gdb) start
Temporary breakpoint 1 at 0x4014a6: file bt.c, line 23.
Starting program: d:\test\gdb/bt.exe
[New Thread 6468.0x1564]
Temporary breakpoint 1, main () at bt.c:23
23 f1(4);
(gdb) p $ebp
$1 = (void *) 0x28ff48
(gdb) x/xw $1+4
0x28ff4c: 0x00401250
(gdb) b f1
Breakpoint 2 at 0x401417: file bt.c, line 4.
(gdb) c
Continuing.
Breakpoint 2, f1 (i=4) at bt.c:4
4 int a1 =0;
(gdb) p $ebp
$2 = (void *) 0x28ff28
(gdb) x/xw $2+4
0x28ff2c: 0x004014b2 //f1函数调用返回后,下一条pc指令
(gdb) disas /m main
Dump of assembler code for function main:
22 {
0x00401498<+0>: push %ebp
0x00401499<+1>: mov %esp,%ebp
0x0040149b<+3>: and $0xfffffff0,%esp
0x0040149e<+6>: sub $0x10,%esp
0x004014a1<+9>: call 0x401940 <__main>
23 f1(4);
0x004014a6<+14>: movl $0x4,(%esp)
0x004014ad<+21>: call 0x401410 <f1>
24 return 0;
0x004014b2<+26>: mov $0x0,%eax
25 } 0x004014b7 <+31>: leave
0x004014b8<+32>: ret
End of assembler dump.
(gdb) b f2
Breakpoint 3 at 0x401448: file bt.c, line 10.
(gdb) c
Continuing.
Breakpoint 3, f2 (i=4) at bt.c:10
10 int a2 =1;
(gdb) p $ebp
$3 = (void *) 0x28fef8
(gdb) x/xw $3+4
0x28fefc: 0x00401439
(gdb) disas /m f1
Dump of assembler code for function f1:
3 {
0x00401410<+0>: push %ebp
0x00401411<+1>: mov %esp,%ebp
0x00401413<+3>: push %ebx
0x00401414<+4>: sub $0x24,%esp
4 int a1 =0;
0x00401417<+7>: movl $0x0,-0xc(%ebp)
5 int b1 =1;
0x0040141e<+14>: movl $0x1,-0x10(%ebp)
6 returna1+b1+f2(i);
0x00401425<+21>: mov -0xc(%ebp),%edx
0x00401428<+24>: mov -0x10(%ebp),%eax
0x0040142b<+27>: lea (%edx,%eax,1),%ebx
0x0040142e<+30>: mov 0x8(%ebp),%eax
0x00401431<+33>: mov %eax,(%esp)
0x00401434 <+36>: call 0x401441 <f2>
0x00401439<+41>: add %ebx,%eax
7 }
0x0040143b<+43>: add $0x24,%esp
0x0040143e <+46>: pop %ebx
0x0040143f<+47>: pop %ebp
0x00401440<+48>: ret
End of assembler dump.
(gdb) b f3
Breakpoint 4 at 0x401478: file bt.c, line 17.
(gdb) c
Continuing.
Breakpoint 4, f3 (j=4) at bt.c:17
17 int a3 =3;
(gdb) p $ebp
$4 = (void *) 0x28fec8
(gdb) x/xw $4+4
0x28fecc: 0x0040146a //f3函数调用返回后,下一条pc指令
(gdb) disas /m f2
Dump of assembler code for function f2:
9 {
0x00401441<+0>: push %ebp
0x00401442<+1>: mov %esp,%ebp
0x00401444 <+3>: push %ebx
0x00401445<+4>: sub $0x24,%esp
10 int a2 =1;
0x00401448<+7>: movl $0x1,-0xc(%ebp)
11 int b2 =2;
0x0040144f<+14>: movl $0x2,-0x10(%ebp)
12
13 returna2*b2+f3(i);
0x00401456<+21>: mov -0xc(%ebp),%eax
0x00401459<+24>: imul -0x10(%ebp),%eax
0x0040145d<+28>: mov %eax,%ebx
0x0040145f<+30>: mov 0x8(%ebp),%eax
0x00401462<+33>: mov %eax,(%esp)
0x00401465 <+36>: call 0x401472 <f3>
0x0040146a <+41>: add %ebx,%eax
14 }
0x0040146c<+43>: add $0x24,%esp
0x0040146f<+46>: pop %ebx
0x00401470<+47>: pop %ebp
0x00401471<+48>: ret
End of assembler dump.
(gdb) p $ebp //f3中的ebp中的值为f2中ebp寄存器的地址
$5 = (void *) 0x28fec8
(gdb) x/xw 0x28fec8
0x28fec8: 0x0028fef8 //f2中ebp的值
(gdb) x/xw 0x0028fef8 //f1中ebp的地址
0x28fef8: 0x0028ff28 //f1中ebp的值
(gdb) x/wx 0x0028ff28 //main中ebp的地址
0x28ff28: 0x0028ff48