lab1-exercise11
在这个exercise中,我们需要完成一个mon_backtrace()
函数(在kern/monitor.c
中),
这个函数的功能就是把当前栈里面的所有栈帧按照规定的格式输出出来,格式如下:
经过前面exercise的洗礼,我们对于栈中的栈帧应该有了足够的了解,为了直观的展示栈帧的结构,上图:
从图上可以直观的看出:
- esp和ebp共同划分了一个栈帧的上下边界,其中,ebp指针指向的地址中存着的是父函数的ebp指针,因此我们只要知道栈顶的栈帧的ebp就能逐层推导出所有函数的edp地址了
- 在当前栈帧的ebp的上面,也就是(ebp+4)的地址处保存的是父函数的返回地址,也就是eip寄存器的值,再往上的五个格子就是父函数需要传递给子函数的参数,但是这五个参数不一定都有用,需要被子函数使用的参数依次放在参数1,参数2,参数3…的地址处
all right,我们已经分析完了我们需要做的事情,但是目前还存在几个问题
- 如何获取当前函数的ebp的值,讲义里面提到,我们可以使用
icn/x86.h
中的read_ebp()
函数,这个函数可以直接返回当前栈顶函数的ebp值。 - 我们通过不断向上回溯获取每个函数栈帧的ebp,但是万事万物都是有尽头的,我们到什么时候终止回溯呢?讲义让我们钻研
kern/entry.S
,注意到,在kern/entry.S
中有这样一段:
可以看到,这段代码在栈区建立之前,把ebp初始化为0x0,“Salvation lies within”。这 就是我们需要的答案!
万事俱备,我们可以开始code了
先写下第一行关键性的代码uint32_t ebp = read_ebp();
int backtrace(){
uint32_t ebp = read_ebp();
return 0;
}
但是问题又来了,read_ebp()
函数返回的是一个16进制的地址值,类似于0xf01000fd
,我们如何通过这一个地址来对内存中的数据进行访问呢?
是的,可以定义一个同类型的指针变量,将该地址赋值给该指针变量,这样,我们就可以通过这个指针变量进行非常方便的寻址操作了。
写下第二行关键性代码uint32_t * temp = (uint32_t *) ebp;
,注意先进行类型的转换再赋值
int backtrace(){
uint32_t ebp = read_ebp();
uint32_t * temp = (uint32_t *) ebp;
return 0;
}
接下来就是按照前述的逻辑编写代码了,写下第三行关键性代码—终止条件while(* temp !=0)
int backtrace(){
uint32_t ebp = read_ebp();
uint32_t * temp = (uint32_t *) ebp;
cprintf("Stack backtrace:\n");
while(* temp !=0){}
return 0;
}
继续完成代码的编写,注意%08x
可以输出8个字节的十六进制数
int backtrace(){
uint32_t ebp = read_ebp();
uint32_t * temp = (uint32_t *) ebp;
cprintf("Stack backtrace:\n");
while(* temp !=0){
cprintf(" ebp %08x eip %08x args %08x %08x %08x %08x %08x\n",temp,*(temp+1),*(temp+2),*(temp+3),*(temp+4),*(temp+5),*(temp+6));
temp = (uint32_t *)(*temp);
}
return 0;
}
好了,为了测验一下自己的代码是否按照预期的方式输出数据,可以运行内核程序,查看输出,输出如下:
看上去很完美了,可以开始make grade
了,“让我试试刀吧”!
????为什么只有30分?
backtrace symbols
和backtrace lines
是exercise12的内容,不予关注,所以我们应该是backtrace count
出错了,图中显示需要8个栈帧,而我们只有7个,少了一个,少了哪个呢?
我们回到之前输出的内容中:
由上往下,依次是函数一个mon_backtrace()
,六个test_backtrace()
,哦吼,居然少了最开始的init()
函数,这个函数是最早被压入栈的,为什么没有被输出呢?可以想到是终止条件出了问题,回顾一下代码,哦!由于疏忽大意,导致while()
函数被提前一轮终止了:
太马虎了,反思一会。
ok,把代码更正之后如下:
int backtrace(){
uint32_t ebp = read_ebp();
uint32_t * temp = (uint32_t *) ebp;
cprintf("Stack backtrace:\n");
while( temp != 0 ){
cprintf(" ebp %08x eip %08x args %08x %08x %08x %08x %08x\n",temp,*(temp+1),*(temp+2),*(temp+3),*(temp+4),*(temp+5),*(temp+6));
temp = (uint32_t *)(*temp);
}
return 0;
}
make grade
40分,顺利通关!
现在只剩exercise12没做了,胜利就在前方,冲啊!