《ucore lab1》實驗報告


資源

  1. ucore在線實驗指導書
  2. 我的ucore實驗代碼

練習1:理解通過make生成執行文件的過程

詳見《ucore lab1 exercise1》實驗報告

練習2:使用qemu執行並調試lab1中的軟件

詳見《ucore lab1 exercise2》實驗報告

練習3:分析bootloader進入保護模式的過程

詳見《ucore lab1 exercise3》實驗報告

練習4:分析bootloader加載ELF格式的OS的過程

詳見《ucore lab1 exercise4》實驗報告

練習5:實現函數調用堆棧跟蹤函數

詳見《ucore lab1 exercise5》實驗報告

練習6:完善中斷初始化和處理

詳見《ucore lab1 exercise6》實驗報告

擴展練習1:增加一用戶態函數(待完成)

擴展proj4,增加syscall功能,即增加一用戶態函數(可執行一特定系統調用:獲得時鍾計數值),當內核初始完畢后,可從內核態返回到用戶態的函數,而用戶態的函數又通過系統調用得到內核態的服務。

擴展練習2:用鍵盤實現用戶模式內核模式切換(待完成)

用鍵盤實現用戶模式內核模式切換。具體目標是:“鍵盤輸入3時切換到用戶模式,鍵盤輸入0時切換到內核模式”。 基本思路是借鑒軟中斷(syscall功能)的代碼,並且把trap.c中軟中斷處理的設置語句拿過來。

調Bug日志

【2019-1-3】Bug 2:lab1運行到pmm_init時失敗

  1. 解決Bug1后,再次執行make qemu仍然失敗,提示以下信息:
ebp:0x00007bf8 eip:0x00007d6e args:0xc031fcfa 0xc08ed88e 0x64e4d08e 0xfa7502a8 
    <unknow>: -- 0x00007d6d --
qemu-system-i386: Trying to execute code outside RAM or ROM at 0x457e0000
This usually means one of the following happened:

(1) You told QEMU to execute a kernel for the wrong machine type, and it crashed on startup (eg trying to run a raspberry pi kernel on a versatilepb QEMU machine)
(2) You didn't give QEMU a kernel or BIOS filename at all, and QEMU executed a ROM full of no-op instructions until it fell off the end
(3) Your guest kernel has a bug and crashed by jumping off into nowhere

This is almost always one of the first two, so check your command line and that you are using the right type of kernel for this machine.
If you think option (3) is likely then you can try debugging your guest with the -d debug options; in particular -d guest_errors will cause the log to include a dump of the guest register state at this point.

Execution cannot continue; stopping here.
  1. 使用gdb單步跟蹤,發現是在pmm_init函數中加載完gdt表后,執行mov %ax, %gs時異常結束了。我嘗試將gs寄存器改為dx,則沒有問題。這意味着gs等段寄存器此時不能被訪問。

  2. 問題比較像是某些權限沒設置好,導致無法訪問段寄存器。應該是前面的步驟哪里沒做好。發散思路:

    • [x] 梳理一遍從BIOS啟動到pmm_init之間要做哪些事情,檢查各個步驟是否OK,比如保護模式是否設置成功
      • [x] bootloader打開A20門、加載gdt、使能PE(沒問題)
      • kern_init在執行pmm_init之前初始化(edata, end)這段內存
      • [x] kern_init在執行pmm_init之前打印字符串及調試信息(沒影響)
    • 查找段寄存器無法訪問的可能原因
    • 確認加載全局描述符表需要哪些操作,是否有遺漏操作或操作有誤
    • 對比lab1和lab2,看下哪些步驟有區別
      • lab1的kernel加載地址為0x100000,lab2的kernel加載地址為0xC0100000
      • [x] lab1和lab2的movl %cr0, %eax對應的匯編代碼不一致(使用gdb調試發現沒問題,應該是objdump文件將二進制代碼翻譯成匯編代碼時出現的問題)
  3. 最終發現是初始化(edata, end)這段內存導致的問題。這段內存包括.got.plt,.data.rel.local,.bss和.data.rel.ro.local四個段,我縮小為只初始化.bss段就沒問題了。這個問題之前在做mit 6.828 lab時也遇到過,現在才想起來。

