天天看點

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

曆時2天,完成了LAB1,完整代碼倉庫可點選:https://github.com/Elio-yang/MIT6.828

exercise3

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

<code>gdb</code>指令:

<code>x/Ni addr</code> :反彙編addr處的N條指令

<code>x/Nx addr</code>:列印N位元組addr處的記憶體

<code>b *addr</code>:在addr處設定斷點

readsect(): 0x7c7c

bootmain():0x7d25

循環結束的第一條指令是0x7d81處的<code>call *0x10018</code>,利用<code>gdb</code>,<code>0x10018</code>記憶體處的值為<code>0x10000c</code>,故第一條指令是<code>call 0x10000c</code>。這個位址就是kernel的entry。

At what point does the processor start executing 32-bit code? What exactly causes the switch from 16- to 32-bit mode?

從<code>ljmp $PROT_MODE_CSEG,$protcseg</code>這條指令後開始執行32位代碼。真正造成切換的,是<code>CR0</code>的<code>PE</code>位被置為,進入了保護模式。

What is the last instruction of the boot loader executed, and what is the first instruction of the kernel it just loaded?

last:

first:

Where is the first instruction of the kernel?

很顯然在0x10000c。

How does the boot loader decide how many sectors it must read in order to fetch the entire kernel from disk? Where does it find this information?

都是通過ELF header得知的。

第一個需要注意的是代碼的連結位址和裝載位址

使用指令

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

在kernel中這兩者是不同的,但是在之前的boot中

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

二者是一緻的。在<code>kern/entry.S</code>中有這樣一段代碼

這便開啟了位址映射,在此之前kernel的VMA和LMA位址處的記憶體一般是不同的,但是開啟分頁之後,LMA映射到了VMA。

第一個值得注意的是:開啟分頁模式,将虛拟位址[0, 4MB)映射到實體位址[0, 4MB),[0xF0000000, 0xF0000000+4MB)映射到[0, 4MB)(/kern/entry.S)

分頁模式下的尋址,在Intel手冊中也有給出

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

開啟這個模式的代碼如下

關于位址的映射在<code>kern/entrypgdir.c</code>有代碼實作

編譯器配置設定的空間是強制性4kB頁對齊的。<code>pgdir</code>是一個1024項的數組。這裡可以不用詳細了解原理<code>For now, you don't have to understand the details of how this works, just the effect that it accomplishes.</code>

exercise7

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

在開啟分頁之前,這兩個位址内容是不一緻的,後面經過了位址映射,這兩者的内容一緻。注釋掉

<code>movl %eax, %cr0</code>程式會崩潰。

首先是幾個函數的調用關系

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

然後練習題

exercise8

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

這個檔案就是<code>lib/printfmt.c</code>

對照上下文很容易補全。

下面是回答問題:

Explain the interface between <code>printf.c</code> and <code>console.c</code>. Specifically, what function does <code>console.c</code> export? How is this function used by <code>printf.c</code>?

對照上文調用關系圖即可

Explain the following from console.c

首先文本模式最多能顯示<code>25*80</code>個字元,即25行每行80個。此處

是以,這一段處理的是超出一螢幕以後的做法:即舍棄最上面一行,整體上移一行。

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)
For the following questions you might wish to consult the notes for Lecture 2. These notes cover GCC's calling convention on the x86. Trace the execution of the following code step-by-step:

In the call to <code>cprintf()</code>, to what does <code>fmt</code> point? To what does <code>ap</code> point? List (in order of execution) each call to <code>cons_putc</code>, <code>va_arg</code>, and <code>vcprintf</code>. For <code>cons_putc</code>, list its argument as well. For <code>va_arg</code>, list what <code>ap</code> points to before and after the call. For <code>vcprintf</code> list the values of its two arguments.

GCC 函數調用約定是參數從右往左入棧。此處<code>fmt</code>指向的就是第一個參數的位置。而<code>ap</code>指向第一個可變參數,也就是第二個參數<code>x</code>的位置。關于變參數,JOS使用的是GCC builtin來實作的。其實作可以用如下代碼進行大緻說明(不是嚴謹的完整實作):

是以:

<code>va_list</code>:即<code>char*</code>

