1. 实验环境
-
OS
CentOS7
-
Kernel
Host 和 VM 中均为 3.10.0
-
Host software
Qemu
Libvirt
GDB -
VM
2. 修改 XML 文件
虚机的 libvirt XML 文件需要修改两处:
- 修改 domain 标签
<domain type="kvm" xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
- 在之前添加
<qemu:commandline>
<qemu:arg value='-S'/>
<qemu:arg value='-gdb'/>
<qemu:arg value='tcp::1234'/>
</qemu:commandline>
另外 XML 中最好带有 uuid 标签,uuid 在 VM 的/boot/grub2/grub.cfg 中可以查看。
3. 编译安装内核
我是在 VM 中编译之后直接安装的,也可以在 Host 编译把 vmlinux 拷贝至 VM 中安装
进入源码目录
-
make mrproper
-
make clean
首次编译可忽略以上两步
-
cp /boot/config-$(uname -r) .config
-
make menuconfig .config
-
确保.config 文件中 CONFIG_DEBUG_INFO=y
如果没有开启请开启
- 关闭以下选项
我在第一次实验的时候下了断点断不住,看到网上有人说需要关闭以下选项,再次尝试可以断住。
修改 Makefile 中的优化等级
以 O0 的方式编译 3.10.0 会出现错误,因此我用 O1 编译,目前还不知道什么原因,不知道大家是不是也是这样呢
修改成 O1 之后编译也会出现一些问题,再次修改 Makefile,去掉-Wall 之后可以成功编译。
- make vmlinux -j 50
- make bzImage
- make modules -j 50
- make modules_install -j 50
- make install -j 50
成功安装内核之后,把这个带有 vmlinux 文件的源码拷贝至 Host 中
3. 开始调试
Host 端进入从 VM 拷贝的源码目录中
gdb vmlinux
GDB 中输入
target remote 127.0.0.1:1234
至此 Host 端的 GDB 已经和 VM 的 Kernel 建立了联系。
调试 Kernel
调试内核在 start_kernel 设置断点后,reboot VM 可以断在这里,但是注意一定要设置 Hardware breakpoint。
hb start_kernel
调试内核模块
调试模块需要在 GDB 中使用 add-symbol-file 命令加载符号表信息,add-symbol-file 需要指定模块的 section 信息,以下两种方式都可以获取 section 信息。
(1) 从程序中读取
b do_init_module
然后在 VM 中加载 ko,我们可以从 do_init_module 的参数 struct module *mod 查看 section 信息。
print mod->sect_attrs->attrs[1]->name
p /x mod->sect_attrs->attrs[1]->address
(2) 在 VM 中执行以下命令
cat /sys/module/virtio_net/sections/.text
cat /sys/module/virtio_net/sections/.data
cat /sys/module/virtio_net/sections/.bss
有一些 ko 可能没有 bss 没有关系
获取到.text 地址之后在 gdb 中加载 ko 模块的符号表,然后就可以调试 ko 模块了
add-symbol-file drivers/net/virtio_net.ko 0xffffffffc040e000 -s .data 0xffffffffc0413000
需要注意的是每次重新加载 ko,section 地址是会变,要 add-symbol-file 重新加载符号表。