bin/kernel:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00003955  00100000  00100000  00001000  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .rodata       000008a0  00103958  00103958  00004958  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .stab         00007bc1  001041f8  001041f8  000051f8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .stabstr      000020cf  0010bdb9  0010bdb9  0000cdb9  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .data         00000950  0010e000  0010e000  0000f000  2**5
                  CONTENTS, ALLOC, LOAD, DATA
  5 .got.plt      0000000c  0010e950  0010e950  0000f950  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  6 .data.rel.local 000000c6  0010e960  0010e960  0000f960  2**5
                  CONTENTS, ALLOC, LOAD, DATA
  7 .data.rel.ro.local 0000006c  0010ea40  0010ea40  0000fa40  2**5
                  CONTENTS, ALLOC, LOAD, DATA
  8 .bss          00001300  0010eac0  0010eac0  0000faac  2**5
                  ALLOC
  9 .comment      0000002a  00000000  00000000  0000faac  2**0
                  CONTENTS, READONLY
  1. 那么為什么初始化.got.plt及data.rel.ro.local這些段會有問題?我從RELRO(Relocation Read Only)這個網頁中找到了答案:.data.rel.ro.local這個段顧名思義就知道是只讀的,現在想將其memset為0,自然會導致問題。

  2. 那么為什么lab2沒有問題?對比兩個lab的tools/kernel.ld文件,發覺lab2的鏈接腳本匯總在定義edata前,先用ALIGN命令將位置設置在能被0x1000整除的位置,這樣恰好將.got.plt, data.rel.ro.local這些段跳過了,因此(edata, end)這段內存恰好只包含.bss段,這時memset就沒問題了!

    . = ALIGN(0x1000);
    .data.pgdir : {
        *(.data.pgdir)
    }

    PROVIDE(edata = .);

順便貼上lab2的bin/kernel的這幾個段的信息:

 5 .got.plt      0000000c  c0118950  c0118950  00019950  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  6 .data.rel.local 000000c6  c0118960  c0118960  00019960  2**5
                  CONTENTS, ALLOC, LOAD, DATA
  7 .data.rel.ro.local 00000088  c0118a40  c0118a40  00019a40  2**5
                  CONTENTS, ALLOC, LOAD, DATA
  8 .data.rel     00000004  c0118ac8  c0118ac8  00019ac8  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  9 .data.pgdir   00002000  c0119000  c0119000  0001a000  2**12
                  CONTENTS, ALLOC, LOAD, DATA
 10 .bss          00000f28  c011b000  c011b000  0001c000  2**5
                  ALLOC
  1. 總結:定位問題的常規方法:收集信息、理解原理、推測原因(將所有可能的原因都列舉出來,逐一排查)。如果不是之前做mit 6.828 lab遇到過同樣的Bug,估計定位起來更艱難,因為我差別就放棄懷疑memset語句有問題了。這個問題的棘手之處也在於:memset時沒立即出錯,等到后面初始化GDT時才出錯。總之,經驗很重要,以及不能隨便放過任何一個可能。

【2019-1-3】Bug 1:bootblock鏈接失敗

  1. 從陳渝老師的github代碼庫clone了一份干凈的代碼到本地,進入labcodes_answer/lab1_result目錄,執行make qemu時失敗了,提示以下信息:
ld -m    elf_i386 -nostdlib -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o
600 >> 510!!
'obj/bootblock.out' size: 600 bytes
make: \*\*\* [bin/bootblock] Error 255
  1. 根據提示信息,可知是鏈接后的obj/bootblock.out文件大於512字節,導致檢查不通過。這個檢查是在哪里設置的呢?在代碼庫里搜索,發現是在tools/sign.c中設置的:
    printf("'%s' size: %lld bytes\n", argv[1], (long long)st.st_size);
    if (st.st_size > 510) {
        fprintf(stderr, "%lld >> 510!!\n", (long long)st.st_size);
        return -1;
    }
  1. 怎么解決?先確認一下是不是每個lab都有這個問題。進入labcodes_answer/lab2_result目錄,執行make qemu成功了,說明lab2是正常的。

  2. 為什么lab1和lab2的現象不一樣呢?有兩種可能:一是鏈接腳本不同,lab2增加了某些鏈接選項,使得鏈接后的文件可以變小;二是代碼文件不同,lab1的代碼文件比lab2大。於是首先比較兩個lab的鏈接腳本,發現基本相同。然后比較兩個lab的diamante文件,發現boot/nootmain.c有以下兩處差異:

// lab1
unsigned int    SECTSIZE  =      512 ;
struct elfhdr * ELFHDR    =      ((struct elfhdr *)0x10000) ;     // scratch space

// lab2
#define SECTSIZE        512
#define ELFHDR          ((struct elfhdr *)0x10000)      // scratch space
  1. 可以推測lab2的寫法是比lab1省內存的,因為使用宏代替了全局變量。但這點差異足夠大到lab1鏈接不通過嗎?先將lab2的寫法同步到lab1,再make一把,發現果然可以了,boot/bootblock.out的size由600字節減少到488字節,少了112字節。真神奇,兩個全局變量竟會導致增加了112字節!

  2. 總結:定位問題的一種思路:如果有兩份代碼,一份有問題,另一份正常,那么可以使用對比法。


免責聲明!

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



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