打印栈内的信息,需要使用在 /lab/inc/x86.h
中的 read_ebp()
来读取 ebp 的值。
完善mon_backtrace的代码
test_backtrace
会调用 mon_backtrace
,它已经在 /lab/kern/monitor.c
中有一个原型了,完善它,并使它产生如下输出(需要使用在 /lab/inc/x86.h
中的 read_ebp()
来读取 ebp 的值):
Stack backtrace:
ebp f0109e58 eip f0100a62 args 00000001 f0109e80 f0109e98 f0100ed2 00000031
ebp f0109ed8 eip f01000d6 args 00000000 00000000 f0100058 f0109f28 00000061
...
在每一行中,ebp后面的值代表的是被这个函数所使用的ebp寄存器的值,这个值也是这个函数的栈帧的最高地址。而eip后面的值代表的是函数的返回地址。最后的五个列在args之后的16进制的值,是传递给这个函数的头五个输入参数,当然,有可能输入参数不到五个。
上面说了那么多,直接用图1来解释(有一处错误:帧指针ebp下面的地址应该是ebp-4):
被调用者的ebp指向的地址保存着调用者的ebp
被调用者的ebp的上面保存着返回地址(因为当被调用者逐渐退栈后,esp也指向图中的ebp的位置,然后通过 pop %ebp
出栈,ebp就变成指向调用者的栈帧的底部,而后esp要再+4。
ebp+8/12/16…. 保存着参数,注意这些参数都是被调用者的输入参数
因此结合博主的代码2,完善代码(博主有部分代码笔者觉得有误,欢迎讨论):
monitor.c
lab手册上写着一些GCC优化给read_ebp()带来的问题,就是说read_ebp()有可能在执行mon_backtrace的 `push %ebp; mov %esp, %ebp` 之前执行,如果是这样的话read_ebp()读取的其实是调用mon_backtrace的函数的栈帧的基地址,会出现mon_backtrace没被跟踪的情况。 另外要注意read_ebp的限定词 `static inline uint32_t read_ebp()`,尤其是inline,说明并不会单独为read_ebp产生新的栈帧。所以它读取的就是调用它的函数的栈帧的基地址,而不是它自身的栈帧,它根本没有栈帧
int
mon_backtrace(int argc, char **argv, strcut Trapframe *tf)
{
// 博主给出的是三行代码,笔者认为是错的,它实现的非inline的情况下,read_ebp具有独立的栈帧,通过read_ebp的ebp指向的地址保存着mon_backtrace的ebp的值来获取mon_backtrace的ebp
int *ebp = (int *)read_ebp(); // 使用gdb查看指令,得知读取发生在push %ebp; mov %esp, %ebp。因此读取的mon_backtrace的栈帧的基地址ebp
cprintf("Stack backtrace:\n");
//If only we haven't pass the stack frame of i386_init
while((int)ebp != 0x0) {
cprintf(" ebp %08x", (int)ebp);
cprintf(" eip %08x", *(ebp+1));
cprintf(" args");
cprintf(" %08x", *(ebp+2));
cprintf(" %08x", *(ebp+3));
cprintf(" %08x", *(ebp+4));
cprintf(" %08x", *(ebp+5));
cprintf(" %08x\n", *(ebp+6));
ebp = (int *)(*ebp); // 将ebp指向的地址内的调用者的 ebp值 赋予新的ebp
}
return 0;
}
最终运行后得到的结果为:
结合Exercise10的表格内的内容,可以验证:
- 0xf010ff18 是 mon_backtrace 的 ebp
- 0xf010ff38 是 test_backtrace(0) 的 ebp