<code>va_start</code>:擷取第一個可變參數的位址

<code>va_arg</code>:傳回指向下一個參數的指針

<code>va_end</code>:清空參數清單

Run the following code.

What is the output? Explain how this output is arrived at in the step-by-step manner of the previous exercise. Here's an ASCII tablethat maps bytes to characters. The output depends on that fact that the x86 is little-endian. If the x86 were instead big-endian what would you set <code>i</code> to in order to yield the same output? Would you need to change <code>57616</code> to a different value? Here's a description of little- and big-endian and a more whimsical description.

把這段代碼加入<code>init.c</code>中,運作<code>make qemu</code>,結果如下

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

<code>0xe110=57616</code>這很好解釋,查閱ASCII表,得知

顯然這是由于小端模式而使用的一個數。為了證明這一點,可以輸出<code>&amp;i</code>記憶體處的位元組。将下面這段代碼放在上面列印代碼的後面

輸出結果如下:

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)
In the following code, what is going to be printed after<code>'y='</code>? (note: the answer is not a specific value.) Why does this happen?

運作結果如下

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

顯然y的值并不一定固定,他就是把記憶體中那個位置的數拿來充當了第二個參數。

Let's say that GCC changed its calling convention so that it pushed arguments on the stack in declaration order, so that the last argument is pushed last. How would you have to change <code>cprintf</code> or its interface so that it would still be possible to pass it a variable number of arguments?

更改了入棧方式,相應地更改<code>va_start</code>和<code>va_start</code>即可。

先看這個練習

exercise9

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

在<code>entry.S</code>中可以找到如下代碼

利用<code>gdb</code>得知,<code>movl $(bootstacktop),%esp</code>會被編譯為<code>movl $0xf0110000,%esp</code>。是以棧何時初始化,棧放在哪兒都清楚了。繼續看代碼

這便開辟了棧的大小,即<code>32KB</code>。棧由高位址向低位址增長。

下面,關于函數的調用過程,做一個總結,可以參考[CSAPP,p164]。

這是從課件ppt截取的兩頁

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)
MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

關于函數的調用,一般有如下的動作發生:

函數調用者(caller)将參數入棧,按照從右到左的順序入棧

call指令會自動将目前%eip(指向call的後面一條指令)入棧,ret指令将自動從棧中彈出該值到eip寄存器

被調用函數(callee)負責:将%ebp入棧,%esp的值賦給%ebp。

是以函數開頭都會是類似的兩條指令

是以整個調用鍊差不多可以描述成如下形式

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

來到下一個練習

exercise10

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

每次call之後會幹什麼,上文已經分析了。至于每次遞歸入棧的字,僞代碼可以表示為

共計<code>0x10</code>B。

exercise11

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

需要我們更改<code>mom_backtrace()</code>函數,達到的效果如下:

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

題目中已經說明,獲得<code>%ebp</code>的函數就是<code>read_ebp()</code>。那麼編碼工作應該很好完成了(利用調用鍊中<code>%ebp</code>的鍊)

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

exercise12

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

練習12的任務有三個:

搞清楚<code>__STAB_*</code>

添加指令<code>backtrace</code>

完善<code>mon_backtrace</code>

任務一

根據提示,檢視這幾個檔案,首先是<code>kernel.ld</code>。

可以知道<code>.stab</code>和<code>.stabstr</code>應該是兩個段。

接着 <code>objdump -h obj/kern/kernel</code>

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)
MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)
MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

任務二

題目中提示了需要使用<code>debuginfo_eip</code>,查找這個函數發現,他會将需要的資訊存到類型為<code>struct Eipdebuginfo</code>的結構體中。檢視該結構體定義(kern/kdebebug.h)

是以隻需要使用<code>debuginfo_eip</code>填充該結構體,再輸出資訊即可。

其中關于檔案行号的查找實作,對照上下文就能實作,注意<code>N_SLINE</code>這就是之前說<code>stab</code>時提到的一個有用的屬性。

運作結果如下:

MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)
MIT6.828——Lab1 partB(麻省理工作業系統課程實驗)

至此,<code>Lab1</code>完結。完整代碼倉庫可點選:https://github.com/Elio-yang/MIT6.828