編譯 linux 0.11,並且使用 QEMU + GDB 調試 kernel ( Ubuntu 11.04, GCC 4.5.2)

編譯 linux 0.11,並且使用 QEMU + GDB 調試 kernel ( Ubuntu 11.04, GCC 4.5.2)
本篇為本人編譯 linux 0.11 心得,寫下來備忘

取得 source code

取得 source code 有很多方式,如果是直接下載原版的 code 的話,會很麻煩 (要使用舊版的 GCC 與 OS ), 網路上有改過的 code,適用於比較新的 GCC( linux-0.11-20110805.tar.gz ), 這邊有個適用 GCC 4.3.2 的,不過我是用更新的,適用 GCC 4.5.2 的 linux-0.11-20110805.tar.gz, 不過都是大同小異,如果有閒的話,可以把兩包抓來 diff 看看差在哪, 也可以去 oldlinux 的論壇裡面看

編譯

我這次的編譯環境為:Ubuntu 11.04( linux kernel 3.0.2)GCC 4.5.2, 而 source 我選擇linux-0.11-20110805.tar.gz, 解壓縮後,使用 terminal,進入到該目錄,只要在有 MakeFile 這支檔案的目錄下,打入 make 就可以進行 compile, 如果編譯好的話,會在該目錄下,看到一個 Image 的檔案,就代表成功了

編譯問題排解

如果出現

make: *** [kernel/blk_drv/blk_drv.a] Error 2  

這是一個明顯的宏語法錯誤,把 blk.h 的第 87 行的 #elif 改為 #else 就行了,如果出現

make: *** [tools/system] ?? 1  

就加入以下的編譯選項

-fno-stack-protector

GCC 新版本有做 stack-protector ,但是開發 OS 用了會有問題,只要在 make.header 中的 CC 選項加入,就可以全部加入 , 例如

CC = gcc -fno-stack-protector

如果再打入 make 不行的話,可以先下 make clean 指令後,再下 make
如果想看每一個檔案所編譯出來的 Object file (附檔名為.o)的 assembly 的話,可以使用 objdump 來觀察 如:

objdump -S test.secton.o (-S 的參數式大寫,這邊大小寫有差別)  

也有圖形化介面的 objdump, 叫 dissy 也是很好用的 如果想看 Hex file的話,可以使用 `hexdump',如

hexdump -C boot.img

-C 參數可以以 BYTE 的方式顯示

模擬測試

使用 x86 emu 把 linux 0.11 裝起來,網路上趙博士是使用 bochs,不是很會用, 這邊也有 bochs 使用教學,心得 可以參考
我選擇使用 qemu,鍵入

qemu -m 16M -boot a -fda Image -hda ./../rootfs/hdc-0.11-new.img

以下列出參數的意義

-fda Image:代表你把 Image 執行目錄下  
-hda ./../rootfs/hdc-0.11-new.img:代表你把 HD img,放在相對目錄"上一層"的`rootfs/`下   
hdc-0.11-new.img:為一模擬 hd 的檔案,可以在趙博士所提供的`linux-0.11-devel-060625.zip`找到  
../hdc-0.11-new.img:視你把該 `hdc-0.11-new.img` 檔放在哪而定  

其實把 makefile .header 打開,裡面其實有 debug 的標籤,也就是如果要 run qemu 的話,只要打 make debug 就會執行

QEMU + GDB

鍵入以下

qemu -m 16 -boot a -fda Image -hda ./../rootfs/hdc-0.11-new.img -s -S -gdb tcp::1234

這邊有兩個 -s-S,分別代表不同意義

-s(小寫 s):運行虛擬機時將 1234 端口開啟成調試端口,供 eclipse 網絡調試時使用  
-S(大寫 S):啟動虛擬機時要「凍住」虛擬機,等待調試器發出繼續運行的命令  
-fda:在 qemu 中,是使用檔案來模擬磁碟的,這邊的意思是,使用 Imgae 這個檔案來當作 floppy A  
-m:設定模擬的記憶體有多大,這邊設定記憶體大小為 16MB  

順利的話,應該會顯示黑畫面,代表目前 QEMU 被凍住,等待 GDB client
在開另一個 terminal,鍵入

gdb tools/system

即可進入到 gdb client

先載入除錯的 symbol

(gdb) file tools/system

連線至遠端

(gdb) target remote localhost:1234

下中斷,停在 CS = 0x7C00 的地方,也就是 BIOS 把 MBR 載入的地方

(gdb)  br *0x7c00

在此時,bios 把控制權正式的交給了 linux,也就是說這裡開始就是我們自己控制的地方
而 0x7C00 對應的 code 應該是 bootsect.S

觀察0x7DFE 與 0x7DFF的值是否為0x55,0xAA

(gdb) x/16b 0x7DF0

因為這邊是組合語言,所以要用 si 來單步

(gdb) si

下中斷

(gdb) b main

執行至中斷 ,則就會執行到 main 中

(gdb) c

列出目前的 code(list)

(gdb)l

介紹一下 gdb 指令

b: 下中斷點
info b :u 列出目前中斷點,也可簡寫成"i b"
continue(c) 繼續執行直到下一個中斷點或結束
list(l): 列出目前上下文
step(s): 單步 (會進入 funciton)
next(n) : 單步 (不會進入 funciton)
until(u) 跳離一個 while for 迴圈
print(p): 顯示某變數,如 p str
info register(i r) : 顯示 CPU 的 register

GDB 印出記憶體的內容,格式為 x/nyz,其中

n: 要印出的數量
y: 顯示的格式,可為C( char), d(整數), x(hex)
z: 單位,可為 b(byte), h(16bit), w(32bit)

若要顯示90000的記憶體位置內容時,可以使用 x/16xb 0x90000

x也可顯示指令,如要顯示目前PC值所代表的指令,可以用

x/10i $pc

或是想要顯示上下文,也可改成

x/10i $pc-5

顯示ROM address 0 的指令,可用

x/10i 0

QEMU 問題排解

而 qemu 要順利打開可能會需要用到 kvm
可以使用

lsmod|grep kvm

看看 kvm 模組有沒有被載入
如果沒有的話,可以打

sudo kvm-ok

試著載入看看
如果再不行,看看 cpu 是否支援 VT or 機版 bios 是不是沒打開 intel-vt ( amd cpu 是 amd-v)的選項
重開機打開後,應該就可以使用 qemu 了

參考資料

可供GDB源碼調試的用GCC 4.X編譯的Linux 0.11實驗環境
目前已經成功地把linux-0.11移植到gcc 4.3.2
《Linux內核完全註釋》閱讀筆記——搭建實驗環境(轉載) Welcome to OldLinux
Eclipse CDT + QEMU 調試linux內核
GNU偵錯器