内核调试:QEMU+GDB 调试 Linux 内核
使用 QEMU 运行自定义 Linux 内核,通过 GDB 远程调试 start_kernel,包含环境搭建、内核编译、busybox rootfs 制作和踩坑记录。
在真实机器上调试内核风险高、门槛高。使用 QEMU 模拟一台 x86_64 虚拟机,配合 GDB 远程调试,可以在普通开发机上完成从内核启动到函数级别的调试。
本文基于 Ubuntu 22.04(WSL 也可),内核版本 5.15.78,QEMU 5.1.0。
一、安装 QEMU
1.1 源码编译安装(可选)
wget https://download.qemu.org/qemu-5.1.0.tar.xz
tar -xf qemu-5.1.0.tar.xz && cd qemu-5.1.0
sudo apt-get install libglib2.0-dev # glib
sudo apt-get install libpixman-1-dev # pixman
# 只编译 x86_64 系统模拟器,减少编译时间
./configure --target-list=x86_64-softmmu
make -j && sudo make install
1.2 apt 直接安装(推荐)
sudo apt install qemu-system-x86
二、编译带调试信息的内核
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.15.78.tar.gz
tar -xvf linux-5.15.78.tar.gz
cd linux-5.15.78
export ARCH=x86
make x86_64_defconfig
make menuconfig
menuconfig 中需要修改两处:
- 关闭 KASLR:
Processor type and features→ 取消Randomize the address of the kernel image (KASLR)。地址随机化会干扰断点。 - 开启调试信息:
Kernel hacking→Compile-time checks and compiler options→ 勾选:Compile the Kernel with debug infoProvide GDB scripts for kernel debugging
如果找不到这些选项,通常是目标内核版本与宿主机差距过大,或没有匹配的调试配置。
编译内核:
make -j 20
最终产物为 arch/x86_64/boot/bzImage。
踩坑:canonical-certs.pem 编译报错
Ubuntu 22.04 编译内核可能报错:
No rule to make target 'debian/canonical-certs.pem', needed by 'certs/x509_certificate_list'.
执行以下两条命令后重新编译:
scripts/config --disable SYSTEM_TRUSTED_KEYS
scripts/config --disable SYSTEM_REVOCATION_KEYS
三、busybox 制作最小 rootfs
内核启动后需要根文件系统承载 init 进程和 shell 命令。
3.1 编译静态 busybox
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -jxvf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
make menuconfig
在 Settings 中勾选:
[*] Build static binary (no shared libs)
保存后编译安装:
make
make install
3.2 制作 rootfs 镜像
dd if=/dev/zero of=rootfs.img bs=1M count=10
mkfs.ext4 rootfs.img
mkdir fs
sudo mount -t ext4 -o loop rootfs.img ./fs
sudo make install CONFIG_PREFIX=./fs
# 创建必要目录并拷贝 busybox 示例配置
cd fs/
sudo mkdir proc dev etc home mnt
sudo cp -r ../examples/bootfloppy/etc/* etc/
cd ..
sudo chmod -R 777 fs/
sudo umount fs
四、QEMU 启动与 GDB 调试
4.1 先测试能否正常启动
qemu-system-x86_64 \
-kernel ./linux-5.15.78/arch/x86/boot/bzImage \
-hda ./busybox-1.36.1/rootfs.img \
-append "root=/dev/sda console=ttyS0" \
-nographic
4.2 以调试模式启动
qemu-system-x86_64 \
-smp 4 \
-m 2048M \
-kernel ../linux-5.15.78/arch/x86/boot/bzImage \
-drive format=raw,file=../busybox-1.36.1/rootfs.img \
-net nic -net tap,ifname=tap0,script=no,downscript=no \
-append "root=/dev/sda nokaslr console=ttyS0" \
-nographic \
-S \
-s
参数说明:
-S:启动后暂停 CPU,等待调试器接入。-s:打开 GDB 远程调试端口 1234(等价于-gdb tcp::1234)。nokaslr:与编译阶段关闭 KASLR 保持一致,保证断点地址稳定。
4.3 GDB 远程连接
另开一个终端:
cd linux-5.15.78
gdb ./vmlinux
target remote :1234
b start_kernel
c
程序会在 start_kernel 处停下,之后可以单步、查看变量和调用栈。
五、虚拟机和宿主机网络互通
在宿主机创建 tap 网卡:
sudo ip tuntap add dev tap0 mode tap
sudo ip link set dev tap0 up
sudo ip address add dev tap0 192.168.2.128/24
QEMU 启动参数中已带 -net tap,ifname=tap0,虚拟机内配置 IP:
ip addr add 192.168.2.129/24 dev eth0
ip link set eth0 up
互相 ping 验证:
# 虚拟机内 ping 宿主机
ping 192.168.2.128
# 宿主机 ping 虚拟机
ping 192.168.2.129
六、VS Code 界面化调试
安装 C/C++ 插件后,使用 launch.json 配置远程 gdb:
{
"version": "0.2.0",
"configurations": [
{
"name": "kernel-debug",
"type": "cppdbg",
"request": "launch",
"miDebuggerServerAddress": "127.0.0.1:1234",
"program": "${workspaceFolder}/vmlinux",
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"MIMode": "gdb"
}
]
}
先用 QEMU 启动内核,再在 VS Code 中按 F5,即可在源码界面打断点调试。
七、扩展:用 ubuntu-base 制作完整 rootfs
busybox 适合调试内核本身;如果需要完整用户环境,可以用 ubuntu-base 通过 chroot 制作:
dd if=/dev/zero of=rootfs.img bs=2048 count=1M
mkfs.ext4 -F -L linuxroot rootfs.img
mkdir fs
mount -t ext4 -o loop rootfs.img ./fs
tar -xzvf ubuntu-base-23.10-base-amd64.tar.gz -C ./fs
cp /etc/resolv.conf ./fs/etc
# 挂载系统目录,chroot 内才能使用 apt
mount -t proc /proc ./fs/proc
mount -t sysfs /sys ./fs/sys
mount -o bind /dev ./fs/dev
mount -o bind /dev/pts ./fs/dev/pts
chroot ./fs
chroot 后可以安装软件、设置 hostname 和 root 密码,完成后卸载:
umount ./fs/proc
umount ./fs/sys
umount ./fs/dev/pts
umount ./fs/dev
八、总结
- QEMU
-S -s是内核调试的核心组合:暂停等待调试器 + 开放 1234 端口。 - 内核必须关闭 KASLR 并开启 debug info,断点才稳定可靠。
- busybox 静态编译 + ext4 镜像是最快的内核调试 rootfs 方案。
canonical-certs.pem报错通过关闭 SYSTEM_TRUSTED/REVOCATION_KEYS 解决。- tap 网卡让虚拟机与宿主机互通,便于后续网络相关调试。
- VS Code cppdbg 可以把命令行调试升级为界面化调试。
相关脚本
整理自 cyy-tag/bash-example 仓库: