GDB+QEMU调试内核模块(实践篇)

调试内核模块的方法有很多。最常用的,可能也是上手难度最低的就是使用prinfk打印出相关的调试信息,但是总给人的感觉不够geeek,所以这里描述一种能够使用gdb+qemu来调试内核的方法。

启动虚拟机

/data/bin/qemu-system-x86_64 -m 6144 -M accel=kvm -cpu host -smp 4 -nographic \
-kernel /root/code/linux-4.18.2/arch/x86/boot/bzImage \
-device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x6,drive=drive-virtio-disk1,id=virtio-disk0,disable-modern=off,disable-legacy=on \
-drive format=raw,if=none,id=drive-virtio-disk1,cache=directsync,file=/data/rootfs.img,aio=native \
-netdev tap,id=tap0,ifname=virbr0-nic,vhost=no,script=no \
-device virtio-net-pci,netdev=tap0 \
-append "root=/dev/vda rw console=ttyS0 nokaslr" -enable-kvm -S -gdb tcp::8889

当进入到虚拟机之后,启动相关的网卡,可以方便的向虚拟机中拷贝相关需要的文件,方便调试。

内核模块实例

使用以下链接 中内核模块,作为调试示例。
如果调试的目标虚拟机是Ubuntu16.04,使用如下分支:

git clone -b build-for-ubuntu16046 https://github.com/caisan/simplefs

如果是centos7 作为调试的目标虚拟机主机,使用如下分支:

git clone -b build-for-centos7 https://github.com/caisan/simplefs

为了能在Guest中编译simplefs文件系统,需要在Guest中编译下Linux-kernel 4.18.2,但是不是完全编译,只需要编译所需要的kernel headers,module依赖。
进入到Guest中:

cd linux-4.18.2
make menuconfig
make modules - j4
make modules_install -j4

上述编译结束后,到Guest 的路径下:/lib/modules/4.18.2下确认下,不出意外用ls -al可以看到buildsource都链接到/root/linux-4.18.2,如下:

然后再进入到simplefs, 执行编译此simplefs模块:

make

随后加载该simplefs.ko模块。注意如果是在centos7上加载此模块,需要先加载jbd2,即就是:modprobe jbd2
调试的虚拟机我选的Ubuntu 16.04.6,内核版本是
如下:

 insmod ./simplefs.ko
cat /sys/module/simplefs/sections/.text
0xffffffffc098f000
cat /sys/module/simplefs/sections/.data
0xffffffffc0992000
cat /sys/module/simplefs/sections/.bss
0xffffffffc0992580

以上通过.text, .data, .bss定位到了模块的加载地址,可以使用这三个信息开始我们的调试。

在Host上调试内核。

以上将基于QEMU+kvm中的Guest安装好了,现在在Host上进行调试工作。这里说在Host上其实不太准确,调试的目标是Guest kernel上的模块,Host上执行相当于是GDB远程调试,whatever,意会这个意思就可以了。但是请务必将Host上调试的kernel代码和Guest上的版本保持一致。
以我为例,我Host上kernel代码路径是在/root/code/linux-4.18.2
因此可以在~/.gdbinit 中添加如下内容:

add-auto-load-safe-path /root/code/linux-4.18.2/scripts/gdb/vmlinux-gdb.py
target remote:8889

(这里要强调的是,Host上的kernel同样是经过编译过后的)
进入到已经编译好的kernel源码目录:

gdb ./vmlinux

此时进入到了gdb中,由于我们调试的simplefs.ko模块,因此还要在gdb中加载这个模块,注意:这里的加载和使用insmod加载有区别,两者都要做。
将Guest中网络设置好之后,可以将在Guest中编译好的simplefs.ko模块拷贝出来,下面.text``.data``.bss相关的地址和在Guest中保持一致。

(gdb) add-symbol-file ./simplefs.ko 0xffffffffc098f000 -s .data 0xffffffffc0992000 -s .bss 0xffffffffc0992580

加载好之后就可以调试了。

触发调试。

这里以调试mount的操作为例,在Guest中执行常规的挂载mount 操作,如下:

dd bs=4096 count=100 if=/dev/zero of=image
./mkfs-simplefs image
mount -o loop -t simplefs image /root/simplefs/mount/

或者采用以下方式设置挂载:

fallocate -l 1G disk.img
losetup -f /root/disk.img

取消挂载:
losetup -D

然后在Host的gdb中设置断点:

(gdb) b simplefs_fill_super
(gdb) c

等待触发到breakpoint。调试命令和gdb使用相同。

posted @ 2020-01-07 14:23  Linux-inside  阅读(4435)  评论(0编辑  收藏  举报