MIT6.828 Lab1 Part3 The kernel Exercise 12

Exercise 12

问题1:debuginfo_eip函数中的_STAB_*来自哪里?

 打开文件kern/kernel.ld,可以看到.stab段和.stabstr段。__STAB_BEGIN__,__STAB_END__,__STABSTR_BEGIN__,__STABSTR_END__符号分别代表.stab段和.stabstr段开始与结束的地址,在kernel.ld文件中均有定义。

/* Include debugging information in kernel memory */
        .stab : {
                PROVIDE(__STAB_BEGIN__ = .);
                *(.stab);
                PROVIDE(__STAB_END__ = .);
                BYTE(0)         /* Force the linker to allocate space
                                   for this section */
        }

        .stabstr : {
                PROVIDE(__STABSTR_BEGIN__ = .);
                *(.stabstr);
                PROVIDE(__STABSTR_END__ = .);
                BYTE(0)         /* Force the linker to allocate space
                                   for this section */
        }

 通过objdump -h obj/kern/kernel指令可以查看到.stabstr段的加载内存地址。

确认boot loader在加载内核时是否把符号表也加载到内存中。怎么确认呢?使用gdb查看符号表的位置是否存储有符号信息。

查看时发现并无符号信息。猜测是由于没有设置断点?所以在gdb内核没有加载,更别提加载符号表了。因为不知道该把断点设置在哪里,先占下坑。

问题2:通过插入对stab_binsearch的调用来查找地址的行号,从而完成debuginfo_eip的实现。

问题的关键是熟悉stabs每行记录的含义。使用objdump -G obj/kern/kernel > output.md将内核的符号表信息输入到output.md文件中,在output.md文件中查看(节选):

Symnum n_type n_othr n_desc n_value  n_strx String
118    FUN    0      0      f01000a6 2987   i386_init:F(0,25)
119    SLINE  0      24     00000000 0      
120    SLINE  0      30     00000012 0      
121    SLINE  0      34     00000029 0      
122    SLINE  0      37     0000002e 0      

每个字段的含义是:

Symnum是符号索引,把整个符号表看做一个数组,Symnum是当前符号的数组下标。

n_type是符号类型,FUN指函数名(在String字段下显示函数名),SLINE值在text段中的行号。

n_othr表示目前没被使用,固定值为0

n_desc表示在文件中的行号。

n_value表示地址。需要注意的是,只有FUN符号类型的地址是绝对地址,SLINE符号的地址是偏移地址,其实际地址是函数入口地址加偏移地址,比如第120行的地址是函数入口地址0xf01000a6+偏移地址00000012=0xf01000b8,代码对应在文件的第34行。

开始代码的编写。

stab_binsearch函数的作用是查找某一个地址所在的区间,在之前的函数中已经找到了地址在哪个函数里面以及函数入口地址,将入口地址与偏移量相加得到了某行对应地址。因此打开kern/kdebug.c文件,在debuginfo_eip函数中编写代码如下得到行号:

 // Your code here.
        stab_binsearch(stabs,&lline,&rline,N_SLINE,addr);
        if(lline<=rline){
           info->eip_line = stabs[lline].n_desc;
        }else{
                cprintf("line not find\n");
        }

问题三:给内核模拟器增加backtrace命令,并在mon_backtrace中增加打印文件名、函数名和行号。

 1.给内核模拟器增加backtrace命令,直接在kern/monitor.c文件中模仿已有命令添加即可。

static struct Command commands[] = {
        { "help", "Display this list of commands", mon_help },
        { "kerninfo", "Display information about the kernel", mon_kerninfo },
        {"backtrace","Display a backtrace of the function stack",mon_backtrace},
};

2.在mon_backtrace中增加打印文件名、函数名和行号。

debuginfo_eip函数的功能是根据地址寻找函数行号。

代码如下。值得注意的是,返回的Eipdebuginfo结构体的eip_fn_name字段除了函数名外还有一段尾巴,比如test_backtrace:F(0,25),需要将":F(0,25)"去掉,可以使用printf("%.*s", length, string)来实现。

另外有一疑惑:ebp[i]是什么意思?mark一下。

int
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{
        // Your code here.
        uint32_t *ebp;
        struct Eipdebuginfo info;
        int result;
        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[i] what meaning?
        memset(&info,0,sizeof(struct Eipdebuginfo));
        result = debuginfo_eip(ebp[1],&info);
        if(result){
           cprintf("failed\r\n",ebp[1]);
        }else{
        cprintf("\t%s:%d: %.*s+%u\r\n",info.eip_file,info.eip_line,info.eip_fn_namelen,info.eip_fn_name,ebp[1]-info.eip_fn_addr);
        }
        ebp = (uint32_t*)*ebp;
        }
        return 0;
}

最后展示一下实验结果:

mon_backtrace函数的输出结果。

输入内核指令backtrace的结果。

 

Lab1的评分

到此,Lab1的实验就结束啦。

参考文章:https://www.cnblogs.com/wuhualong/p/lab01_exercise12_print_more_info.html

猜你喜欢

转载自blog.csdn.net/qq_43012789/article/details/107494408