MIT 6.828 JOS學習筆記7. Lab 1 Part 2.2: The Boot Loader


Lab 1 Part 2 The Boot Loader

Loading the Kernel

我們現在可以進一步的討論一下boot loader中的C語言的部分,即boot/main.c。但是在我們分析之前,我們應該先回顧一些關於C語言的基礎知識。


 Exercise 4:

  閱讀關於C語言的指針部分的知識。最好的參考書自然是"The C Programming Language"。

  閱讀5.1到5.5節。然后下載pointers.c的代碼,並且編譯運行它,確保你理解在屏幕上打印出來的所有的值是怎么來的。尤其要重點理解第1行,第6行的指針地址是如何得到的,以及在第2行到第4行的值是如何得到的,還有為什么在第5行打印出來的值看起來像程序崩潰了。

  Waring:除非你已經對C語言相當了解,否則千萬不要跳過這一個練習。

    這個練習的解答連接:http://www.cnblogs.com/fatsheep9146/p/5216735.html


 

  為了能夠理解boot/main.c程序,你必須首先清楚什么是ELF文件。當你在編譯並且鏈接了像JOS內核這樣的C語言程序之后,編譯器會把C語言源文件(.c后綴)轉換為目標文件(.o后綴)。目標文件中包含的是機器直接能夠執行的機器指令。鏈接器在把所有的目標文件組合成一個單獨的二進制映像(binary image),比如obj/kern/kernel。這類二進制映像文件就是ELF格式的。

  在6.828中,你可以認為一個可以執行的ELF文件是由三大部分組成:一個是帶有加載信息的文件頭,然后緊跟着程序段表,然后緊跟着的就是幾個程序段(program section)。其中每一個段都是一塊連續的代碼或者數據。它們在被運行時要首先被加載到內存中。boot loader的工作就是把它們加載到內存中。

  一個ELF文件,開始處是一個固定長度的ELF文件頭,后面緊跟着一個程序段表,這個段表中列出了要加載到內存中的所有段。關於ELF文件頭的格式在inc/elf.h文件中有聲明。在6.828中我們對三個段非常感興趣:

  * .text段:存放所有程序的可執行代碼

  * .rodata段:存放所有只讀數據的數據段,比如字符串常量。

  * .data段:存放所有被初始化過的數據段,比如有初始值的全局變量。

  當鏈接器在計算整個程序的內存布局時,它會為沒有被初始化過的變量,比如int x;,在一個緊跟在.data段后的段,.bss段中保留它們的信息。C語言要求所有沒有被初始化的變量值都為0。因而我們並不需要在ELF文件中存放這些變量的值,因為一定是0。因此鏈接器只是把這些變量的地址和大小存放在.bss段中。只有當程序裝入內存后,由裝入器為這些段賦予初值0。

  你可以通過下面的指令來考察JOS內核中所有段的名字,大小和地址。

     objdump -h obj/kern/kernel

  得到的結果如下圖:

  

  在圖中我們會發現這個可執行文件的所有段的信息,其中不僅僅包括我們之前提到的那四個段,還有一些其他的,他們主要用於存放一些debug信息等等。

  在每一個段中都有兩個比較重要的字段,VMA(鏈接地址),LMA(加載地址)。其中加載地址代表的就是這個段被加載到內存中后,它所在的物理地址。鏈接地址則指的是這個段希望被存放到的邏輯地址。

  每一個ELF文件中都有一個Program Headers Table,用於指明ELF文件中哪些部分被加載到內存,以及被加載到內存中的地址。你可以通過輸入下述指令來獲取kernel的Program Headers Table的信息:

    objdump -x obj/kern/kernel

  

  其中Program Header中列出的是所有被加載到內存中的段的信息,這也是Program Headers Table的表項。每一個表項圖中都把這個表項中涉及到的所有字段都列出來了。可見有一些段最后沒有被加入到內存之中。在上圖中,那些需要被加載到內存的段被標記為LOAD。    

  BIOS通常會把boot sector加載到內存地址0x7c00處,這是boot sector的加載地址,也是boot sector的鏈接地址。我們可以通過boot/Makefrag文件中的-Ttext 0x7c00語句設置boot sector的鏈接地址,並且這個鏈接地址后來會被鏈接器所使用,保證鏈接器產生正確的代碼。


 

  Exercise 5

    再一次追蹤一下boot loader的一開始的幾句指令,找到第一條滿足如下條件的指令處:

    當我修改了boot loader的鏈接地址,這個指令就會出現錯誤。

    找到這樣的指令后,把boot loader的鏈接地址修改一下,我們要在boot/Makefrag文件中修改它的鏈接地址,修改完成后運行 make clean, 然后通過make指令重新編譯內核,再找到那條指令看看會發生什么。最后別忘了改回來。

  練習解答連接:http://www.cnblogs.com/fatsheep9146/p/5220004.html


 

  再讓我們回顧一下內核的加載地址和鏈接地址。和boot loader不同,內核的這兩個地址是不同的。內核告訴boot loader把它加載到低地址處(加載地址),但是它希望運行在高地址處(鏈接地址)。我們將在下一章仔細看這個問題。

  除了各個段的信息,在ELF頭部中,還有一個非常重要的信息就是e_entry字段。這個字段存放的是這個可執行程序的執行入口處的鏈接地址。通過下面的指令你可以查看內核程序入口處。

  

   可見程序入口地址為0x0010000C,這個地址也和我們之前的分析相符合。


  Exercise 6

  在這個練習中,我們將嘗試使用GDB的x命令(查看內存命令)。 x/Nx ADDR。這個指令將打印出從ADDR地址開始之后的N個字的內容。重啟一下Qemu。在Bios進入boot loader之前,內存地址0x00100000處8個字的內容,然后進入boot loader運行到內核開始處停止,再看下這個地址處的值。為什么二者不同?第二次這個內存處所存放的值的含義是什么?

  解答:

    在進入boot loader之前,從內存地址0x00100000處開始之后8個字的內容為:

    

    在進入kernel那一刻之前,從內存地址0x00100000處開始之后8個字的內容為:

    

      為什么會產生這種變化,因為bootmain函數在最后會把內核的各個程序段送入到內存地址0x00100000處,所以這里現在存放的就是內核的某一個段的內容,由於程序入口地址是0x0010000C,正好位於這個段中,所以可以推測,這里面存放的應該是指令段,即.text段的內容。

  

  那么到此為止Lab 1的Part 2部分的所有實驗就都已經完成了。

  如果有問題和建議,歡迎騷擾

    zzqwf12345@163.com

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM