iT邦幫忙

0

[linux][riscv] 如何使用 QEMU,對 linux kernel 下斷點

  • 分享至 

  • xImage
  •  

系列文章 : 資訊工程自學筆記

這一篇是想紀錄一下,該怎麼用 QEMU 開啟 linux kernel,並使用 gdb 接上 QEMU,並對運行在 QEMU 裡面的 linux kernel 下斷點。


在 QEMU 裡面運行 linux kernel

第一步就是需要在 QEMU 裡面嘗試去運行 linux kernel。

很幸運的是,這在之前的筆記就有記錄下來了,詳情可以參考 https://ithelp.ithome.com.tw/articles/10399676

下面是懶人包,假如沒有意外的話,使用下列的指令可以在 QEMU 上運行 linux kernel。

git clone git@github.com:TommyWu-fdgkhdkgh/qemu-boot-riscv-linux.git
cd qemu-boot-riscv-linux

make buildroot/build

make linux/build

make opensbi/build

make qemu/build

make qemu/run

取得 RISC-V 的 gdb

我們可以在 github 上取得編譯好的 image。

例如我這邊選擇 2026.05.06 release 出來的 image
https://github.com/riscv-collab/riscv-gnu-toolchain/releases/tag/2026.05.06


然後用 wget 把壓縮檔下載到自己的機器上

wget https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2026.05.06/riscv64-glibc-ubuntu-24.04-gcc.tar.xz 

接著再解壓縮

tar Jxvf riscv64-glibc-ubuntu-24.04-gcc.tar.xz

這樣子,就可以很簡單的得到 gdb !

./riscv/bin/riscv64-unknown-linux-gnu-gdb
…
(gdb)  

更改 linux kernel 的設定

假如想要下斷點在 linux kernel 裡面的話,多兩個設定會比較方便

  • 替 linux kernel 的 vmlinux 添加 debug symbol
  • 取消 KASLR ( Kernel Address Space Layout Randomization (KASLR) )

我們可以進入 linux 的資料夾

cd linux

然後用 menuconfig 進入選單介面

make ARCH=riscv CROSS_COMPILE=XXX/buildroot/output/host/bin/riscv64-buildroot-linux-gnu- menuconfig

接著做以下的設定

# 替 linux kernel 加上 debug symbol
Kernel hacking
--> Compile-time checks and compiler options
     --> Debug information
          --> Rely on the toolchain's implicit default DWARF version

# 關掉 KASLR
Kernel features
--> Randomize the address of the kernel image

最後再重新編譯 linux kernel

make ARCH=riscv CROSS_COMPILE=XXX/buildroot/output/host/bin/riscv64-buildroot-linux-gnu- vmlinux Image -j16

使用 gdb 連上 QEMU,並下斷點

XXX/qemu-boot-riscv-linux/qemu/build/qemu-system-riscv64  -M virt -bios  XXX/qemu-boot-riscv-linux/opensbi/build/platform/generic/firmware/fw_jump.elf -kernel XXX/qemu-boot-riscv-linux/linux/arch/riscv/boot/Image   -append "root=/dev/vda ro console=ttyS0"  -drive file=XXX/qemu-boot-riscv-linux/buildroot/output/images/rootfs.ext4,format=raw,id=hd0,if=none  -device virtio-blk-device,drive=hd0 -netdev  user,id=net0 -device virtio-net-device,netdev=net0  -nographic -s -S

可以在 Makefile 裡面看一下,是怎麼對 QEMU 下指令的,或是在 Makefile 裡面把 @ 拿掉,就可以在使用 Makefile 時候,看一下 Makefile 下的指令長什麼樣子。

並在結尾的地方加上 -s -S

  • -s ( 小寫的 s ) : -gdb tcp::1234 的簡寫,QEMU 會在 localhost 的 1234 port 等待 gdb 連進來。
  • -S ( 大寫的 S ) : QEMU 會凍住不動,直到 gdb 使用 continue command 或其他繼續執行的 command。

接下來可以運行 GDB

./riscv/bin/riscv64-unknown-linux-gnu-gdb  ./qemu-boot-riscv-linux/linux/vmlinux

在 GDB 連接到 localhost 的 1234 port

(gdb) target remote localhost:1234

連接到之後,可以嘗試下斷點,並 continue
沒問題的話,可以嘗試列出 stack trace

(gdb) b setup_arch
(gdb) continue
(gdb) bt
#0  setup_arch (cmdline_p=cmdline_p@entry=0xffffffff81403e98) at arch/riscv/kernel/setup.c:230
#1  0xffffffff80a00750 in start_kernel () at init/main.c:897
#2  0xffffffff80001158 in _start_kernel () at arch/riscv/kernel/head.S:321

在下斷點在開啟 paging 之前的程式碼

這時候會發現,原本的方法會沒辦法對開啟 paging 之前的程式碼下斷點 ( e.g. linux/arch/riscv/kernel/head.S/_start ),這需要做一些其他處理。


使用 readelf 取得 符號以及 section 資訊

./qemu-boot-riscv-linux/buildroot/output/host/bin/riscv64-buildroot-linux-gnu-readelf -s ./qemu-boot-riscv-linux/linux/vmlinux > log

我們可以看到 _start 的位址,並且可以知道 _start 是在 .head.text section。

…
1: ffffffff80000000     0 SECTION LOCAL  DEFAULT    1 .head.text
…
154735: ffffffff80000000  4648 NOTYPE  GLOBAL DEFAULT    1 _start
…

接著我們需要回到 QEMU

  • 替 QEMU 加上 debug symbol
  • 對 QEMU 下斷點,看一下 QEMU 是把 kernel image 載入到 physical memory 的哪一個區段。

替 QEMU 加上 debug symbol,並需要重新編譯 QEMU。

cd qemu
source ./my-venv/bin/activate
./configure --target-list=riscv64-softmmu --enable-debug
make -j16

開啟 QEMU,並對 QEMU 的 qemu/hw/riscv/virt.c/virt_machine_done 下斷點觀察 kernel_start_addr 的值。
在我這邊,kernel_start_addr 會是 0x80200000

{ https://github.com/qemu/qemu/blob/stable-10.0/hw/riscv/virt.c#L1492 }

    if (machine->kernel_filename && !kernel_entry) {
        kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
                                                         firmware_end_addr);
        riscv_load_kernel(machine, &boot_info, kernel_start_addr,
                          true, NULL);
        kernel_entry = boot_info.image_low_addr;
    }

接著回到 GDB,一開啟 GDB 就把 symbol file 用 add-symbol-file 載入到 GDB 裡面,並明確指出 .head.text 的偏移量是 0x80200000

接著我們就可以嘗試對 _start 下斷點,並 continue 查看有沒有成功。

./riscv/bin/riscv64-unknown-linux-gnu-gdb  ./qemu-boot-riscv-linux/linux/vmlinux
(gdb) add-symbol-file ./qemu-boot-riscv-linux/linux/vmlinux -s .head.text 0x80200000
(gdb) target remote localhost:1234
(gdb) b _start
(gdb) continue

圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言