好难啊,去年做的时候就很难,难到直接不知道怎么出来的,现在能搞出来了。。。。。。

Exercise 12. Modify your stack backtrace function to display, for each eip, the function name, source file name, and line number corresponding to that eip.In debuginfo_eip, where do __STAB_* come from? This question has a long answer; to help you to discover the answer, here are some things you might want to do:look in the file kern/kernel.ld for __STAB_*
run objdump -h obj/kern/kernel
run objdump -G obj/kern/kernel
run gcc -pipe -nostdinc -O2 -fno-builtin -I. -MD -Wall -Wno-format -DJOS_KERNEL -gstabs -c -S kern/init.c, and look at init.s.
see if the bootloader loads the symbol table in memory as part of loading the kernel binary
Complete the implementation of debuginfo_eip by inserting the call to stab_binsearch to find the line number for an address.Add a backtrace command to the kernel monitor, and extend your implementation of mon_backtrace to call debuginfo_eip and print a line for each stack frame of the form:K> backtrace
Stack backtrace:ebp f010ff78  eip f01008ae  args 00000001 f010ff8c 00000000 f0110580 00000000kern/monitor.c:143: monitor+106ebp f010ffd8  eip f0100193  args 00000000 00001aac 00000660 00000000 00000000kern/init.c:49: i386_init+59ebp f010fff8  eip f010003d  args 00000000 00000000 0000ffff 10cf9a00 0000ffffkern/entry.S:70: <unknown>+0
K>
Each line gives the file name and line within that file of the stack frame's eip, followed by the name of the function and the offset of the eip from the first instruction of the function (e.g., monitor+106 means the return eip is 106 bytes past the beginning of monitor).Be sure to print the file and function names on a separate line, to avoid confusing the grading script.Tip: printf format strings provide an easy, albeit obscure, way to print non-null-terminated strings like those in STABS tables. printf("%.*s", length, string) prints at most length characters of string. Take a look at the printf man page to find out why this works.You may find that some functions are missing from the backtrace. For example, you will probably see a call to monitor() but not to runcmd(). This is because the compiler in-lines some function calls. Other optimizations may cause you to see unexpected line numbers. If you get rid of the -O2 from GNUMakefile, the backtraces may make more sense (but your kernel will run more slowly).

要完成好几个任务

  1. 找出__STAB_*都来自哪里
  2. 确定bootloader是否将符号表作为加载内核的一部分加载到内存里了
  3. 补全debuginfo_eip函数的功能,让这个函数能查找行号
  4. 补全mon_backtrace函数的功能,使得其能够得到像上面那样的输出

1. 找出__STAB_*都来自哪里

这个比较容易,按照要求中的提示打开kern/kernel.ld,包含__STAB_*的部分如下

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

可以看到这个begin和end分别是.stab段的开始和结束。

ld文件是链接器脚本文件,链接时就按照这个脚本文件将目标文件和库链接到一起。它的语法见链接脚本文件ld语法

2. 确定bootloader是否将符号表作为加载内核的一部分加载到内存里了

这个我没想出来,看别人的答案是使用objdump -h找到stabstr段的地址,gdb调试时直接用x命令打印stabstr位置处有没有信息,如果有信息,就是加载成功了。

3. 补全debuginfo_eip函数的功能,让这个函数能查找行号

一开始也是始终没想明白,后来使用

objdump -G obj/kern/kernel # -G, --stabs Display (in raw form) any STABS info in the file

打印出来所有的调试信息,看了很多遍,才明白过来。怀疑-G是综合把.stab和.stabstr的文件一起输出的,所以才会包含字符串什么的。

截取一小部分。

yichuan@ubuntu:~/6.828/lab$ objdump -G obj/kern/kernelobj/kern/kernel:     file format elf32-i386Contents of .stab section:Symnum n_type n_othr n_desc n_value  n_strx String-1     HdrSym 0      1241   000018fc 1
0      SO     0      0      f0100000 1      {standard input}
1      SOL    0      0      f010000c 18     kern/entry.S
2      SLINE  0      44     f010000c 0
3      SLINE  0      57     f0100015 0
4      SLINE  0      58     f010001a 0
5      SLINE  0      60     f010001d 0
6      SLINE  0      61     f0100020 0
7      SLINE  0      62     f0100025 0
8      SLINE  0      67     f0100028 0
9      SLINE  0      68     f010002d 0
10     SLINE  0      74     f010002f 0
11     SLINE  0      77     f0100034 0
12     SLINE  0      80     f0100039 0
13     SLINE  0      83     f010003e 0
14     SO     0      2      f0100040 31     kern/entrypgdir.c
15     OPT    0      0      00000000 49     gcc2_compiled.

