《MIT 6.828 Homework 1: boot xv6》解題報告
本作業的網站連結:MIT 6.828 Homework 1: boot xv6
問題
Exercise: What is on the stack?
While stopped at the above breakpoint, look at the registers and the stack contents:
(gdb) info reg
...
(gdb) x/24x $esp
...
(gdb)
Write a short (3-5 word) comment next to each non-zero value on the stack explaining what it is. Which part of the stack printout is actually the stack?
Here are some questions to help you along:
- Begin by restarting qemu and gdb, and set a break-point at 0x7c00, the start of the boot block (bootasm.S). Single step through the instructions (type si at the gdb prompt). Where in bootasm.S is the stack pointer initialized? (Single step until you see an instruction that moves a value into %esp, the register for the stack pointer.)
- Single step through the call to bootmain; what is on the stack now?
- What do the first assembly instructions of bootmain do to the stack? Look for bootmain in bootblock.asm.
- Continue tracing via gdb (using breakpoints if necessary -- see hint below) and look for the call that changes eip to 0x10000c. What does that call do to the stack? (Hint: Think about what this call is trying to accomplish in the boot sequence and try to identify this point in bootmain.c, and the corresponding instruction in the bootmain code in bootblock.asm. This might help you set suitable breakpoints to speed things up.)
解答
本題目要求解釋核心啟動時棧中的資料。由於PC啟動順序是BIOS -> boot loader -> kernel,要想知道核心啟動時棧中資料的來源,需要知道前面BIOS和boot loader如何使用棧。因此,下面先解答文中提出的早期BIOS和boot loader啟動的問題,再來解釋核心啟動時棧中的資料。
問題1:棧指標的初始值是什麼?
在地址0x7c00處設定斷點,使用c命令執行至此,然後使用si命令執行一步,最後檢視暫存器資訊,結果如下所示。可知棧指標的初始值為0x6f20,並且地址0x6f20存的資料為0xf000d239.
(gdb) b *0x7c00
Breakpoint 1 at 0x7c00
(gdb) c
Continuing.
[ 0:7c00] => 0x7c00: cli
Thread 1 hit Breakpoint 1, 0x00007c00 in ?? ()
(gdb) si
[ 0:7c01] => 0x7c01: xor %ax,%ax
0x00007c01 in ?? ()
(gdb) info reg
eax 0xaa55 43605
ecx 0x0 0
edx 0x80 128
ebx 0x0 0
esp 0x6f20 0x6f20
ebp 0x0 0x0
esi 0x0 0
edi 0x0 0
eip 0x7c01 0x7c01
eflags 0x2 [ ]
cs 0x0 0
ss 0x0 0
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) x/xw 0x6f20
0x6f20: 0xf000d239
問題2:當呼叫bootmain時棧中資料是什麼?
單步執行到call bootmain
處,發現esp暫存器的值為0x7c00,也就是boot block的起始地址。當執行完call指令後,esp暫存器的值變為0x7bfc,call指令的下一條指令的地址,也是bootmain函式的返回地址。
(gdb) si
=> 0x7c43: mov $0x7c00,%esp
0x00007c43 in ?? ()
1: /x $ebp = 0x0
2: /x $esp = 0x6f20
(gdb) si
=> 0x7c48: call 0x7d3b
0x00007c48 in ?? ()
1: /x $ebp = 0x0
2: /x $esp = 0x7c00
(gdb) si
=> 0x7d3b: push %ebp
Thread 1 hit Breakpoint 2, 0x00007d3b in ?? ()
1: /x $ebp = 0x0
2: /x $esp = 0x7bfc
問題3:bootmain的第一條指令做了什麼?
從bootblock.asm檔案可以看到bootmain的第一條指令將ebp暫存器的值壓棧。
7d3b: 55 push %ebp
這導致esp暫存器的值減4,由0x7bfc變為0x7bf8,而棧頂儲存的元素也就是ebp暫存器的值,亦即為0.
問題4:那個修改eip的值為0x10000c的call語句對棧做了什麼?
修改eip的值為0x10000c的語句是call *0x10018
,其中地址0x10018處儲存的內容為0x10000c,所以此語句做的事情是:先將返回地址0x7d8d壓棧,然後跳到0x10000c的位置。
問題5:解釋核心啟動時棧中的資料
按照題目要求,執行gdb後,在地址0x0010000c處設定斷點。然後
- 檢視暫存器資訊
(gdb) info reg
eax 0x0 0
ecx 0x0 0
edx 0x1f0 496
ebx 0x10074 65652
esp 0x7bdc 0x7bdc
ebp 0x7bf8 0x7bf8
esi 0x10074 65652
edi 0x0 0
eip 0x10000c 0x10000c
eflags 0x46 [ PF ZF ]
cs 0x8 8
ss 0x10 16
ds 0x10 16
es 0x10 16
fs 0x0 0
gs 0x0 0
- 檢視棧中資料
(gdb) x/24x $esp
0x7bdc: 0x00007d8d 0x00000000 0x00000000 0x00000000
0x7bec: 0x00000000 0x00000000 0x00000000 0x00000000
0x7bfc: 0x00007c4d 0x8ec031fa 0x8ec08ed8 0xa864e4d0
0x7c0c: 0xb0fa7502 0xe464e6d1 0x7502a864 0xe6dfb0fa
0x7c1c: 0x16010f60 0x200f7c78 0xc88366c0 0xc0220f01
0x7c2c: 0x087c31ea 0x10b86600 0x8ed88e00 0x66d08ec0
- 解釋棧中資料
注意,棧實際上從0x7c00向下增長,大於0x7c00的地址儲存的是BIOS和boot loader的程式碼。
0x7bdc: 0x00007d8d // function return address after calling kernel
0x7be0: 0x00000000 // reserved value
0x7be4: 0x00000000 // reserved value
0x7be8: 0x00000000 // reserved value
0x7bec: 0x00000000 // reserved value
0x7bf0: 0x00000000 // ebx's value when calling bootmain
0x7bf4: 0x00000000 // esi's value when calling bootmain
0x7bf8: 0x00000000 // edi's value when calling bootmain
0x7bfc: 0x00007c4d // function return address after calling bootmain
0x7c00: 0x8ec031fa // cli (The following is instructions of bootblock)
0x7c04: 0x8ec08ed8
0x7c08: 0xa864e4d0
0x7c0c: 0xb0fa7502
0x7c10: 0xe464e6d1
0x7c14: 0x7502a864
0x7c18: 0xe6dfb0fa
0x7c1c: 0x16010f60
0x7c20: 0x200f7c78
0x7c24: 0xc88366c0
0x7c28: 0xc0220f01
0x7c2c: 0x087c31ea
0x7c30: 0x10b86600
0x7c34: 0x8ed88e00
0x7c38: 0x66d08ec0
備註
- 第一次執行gdb時,沒能執行到.gdbinit檔案,有以下列印資訊:
warning: File "/home/along/src/6.828/src/xv6-public/.gdbinit" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
add-auto-load-safe-path /home/along/src/6.828/src/xv6-public/.gdbinit
line to your configuration file "/home/along/.gdbinit".
按照提示,在/home/along/.gdbinit檔案中增加以上語句後,再執行gdb就正常了。