MIT6.828-LAB1 : PC启动 (4)

  为了能够更好的了解在x86上的C程序调用过程的细节,我们首先找到在obj/kern/kern.asm中test_backtrace子程序的地址,设置断点,并且探讨一下在内核启动后,这个程序被调用时发生了什么。对于这个循环嵌套调用的程序test_backtrace,它一共压入了多少信息到堆栈之中。并且它们都代表什么含义?

好下面就开始看源码和打断点

先看c语言的代码然后再分析汇编代码

voidtest_backtrace(int x){ cprintf("entering test_backtrace %d\n", x); if (x > 0) test_backtrace(x-1); else mon_backtrace(0, 0, 0); cprintf("leaving test_backtrace %d\n", x);}

可以发现这里是一个递归调用的过程,输入x的表示调用次数

好下面切换到汇编语言。先看一下在函数执行之前的栈指针的一些信息。可以发现这里两个寄存器的值和我们在调用i386_init之前一摸一样。

image-20210620171256439

当第一次进入这个函数的时候x=5.这表示我们要执行这个代码五次

而这个代码也就是一个简单的调用。由于下一个问题就是要实现对于调用的trace函数。所以我们在下面进行讲解

4.5 Exercise 11

    实现backtrace子程序。来进行堆栈的回溯

  这个函数应该能够展示出下面这种格式的信息:

  Stack backtrace:

 ebp f0109358 eip f0100a62 args 00000001 f0109e80 f0109e98 f0100ed2 00000031

​ ebp f0109ed8 eip f01000d6 args 00000000 00000000 f0100058 f0109f28 00000061

  这个子程序的功能就是要显示当前正在执行的程序的栈帧信息。包括当前的ebp寄存器的值,这个寄存器的值代表该子程序的栈帧的最高地址。eip则指的是这个子程序执行完成之后要返回调用它的子程序时,下一个要执行的指令地址。后面的值就是这个子程序接受的来自调用它的子程序传递给它的输入参数。下面这张图对栈帧做了很好的解释

 

img

根据上图我们可以很轻松的获取我们想要的参数。

返回地址 ebp+ 4

参数1 ebp + 8

...........

所以综上所述,只要我们知道当前运行程序的ebp寄存器的值就可以,之后至于其他的我们都可以根据ebp寄存器的值推导出来。

int mon_backtrace(int argc, char **argv, struct Trapframe *tf) { // Your code here. cprintf("Start backtrace\n"); uint32_t ebp = read_ebp(); while(ebp){ uint32_t *stack_frame = (uint32_t *)(ebp); cprintf("ebp %08x eip %08x args %08x %08x %08x %08x %08x\n", ebp, /*ebp*/ stack_frame[1], /*eip*/ stack_frame[2], /*arg1*/ stack_frame[3], /*arg2*/ stack_frame[4], /*arg3*/ stack_frame[5], /*arg4*/ stack_frame[6]); /*arg5*/ ebp = stack_frame[0]; } return 0; }

这里的代码看起来非常简单。但还是需要有理解的。首先这里的ebp获取到的是一个指针。因此我们想要获得到ebp的值的话。需要通过数组访问,或者直接取值操作。同时需要理解一个非常重要的点。就是下面这几行汇编代码

f0100044: 55 push %ebp f0100045: 89 e5 mov %esp,%ebp f0100047: 56 push %esi f0100048: 53 push %ebx

这里是test_backtrace递归的开始。这里每次都先把ebp入栈。然后再把esp的值赋给ebp。也就是说下一次调用的时候。它入栈的ebp寄存器里就存储了上一次的esp指针。通过这个就可以找到每一次调用的栈帧的起始地址分别在哪里。这里如果看过csapp第三章的话,这个应该非常好理解

4.6 Exercise 12

这次我们需要修改stack backtrace函数,让它显示每一个eip, func_name, source_file_name, line_number。为了帮助实现这些功能,在kern/kdebug.c中已经实现了一个函数debuginfo_eip(),这个函数能够查找eip的符号表然后返回关于该地址的debug信息。

这里要修改我们之前的mon_backtrace函数。其实改动也不大只需要把debuginfo_eip加进去就好了

int mon_backtrace(int argc, char **argv, struct Trapframe *tf) { // Your code here. cprintf("Start backtrace\n"); uint32_t ebp = read_ebp(); struct Eipdebuginfo info; while(ebp){ uint32_t *stack_frame = (uint32_t *)(ebp); cprintf("ebp %08x eip %08x args %08x %08x %08x %08x %08x\n", ebp, /*ebp*/ stack_frame[1], /*eip*/ stack_frame[2], /*arg1*/ stack_frame[3], /*arg2*/ stack_frame[4], /*arg3*/ stack_frame[5], /*arg4*/ stack_frame[6]); /*arg5*/ uint32_t eip = stack_frame[1]; debuginfo_eip(eip,&info); cprintf(" %s:%d: %.*s+%d\n", info.eip_file, info.eip_line, info.eip_fn_namelen, info.eip_fn_name, eip - info.eip_fn_addr); ebp = stack_frame[0]; } return 0; }

这样就可以过掉所有的test了



image-20210620205514098

就可以拿到满分了over

5. 总结

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyxszw.html