每个条目有7个参数

  • symnum:条目序号
  • n_type:条目类型,各个缩写的含义不知道是啥。
  • n_othr:一直都为空,不知为何
  • n_desc:后来才知道这是符号在文件中的行号
  • n_value:符号的地址。FUN类型的地址是绝对地址,而对其他类型,如果前面有出现过FUN,那么SLINE指的就是这个函数内部的偏移地址,是相对地址。例子中的SLINE类型等前面都没出现过FUN,所以都是绝对地址。
  • n_strx:字符串表的索引,指的是这个符号名字首字母在字符串表中的索引位置。比如{standard input}的n_strx为1,因为其首字母位置1,结尾是17,可能有一个终止符,所以kern/entry.S的n_strx为18。后面同理。
  • String:即符号的字符串名。

debuginfo_eip函数使用了结构体Eipdebuginfo,定义在kdebug.h

int
debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info)

查看该结构体定义

struct Eipdebuginfo {const char *eip_file;       // Source code filename for EIPint eip_line;            // Source code linenumber for EIPconst char *eip_fn_name;   // Name of function containing EIP//  - Note: not null terminated!int eip_fn_namelen;       // Length of function nameuintptr_t eip_fn_addr;        // Address of start of functionint eip_fn_narg;     // Number of function arguments
};

可以看到由当前eip能够得到这么多调试信息。其中有些已实现,而得到行号要我们来完成。

按照kdebug.c的写法,查找各个变量的含义,结合上下文,最后得到如下写法。

 stab_binsearch(stabs, &lline, &rline, N_SLINE, addr);if (lline <= rline)    {info->eip_line = stabs[lline].n_desc;  }else return -1;

kdebug.c的注释里面提示已经很详细了。n_desc就是我们要的行号。

4. 补全mon_backtrace函数的功能,使得其能够得到像题目中那样的输出

有了kdebug的补全提示,这个写起来就很容易了。

int
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{uint32_t *ebp;struct Eipdebuginfo tinfo;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]);int res = debuginfo_eip(ebp[1], &tinfo);    //因为原型里指定的是指针,所以此处取地址if(res != 0)   cprintf("Error!\n");else {cprintf("\t\t%s:%d: %.*s+%u\n", tinfo.eip_file, tinfo.eip_line, tinfo.eip_fn_namelen, tinfo.eip_fn_name, ebp[1] - tinfo.eip_fn_addr);}ebp = (uint32_t *)*ebp;}return 0;
}

由于debuginfo_eip写的是如果正常运行就返回0,如果出错就返回负数,所以有这个条件判断。其他信息都已经在Eipdebuginfo结构体中包含,上面已说过。

注意输出格式符 %.*s,表示这个位置要对应两个变量,第一个是一个整型数,表示最大字符串位数,第二个是字符串。如果字符串长度超过位数限制,就只输出位数规定的字符数。

最后使用了减法,ebp[1] - tinfo.eip_fn_addr,因为这个位置输出的是在文件中的行号,所以要用当前的eip指向的位置减去所在函数的起始地址才可以。

为了得到运行结果看是否正确,要在适当的位置加断点。

之前的kernel.asm

 test_backtrace(5);
f01000c8:   c7 04 24 05 00 00 00    movl   $0x5,(%esp)
f01000cf:   e8 6c ff ff ff          call   f0100040 <test_backtrace>
f01000d4:   83 c4 10                add    $0x10,%esp// Drop into the kernel monitor.while (1)monitor(NULL);
f01000d7:   83 ec 0c                sub    $0xc,%esp
f01000da:   6a 00                   push   $0x0
f01000dc:   e8 37 07 00 00          call   f0100818 <monitor>

我们写的函数被test_backtrace调用,运行结果(就是cprintf语句)会表现出正确性,所以尝试在0xf01000d7处加断点。

运行结果如下,可以使用make grade检测一下看看能得多少分。

