安裝環境

apt-get install qemu-system build-essential flex bison libelf-dev libssl-dev
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.12.tar.xz
tar -xvf linux-5.15.12.tar.xz
cd linux-5.15.12

編譯 kernel

幾個比較常用到的 Linux kernel makefile target

make menuconfig
# 確定 DEBUG_INFO 和 GDB_SCRIPTS 的選項為 y
# 直接 make 會連 module 一起編很久,這邊就編 vmlinux 和給 VM 開機用的 bzImage
make -j4 vmlinux
make -j4 bzImage

編譯需要一些時間,如果有跳錯通常是缺函式庫,如果不是可能要 google 一下錯誤訊息,我自己遇到的不是因為缺函式庫的錯誤只有下面這個

no rule to make target 'debian/canonical-certs.pem'

編輯 .config 把 CONFIG_SYSTEM_TRUSTED_KEYS 的值改成 "" 以及把 CONFIG_SYSTEM_REVOCATION_KEYS 的值也改成 ""

在 QEMU 中啟動 Linux kernel

執行以下指令

qemu-system-x86_64 -kernel arch/x86/boot/bzImage -nographic -append "console=ttyS0"

基本的意思就是啟動一台 x86_64 架構處理器的 VM ,以 bzImage 映像檔作為 kernel ,關閉圖形並將文字輸出到 ttyS0 裝置上

沒有特別的狀況的話, kernel 會開始啟動,直到處理器使用率被跑滿,畫面會停在這一句

[    8.088348] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---

表示 kernel 找不到可以掛載的 root fs ,沒辦法啟動應該在裡面的 init process

這時可以按鍵盤的 Ctrl+a 接著按 x 中止 VM

啟動 gdb

linux kernel 有提供一些 linux debug 用的 gdb 指令,可以修改 gdb 的設定檔讓 gdb 啟動時自動載入加入這些指令的 script

echo "add-auto-load-safe-path `pwd`/scripts/gdb/vmlinux-gdb.py" >> ~/.gdbinit

現在用以下指令啟動 QEMU

qemu-system-x86_64 -kernel arch/x86/boot/bzImage -nographic -append "console=ttyS0 nokaslr" -S -s

-S 參數是讓 QEMU 將 VM 啟動時就將 VM 停住等待 gdb 的指令,-s 參數則是讓 QEMU 會監聽 port 1234 的連線。 nokaslr 的核心參數是停用隨機分配 kernel 運作位址的功能。接著在另一個 terminal 上啟動 gdb 連線

user@user-VirtualBox:~/os/linux-5.15.12$ gdb vmlinux 
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from vmlinux...
(gdb) target remote :1234
Remote debugging using :1234
0x000000000000fff0 in exception_stacks ()
(gdb) b start_kernel
Breakpoint 1 at 0xffffffff831c0fb5: file init/main.c, line 934.
(gdb) c
Continuing.

Breakpoint 1, start_kernel () at init/main.c:934
934 {
(gdb)

start_kernel 是 Linux 核心在完成處理器架構相關的初始化之後的進入點,到達這個 breakpoint 之後就可以用 gdb 讓 kernel 進行單步執行或檢查記憶體、處理器等 debug 工作

Linux 提供的 gdb 指令都是 lx- 開頭,可以用 apropos 列出相關的指令

(gdb) apropos lx-
lx-clk-summary -- Print clk tree summary
lx-cmdline --  Report the Linux Commandline used in the current kernel.
lx-configdump -- Output kernel config to the filename specified as the command
lx-cpus -- List CPU status arrays
lx-device-list-bus -- Print devices on a bus (or all buses if not specified)
lx-device-list-class -- Print devices in a class (or all classes if not specified)
lx-device-list-tree -- Print a device and its children recursively
lx-dmesg -- Print Linux kernel log buffer.
lx-fdtdump -- Output Flattened Device Tree header and dump FDT blob to the filename
lx-genpd-summary -- Print genpd summary
lx-iomem -- Identify the IO memory resource locations defined by the kernel
lx-ioports -- Identify the IO port resource locations defined by the kernel
lx-list-check -- Verify a list consistency
lx-lsmod -- List currently loaded modules.
lx-mounts -- Report the VFS mounts of the current process namespace.
lx-ps -- Dump Linux tasks.
lx-symbols -- (Re-)load symbols of Linux kernel and currently loaded modules.
lx-timerlist -- Print /proc/timer_list
lx-version --  Report the Linux Version of the current kernel.
(gdb) 

Reference

http://nickdesaulniers.github.io/blog/2018/10/24/booting-a-custom-linux-kernel-in-qemu-and-debugging-it-with-gdb/