咱們須要在lab1中完成kdebug.c中函數print_stackframe的實現,能夠經過函數print_stackframe來跟蹤函數調用堆棧中記錄的返回地址。若是可以正確實現此函數,可在lab1中執行 「make qemu」後,在qemu模擬器中獲得相似以下的輸出:html
1 ebp:0x00007b28 eip:0x00100992 args:0x00010094 0x00010094 0x00007b58 0x00100096
2 kern/debug/kdebug.c:305: print_stackframe+22
3 ebp:0x00007b38 eip:0x00100c79 args:0x00000000 0x00000000 0x00000000 0x00007ba8
4 kern/debug/kmonitor.c:125: mon_backtrace+10
5 ebp:0x00007b58 eip:0x00100096 args:0x00000000 0x00007b80 0xffff0000 0x00007b84
6 kern/init/init.c:48: grade_backtrace2+33
7 ebp:0x00007b78 eip:0x001000bf args:0x00000000 0xffff0000 0x00007ba4 0x00000029
8 kern/init/init.c:53: grade_backtrace1+38
9 ebp:0x00007b98 eip:0x001000dd args:0x00000000 0x00100000 0xffff0000 0x0000001d
10 kern/init/init.c:58: grade_backtrace0+23
11 ebp:0x00007bb8 eip:0x00100102 args:0x0010353c 0x00103520 0x00001308 0x00000000
12 kern/init/init.c:63: grade_backtrace+34
13 ebp:0x00007be8 eip:0x00100059 args:0x00000000 0x00000000 0x00000000 0x00007c53
14 kern/init/init.c:28: kern_init+88
15 ebp:0x00007bf8 eip:0x00007d73 args:0xc031fcfa 0xc08ed88e 0x64e4d08e 0xfa7502a8
16 <unknow>: -- 0x00007d72 –
請完成實驗,看看輸出是否與上述顯示大體一致,並解釋最後一行各個數值的含義。編程
提示:可閱讀小節「函數堆棧」,瞭解編譯器如何創建函數調用關係的。在完成lab1編譯後,查看lab1/obj/bootblock.asm,瞭解bootloader源碼與機器碼的語句和地址等的對應關係;查看lab1/obj/kernel.asm,瞭解 ucore OS源碼與機器碼的語句和地址等的對應關係。函數
要求完成函數kern/debug/kdebug.c::print_stackframe的實現,提交改進後源代碼包(能夠編譯執行) ,並在實驗報告中簡要說明實現過程,並寫出對上述問題的回答。ui
補充材料:
因爲顯示完整的棧結構須要解析內核文件中的調試符號,較爲複雜和繁瑣。代碼中有一些輔助函數可使用。例如能夠經過調用print_debuginfo函數完成查找對應函數名並打印至屏幕的功能。具體能夠參見kdebug.c代碼中的註釋。編碼
1.編程前,首先了解下當前狀況:在Terminal下輸入make qemu
,發現打印如下信息後就退出了:spa
1 along:~/src/ucore/labcodes/lab1$ sudo make qemu 2 WARNING: Image format was not specified for 'bin/ucore.img' and probing guessed raw. 3 Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. 4 Specify the 'raw' format explicitly to remove the restrictions. 5 (THU.CST) os is loading ... 6
7 Special kernel symbols: 8 entry 0x00100000 (phys) 9 etext 0x001036f3 (phys) 10 edata 0x0010e950 (phys) 11 end 0x0010fdc0 (phys) 12 Kernel executable memory footprint: 64KB
2.分析print_stackframe的函數調用關係debug
1 kern_init ->
2 grade_backtrace ->
3 grade_backtrace0(0, (int)kern_init, 0xffff0000) ->
4 grade_backtrace1(0, 0xffff0000) ->
5 grade_backtrace2(0, (int)&0, 0xffff0000, (int)&(0xffff0000)) ->
6 mon_backtrace(0, NULL, NULL) ->
7 print_stackframe ->
3.找到print_stackframe函數,發現函數裏面的註釋已經提供了十分詳細的步驟,基本上按照提示來作就好了。代碼以下所示。指針
1 void print_stackframe(void) { 2 uint32_t *ebp = 0; 3 uint32_t esp = 0; 4
5 ebp = (uint32_t *)read_ebp(); 6 esp = read_eip(); 7
8 while (ebp) 9 { 10 cprintf("ebp:0x%08x eip:0x%08x args:", (uint32_t)ebp, esp); 11 cprintf("0x%08x 0x%08x 0x%08x 0x%08x\n", ebp[2], ebp[3], ebp[4], ebp[5]); 12
13 print_debuginfo(esp - 1); 14
15 esp = ebp[1]; 16 ebp = (uint32_t *)*ebp; 17 } 18 /* LAB1 YOUR CODE : STEP 1 */
19 /* (1) call read_ebp() to get the value of ebp. the type is (uint32_t); 20 * (2) call read_eip() to get the value of eip. the type is (uint32_t); 21 * (3) from 0 .. STACKFRAME_DEPTH 22 * (3.1) printf value of ebp, eip 23 * (3.2) (uint32_t)calling arguments [0..4] = the contents in address (uint32_t)ebp +2 [0..4] 24 * (3.3) cprintf("\n"); 25 * (3.4) call print_debuginfo(eip-1) to print the C calling function name and line number, etc. 26 * (3.5) popup a calling stackframe 27 * NOTICE: the calling funciton's return addr eip = ss:[ebp+4] 28 * the calling funciton's ebp = ss:[ebp] 29 */
30 }
4.編碼完成後,執行make qemu
,打印結果以下所示,與實驗指導書的結果相似。調試
1 ebp:0x00007b38 eip:0x00100bf2 args:0x00010094 0x0010e950 0x00007b68 0x001000a2
2 kern/debug/kdebug.c:297: print_stackframe+48
3 ebp:0x00007b48 eip:0x00100f40 args:0x00000000 0x00000000 0x00000000 0x0010008d
4 kern/debug/kmonitor.c:125: mon_backtrace+23
5 ebp:0x00007b68 eip:0x001000a2 args:0x00000000 0x00007b90 0xffff0000 0x00007b94
6 kern/init/init.c:48: grade_backtrace2+32
7 ebp:0x00007b88 eip:0x001000d1 args:0x00000000 0xffff0000 0x00007bb4 0x001000e5
8 kern/init/init.c:53: grade_backtrace1+37
9 ebp:0x00007ba8 eip:0x001000f8 args:0x00000000 0x00100000 0xffff0000 0x00100109
10 kern/init/init.c:58: grade_backtrace0+29
11 ebp:0x00007bc8 eip:0x00100124 args:0x00000000 0x00000000 0x00000000 0x0010379c
12 kern/init/init.c:63: grade_backtrace+37
13 ebp:0x00007be8 eip:0x00100066 args:0x00000000 0x00000000 0x00000000 0x00007c4f
14 kern/init/init.c:28: kern_init+101
15 ebp:0x00007bf8 eip:0x00007d6e args:0xc031fcfa 0xc08ed88e 0x64e4d08e 0xfa7502a8
16 <unknow>: -- 0x00007d6d --
最後一行是ebp:0x00007bf8 eip:0x00007d6e args:0xc031fcfa 0xc08ed88e 0x64e4d08e 0xfa7502a8
,共有ebp,eip和args三類參數,下面分別給出解釋。rest
ebp:0x0007bf8
此時ebp的值是kern_init函數的棧頂地址,從obj/bootblock.asm文件中知道整個棧的棧頂地址爲0x00007c00,ebp指向的棧位置存放調用者的ebp寄存器的值,ebp+4指向的棧位置存放返回地址的值,這意味着kern_init函數的調用者(也就是bootmain函數)沒有傳遞任何輸入參數給它!由於單是存放舊的ebp、返回地址已經佔用8字節了。
eip:0x00007d6e
eip的值是kern_init函數的返回地址,也就是bootmain函數調用kern_init對應的指令的下一條指令的地址。這與obj/bootblock.asm是相符合的。 7d6c: ff d0 call *%eax 7d6e: ba 00 8a ff ff mov $0xffff8a00,%edx
args:0xc031fcfa 0xc08ed88e 0x64e4d08e 0xfa7502a8
通常來講,args存放的4個dword是對應4個輸入參數的值。但這裏比較特殊,因爲bootmain函數調用kern_init並沒傳遞任何輸入參數,而且棧頂的位置剛好在boot loader第一條指令存放的地址的上面,而args剛好是kern_int的ebp寄存器指向的棧頂往上第2~5個單元,所以args存放的就是boot loader指令的前16個字節!能夠對比obj/bootblock.asm文件來驗證(驗證時要注意系統是小端字節序)。
00007c00 <start>: 7c00: fa cli 7c01: fc cld 7c02: 31 c0 xor %eax,%eax 7c04: 8e d8 mov %eax,%ds 7c06: 8e c0 mov %eax,%es 7c08: 8e d0 mov %eax,%ss 7c0a: e4 64 in $0x64,%al 7c0c: a8 02 test $0x2,%al 7c0e: 75 fa jne 7c0a <seta20.1>
https://www.cnblogs.com/wuhualong/p/ucore_lab1_exercise5_report.html