test_backtrace反汇编后长这样
test_backtrace(int x)
{
f0100040: 55 push %ebp
f0100041: 89 e5 mov %esp,%ebp
f0100043: 53 push %ebx
f0100044: 83 ec 0c sub $0xc,%esp
f0100047: 8b 5d 08 mov 0x8(%ebp),%ebx
cprintf("entering test_backtrace %d\n", x);
f010004a: 53 push %ebx
f010004b: 68 c0 18 10 f0 push $0xf01018c0
f0100050: e8 e1 08 00 00 call f0100936 <cprintf>
if (x > 0)
f0100055: 83 c4 10 add $0x10,%esp
f0100058: 85 db test %ebx,%ebx
f010005a: 7e 11 jle f010006d <test_backtrace+0x2d>
test_backtrace(x-1);
f010005c: 83 ec 0c sub $0xc,%esp
f010005f: 8d 43 ff lea -0x1(%ebx),%eax
f0100062: 50 push %eax
f0100063: e8 d8 ff ff ff call f0100040 <test_backtrace>
f0100068: 83 c4 10 add $0x10,%esp
f010006b: eb 11 jmp f010007e <test_backtrace+0x3e>
else
mon_backtrace(0, 0, 0);
f010006d: 83 ec 04 sub $0x4,%esp
f0100070: 6a 00 push $0x0
f0100072: 6a 00 push $0x0
f0100074: 6a 00 push $0x0
f0100076: e8 f3 06 00 00 call f010076e <mon_backtrace>
f010007b: 83 c4 10 add $0x10,%esp
cprintf("leaving test_backtrace %d\n", x);
f010007e: 83 ec 08 sub $0x8,%esp
f0100081: 53 push %ebx
f0100082: 68 dc 18 10 f0 push $0xf01018dc
f0100087: e8 aa 08 00 00 call f0100936 <cprintf>
}
f010008c: 83 c4 10 add $0x10,%esp
f010008f: 8b 5d fc mov -0x4(%ebp),%ebx
f0100092: c9 leave
f0100093: c3 ret
下面是gdb调试过程,breakpoint 4 之前的是test_backtrace(4),之后的是调用test_backtrace(3)的测试过程。
(gdb) info registers ebp
ebp 0xf010ffb8 0xf010ffb8
(gdb) x/1x 0xf010ffb8
0xf010ffb8: 0xf010ffd8
(gdb) x/8x 0xf010ffd8
0xf010ffd8: 0xf010fff8 0xf01000d4 0x00000005 0x00001aac
0xf010ffe8: 0x00000640 0x00000000 0x00000000 0x00000000
(gdb) x/8x 0xf010ffb8
0xf010ffb8: 0xf010ffd8 0xf0100068 0x00000004 0x00000005
0xf010ffc8: 0x00000000 0x00010094 0x00010094 0x00010094
(gdb) info registers ebp
ebp 0xf010ffb8 0xf010ffb8
(gdb) si
=> 0xf0100063 <test_backtrace+35>: call 0xf0100040 <test_backtrace>
0xf0100063 16 test_backtrace(x-1);
(gdb) x/8x ($ebp)
0xf010ffb8: 0xf010ffd8 0xf0100068 0x00000004 0x00000005
0xf010ffc8: 0x00000000 0x00010094 0x00010094 0x00010094
(gdb) x/8x 0xf010fff8
0xf010fff8: 0x00000000 0xf010003e 0x00111021 0x00000000
0xf0110008 <entry_pgdir+8>: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) p $esp
$6 = (void *) 0xf010ffa0
(gdb) si
=> 0xf0100040 <test_backtrace>: push %ebp
test_backtrace (x=3) at kern/init.c:13
13 {
(gdb) b *0xf0100063
Breakpoint 4 at 0xf0100063: file kern/init.c, line 16.
(gdb) c
Continuing.
=> 0xf0100050 <test_backtrace+16>: call 0xf0100936 <cprintf>
Breakpoint 1, 0xf0100050 in test_backtrace (x=3) at kern/init.c:14
14 cprintf("entering test_backtrace %d\n", x);
(gdb) c
Continuing.
=> 0xf0100055 <test_backtrace+21>: add $0x10,%esp
Breakpoint 3, test_backtrace (x=3) at kern/init.c:15
15 if (x > 0)
(gdb) c
Continuing.
=> 0xf010005a <test_backtrace+26>: jle 0xf010006d <test_backtrace+45>
Breakpoint 2, 0xf010005a in test_backtrace (x=3) at kern/init.c:15
15 if (x > 0)
(gdb) c
Continuing.
=> 0xf0100063 <test_backtrace+35>: call 0xf0100040 <test_backtrace>
Breakpoint 4, 0xf0100063 in test_backtrace (x=3) at kern/init.c:16
16 test_backtrace(x-1);
(gdb) info registers ebp
ebp 0xf010ff98 0xf010ff98
(gdb) x/8x ($ebp)
0xf010ff98: 0xf010ffb8 0xf0100068 0x00000003 0x00000004
0xf010ffa8: 0x00000000 0x00000000 0x00000000 0x00000005
(gdb) p $eax
$7 = 2
(gdb) p $eip
$8 = (void (*)()) 0xf0100063 <test_backtrace+35>
(gdb) p $esp
$9 = (void *) 0xf010ff80
(gdb) x/16x ($esp)
0xf010ff80: 0x00000002 0x00000003 0xf010ffb8 0x00000000
0xf010ff90: 0xf01008fd 0x00000004 0xf010ffb8 0xf0100068
0xf010ffa0: 0x00000003 0x00000004 0x00000000 0x00000000
0xf010ffb0: 0x00000000 0x00000005 0xf010ffd8 0xf0100068
(gdb) x/20x ($esp)
0xf010ff80: 0x00000002 0x00000003 0xf010ffb8 0x00000000
0xf010ff90: 0xf01008fd 0x00000004 0xf010ffb8 0xf0100068
0xf010ffa0: 0x00000003 0x00000004 0x00000000 0x00000000
0xf010ffb0: 0x00000000 0x00000005 0xf010ffd8 0xf0100068
0xf010ffc0: 0x00000004 0x00000005 0x00000000 0x00010094
在递归到同一个位置时,观察了两次栈。发现,
test_backtrace(4)时 ebp 0xf010ffb8,*(ebp) 0xf010ffd8,
test_backtrace(3)时 ebp 0xf010ff98,*(ebp) 0xf010ffb8,
两次调用下一层递归前,ebp都指向上一层的ebp,而且*(ebp)[0] 就相当于上一层的ebp,从*(ebp)[1]开始才是eip、args啥的。
*(ebp)[1]是ret返回后的eip,其实就是call指令下的那条语句。这里就是
f0100068: 83 c4 10 add $0x10,%esp
mon_backtrace这样写
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{
uint32_t *ebp;
ebp = (uint32_t *)read_ebp();
cprintf("Stack backtrace:\r\n");
while (ebp)
{
cprintf(" ebp %08x eip %08x args %08x %08x %08x %08x %08x\r\n",
ebp, ebp[1], ebp[2], ebp[3], ebp[4], ebp[5], ebp[6]);
ebp = (uint32_t *)*ebp;
}
return 0;
}
因为我们要打印的是test_backtrace的参数和eip什么的,而不是mon_backtrace的,而且test调用mon,所以必须要对指针取一次值,才能访问到它的上层函数。