使用 GDB 调试 Linux Kernel 需要以下几个步骤:
-
编译一个可以调试的内核
-
QEMU 启动内核
-
GDB 连接 QEMU
编译一个可以调试的内核
在前文 Linux Kernel 从编译到运行 中我们编译了一个内核,这个内核是没有调试信息的,我们需要重新编译一个带调试信息的内核。
首先使用 make mrproper
清理内核源码,然后使用 make menuconfig
配置内核,
Kernel hacking > Compile-time checks and compiler options > Debug infomation > 选择 Rely on the toolchain's implicit default DWARF version
和 debug 有关的内核配置项还包括:
- CONFIG_GDB_SCRIPTS
- CONFIG_DEBUG_INFO
- CONFIG_KGDB
- CONFIG_KGDB_SERIAL_CONSOLE
- CONFIG_KGDB_TESTS
它们大部分是默认开启的,我们只需要确认一下。
然后使用 make -j8
编译内核,这个过程比较慢,需要等待一段时间。
QEMU 启动内核
在前文 Linux Kernel 从编译到运行 中我们使用 QEMU 启动内核,这里我们需要修改一下启动命令,增加 -s -S
参数。
最好使用内核启动参数 nokaslr
来禁用内核地址空间随机化。
修改后的 Makefile 如下:
.PHONY: initramfs run clean
initramfs:
cd initramfs && find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.img
run:
qemu-system-x86_64 \
-kernel bzImage \
-initrd initramfs.img \
-nographic \
-append "console=ttyS0 nokaslr"\
-m 1024 \
-smp 1 \
-S \
-s
clean:
rm -f initramfs.cpio initramfs.img
其中,-S
选项表示在启动时暂停 CPU,-s
选项表示在 1234 端口监听 GDB 连接。
-s
也可以使用 -gdb tcp::1234
代替。
把编译好的 bzImage 和 vmlinux 拷贝放到 QEMU 启动目录下
cp arch/x86/boot/bzImage ../workspace/
cp vmlinux ../workspace/
我的 QEMU 启动目录是
../workspace/
,你可以根据实际情况修改。
然后执行 make run
启动 QEMU。
GDB 连接 QEMU
使用 -S
选项启动 QEMU 后,QEMU 会暂停 CPU,等待 GDB 连接。
然后在另一个终端中使用 GDB 连接 QEMU
gdb vmlinux
然后在 GDB 中输入 target remote :1234
连接 QEMU
这时候就可以使用 GDB 调试内核了。
这里我们首先在 start_kernel
函数上打一个断点。
(gdb) b start_kernel
Breakpoint 1 at 0xffffffff838d9e90: file init/main.c, line 904.
然后输入 c
继续执行,QEMU 会继续执行内核启动过程,直到遇到断点。
(gdb) c
Continuing.
Breakpoint 1, start_kernel () at init/main.c:904
904 {
这样就可以在内核启动的过程中使用 GDB 进行调试了。
Tips
当我们在真实的调试过程中,会反复执行某些命令,例如 target remote :1234
,设置断点,查看变量等等。
对于这些命令,我们可以将它们写入 .gdbinit
文件中,这样每次启动 GDB 时会自动执行这些命令。
如果想使用当前目录下的 .gdbinit
文件,需要在 home 目录下的 gdbinit 文件中添加 set auto-load safe-path /
,否则 GDB 会忽略当前目录下的 .gdbinit
文件。
例如:
mkdir -p ~/.config/gdb
echo "set auto-load safe-path /" > ~/.config/gdb/gdbinit
echo "target remote :1234" > .gdbinit
echo "b start_kernel" >> .gdbinit
echo "c" >> .gdbinit
# 启动内核
make run
# 启动 GDB
gdb vmlinux
此时,GDB 会自动连接 QEMU,设置断点,并继续执行至 start_kernel
函数。
The End…