yichuan@ubuntu:~/6.828/lab$ make grade
make clean
make[1]: Entering directory '/home/yichuan/6.828/lab'
rm -rf obj .gdbinit jos.in qemu.log
make[1]: Leaving directory '/home/yichuan/6.828/lab'
./grade-lab1
make[1]: Entering directory '/home/yichuan/6.828/lab'
+ as kern/entry.S
+ cc kern/entrypgdir.c
sh: echo: I/O error
+ cc kern/init.c
+ cc kern/console.c
+ cc kern/monitor.c
+ cc kern/printf.c
+ cc kern/kdebug.c
+ cc lib/printfmt.c
+ cc lib/readline.c
+ cc lib/string.c
sh: echo: I/O error
+ ld obj/kern/kernel
ld: warning: section `.bss' type changed to PROGBITS
+ as boot/boot.S
+ cc -Os boot/main.c
+ ld boot/boot
boot block is 390 bytes (max 510)
+ mk obj/kern/kernel.img
make[1]: Leaving directory '/home/yichuan/6.828/lab'
running JOS: (0.6s) printf: FAIL AssertionError: ...leaving test_backtrace 4leaving test_backtrace 5Welcome to the JOS kernel monitor!Type 'help' for a list of commands.qemu: terminating on signal 15 from pid 21790MISSING '6828 decimal is 15254 octal!'backtrace count: OK backtrace arguments: OK backtrace symbols: OK backtrace lines: OK
Score: 30/50
GNUmakefile:200: recipe for target 'grade' failed
make: *** [grade] Error 1

mit 6.828 lab1_exercise12_讲解相关推荐

  1. MIT 6.828 lab1 part2

    Part 2: The Boot Loader 加载内核 为了理解boot/main.c,你需要知道ELF二进制文件是什么.当你编译和链接一个C程序(如JOS内核)时,编译器将每个C源文件('. C ...

  2. 《MIT 6.828 Lab 1 Exercise 10》实验报告

    本实验的网站链接:MIT 6.828 Lab 1 Exercise 10. 题目 Exercise 10. To become familiar with the C calling conventi ...

  3. MIT 6.828 操作系统工程 lab4A:多处理器支持和协作多任务

    MIT 6.828 操作系统工程 lab4A:多处理器支持和协作多任务 这篇是我自己探索实现 MIT 6.828 lab 的笔记记录,会包含一部分代码注释和要求的翻译记录,以及踩过的坑/个人的解决方案 ...

  4. 2022-2-16 MIT 6.828 Lab1:Booting a PC part1-part2

    Part 1: PC Bootstrap Getting Started with x86 assembly Exercise 1. Familiarize yourself with the ass ...

  5. 手机APP开发之MIT Appinventor详细实战教程(九),工具箱的设计和MIT的基础知识讲解

    (一)APP功能介绍 这次我给大家介绍一个关于工具箱的App ,这个APP在逻辑方面较为简单.但是他的设计过程中包含了很多相关的知识,通过这篇文章,可以让大家很具体有直观的了解到这个编程软件的使用方法 ...

  6. 2022-2-16 MIT 6.828 实验环境安装

    前期参考:MIT6.828 实验环境安装教程 但是中途遇到了一个问题: 参考这个解决 最终的成果

  7. 资源向导之 JOS 计划 MIT 6.828

    Project of JOS update: 2016.03.18 哇,JOS过去一段时间了,有一些同学可能获得JOS实验初始的源代码比较困难,原因可能是GFW也可能是JOS官方他们每学期都可能会课程 ...

  8. MIT 6.828 学习笔记2 阅读main.c

    #include <inc/x86.h> #include <inc/elf.h>/********************************************** ...

  9. MIT 6.828 JOS学习笔记12 Exercise 1.9

    Lab 1中Exercise 9的解答报告 Exercise 1.9: 判断一下操作系统内核是从哪条指令开始初始化它的堆栈空间的,以及这个堆栈坐落在内存的哪个地方?内核是如何给它的堆栈保留一块内存空间 ...

最新文章

  1. LOJ2586 APIO2018 选圆圈
  2. php.ini 配置详解
  3. 【解析】案例4-1.5 顺序存储的二叉树的最近公共祖先问题
  4. qpython3编辑器怎么用_Python快速入门系列:Pyqt5界面开发好帮手-Qss样式编辑器介绍...
  5. 基于高斯分布的异常检测代码实现
  6. ShadeGraph教程之节点详解3:Input Nodes
  7. 腾讯云 mysql 密码_腾讯云mysql重新设置密码解决办法
  8. 前方迷茫,我欲借何过大江
  9. LAMP架构调优(四)——资源压缩传输
  10. 为什么我只贴代码不给你们源码?
  11. C++ 模拟鼠标键盘操作
  12. Android编码架构MVx演进历史
  13. 双11快速拉新促活,容联云智能客服助力商家提升GMV
  14. cpu 指锟筋集 linux锟介看 shell,Linux Shell中PS命令中的%CPU的含义介绍
  15. Gamemaker studio2经验(2)——TCP联机
  16. 【观察】首款7nm芯片服务器亮相,联想驱动数据中心再创新
  17. HP ProBook 4421s配置完成无线网卡
  18. 全新的windows7论坛之家
  19. 淘宝客网站推广(一)
  20. 云主机供应商防火墙导致的TCP连接无法建立问题

热门文章

  1. 自制u盘偷猎者源码分享
  2. 多人在线斗地主游戏开发——自定义TCP网络通信协议包格式
  3. chrome的护眼插件
  4. 【网络技术联盟站】网络工程师深入篇之网络基础知识
  5. 计算机在矿山企业中的应用,计算机在矿山中的应用.doc
  6. pycharm执行文件时报错can't find '__main__' module解决方法
  7. Janus源码分析(3)——请求处理过程
  8. HTML继承和元素类型转换
  9. 【c++】拼音转数字
  10. 开源多云技术平台——Choerodon猪齿鱼发布0.19版本