Linux kernel debug方案总结
前言
最近为了做Linux kernel module开发,配置Linux kernel debug环境踩了好多坑,捡回好久不用的CSDN账号总结一下
方案总结
利用log日志(仅作记录):
- printk
- dump_stack()
- debugfs
利用gdb调试方案:
- qemu+busybox方案;
优点:有利于kernel学习,可以从start_kernel开始断点
缺点:不利于kernel模块的debug学习,busybox构建最小系统后,依赖项都需要自己映射,过于麻烦 - qemu调试方案(linux kernel官方方案总结);
优点:简单有效,是需要修改kernel配置最少最简单的方案
缺点:有的时候会莫名其妙闪退,可能是x86平台的原因,断点好像除了gdb强行ctrl+c之外没有其他的办法可以主动打断 - qemu+kgdb双机模拟方案;
优点:可以主动打断
缺点:配置麻烦一些,lx-symbol命令的速度比qemu直接-s调试要慢很多
我在这里选择方案2和方案3,方案1就不做配置了。
准备工作
qemu安装
下载kernel源码
下面的repo是ubuntu默认版本,也可以使用其它的kernel版本
# 以下步骤均可以在host端完成
git clone git://git.launchpad.net/~ubuntu-kernel/ubuntu/+source/linux/+git/$(lsb_release -cs)
# 找到当前运行的Ubuntu的精确版本号:
cat /proc/version_signature
# 输出:Ubuntu 5.15.0-60.66-generic 5.15.78
# 切换到对应的版本
git checkout Ubuntu-5.15.0-60.66
# 复制当前kernel版本的config文件
cp -v /boot/config-$(uname -r) .config
# 修改.config文件
禁用CONFIG_RANDOMIZE_BASE
开启CONFIG_GDB_SCRIPTS
开启CONFIG_DEBUG_INFO_REDUCED
开启CONFIG_FRAME_POINTER
# 编译
apt install flex bison openssl libssl-dev libelf-dev
make -j $(nproc)
# 如果这里编译失败,清空.config文件里CONFIG_SYSTEM_TRUSTED_KEYS和CONFIG_SYSTEM_REVOCATION_KEYS值的内容为空
# CONFIG_SYSTEM_TRUSTED_KEYS=""
# CONFIG_SYSTEM_REVOCATION_KEYS=""
# 在VM里打开共享文件夹,具体步骤见上面的链接,然后执行
# 安装必须开启INSTALL_MOD_STRIP,否则文件过大会导致无法启动。后续调试可以单独编译module
make INSTALL_MOD_STRIP=1 modules_install
make -j $(nproc) install
# 单独编译module的命令
make -C . M=module在kernel里的路径 modules
方案1 - linux kernel官方方案
启动qemu
# 注意自己改路径
qemu-system-x86_64 -m 2048 -enable-kvm -cpu host \
-smp cores=16,sockets=1 \
-drive file=ubuntu-22.04.img,if=virtio \
-nic user,hostfwd=tcp::5557-:22 \
-fsdev local,security_model=passthrough,id=fsdev0,path=/kernel_code_directory \
-device virtio-9p-pci,fsdev=fsdev0,mount_tag=kernelmake \
-S -s \
-vnc :1 -monitor stdio
-s:qemu开启gdb,默认端口1234
-S:会在系统开始的时候直接断点
-fsdev和-device …两行是为了共享kernel code文件夹,如果修改kernel代码的话方便直接debug测试,不需要进行文件传输,具体见 linux qemu虚拟机安装。
启动gdb
# host
cd kernel_code_directory
gdb vmlinux
target remote :1234
如果映射成功的话,会触发exception断点,continue进入系统
注意:不要打断点在start_kernel上,打start_kernel断点再continue会失败,但不影响debug kernel module
映射kernel module到gdb
ssh进入vm之后,有两种方案
a. 加载该mod,然后
cat /sys/module/{这个modulede的名字}/sections/.text
cat /sys/module/{这个modulede的名字}/sections/.data
cat /sys/module/{这个modulede的名字}/sections/.bss
获取三个地址,在gdb里输入
add-symbol-file {这个module}.ko <text_addr> -s .data <data_addr> -s .bss <bss_addr>
b. 加载该mod,然后在gdb里输入lx-symbol,让gdb自动更新所有已经加载的符号表
测试
需要注意,module是动态加载的,rmmod之后module的代码段就不在当前内存空间内了,所以断点module init的函数是无法触发的,测试可以断点module_exit,然后rmmod,查看触发情况。
方案2 - qemu+kgdb双机模拟方案
VM kernel设置
- 需要开启KGDB选项,可以用menuconfig,或者.config文件里搜索,5.15版本默认是开启的。
- 修改grub,有两种方式:
a. 在启动选项中,选择Advanced Options,在需要debug的kernel版本上按e,进入编辑内核启动项,加入kgdbwait kgdboc=ttyS0,115200 nokaslr,按F10启动
b. 进入系统,在/etc/default/grub中修改启动命令行,加入kgdbwait kgdboc=ttyS0,115200 nokaslr,update-grub&&update-grub2,重启
qemu启动命令
在上述基础上,删去-S -s,加上
-serial tcp::1234,server,nowait
将串口映射到tcp端口1234上
启动gdb
- 同方案1,如果映射成功,断点会在kgdb_breakpoint函数的arch_kgdb_breakpoint()后面,确认没问题continue进入系统,剩下的步骤都与方案1一致
- 断点触发方式:
vm console输入echo g > /proc/sysrq-trigger,会将控制权交给gdb
Reference
- https://www.binss.me/blog/how-to-debug-linux-kernel/
- https://docs.kernel.org/translations/zh_CN/dev-tools/gdb-kernel-debugging.html
- https://www.cnblogs.com/haiyonghao/p/14440777.html