KGDB-双物理机调试内核


KGDB-双物理机调试内核

author: Ewan

准备工作

搭建双物理机调试Linux内核环境之前,需要:

  • 2台物理机,均装有Linux操作系统,下文以Host和Target区分调试机和被调试机
  • 一份需要调试的Linux源码
  • 一根串口转USB线

attention:本文使用Target的ttyS1,Host的ttyUSB0,波特率115200.

  1. 将串口转USB线的串口端连接到Target上,USB端连接到Host上

    连接好之后可以在Host和Guest上都安装cutecom串口通信软件,测试Host和Guest的串口通信是否正常

    cutecom安装:sudo apt-get install cutecom

    测试方法为,在Host和Target上均用sudo cutecom打开cutecom,然后在Host上打开设备/dev/ttyUSB0,在Target上打开设备/dev/ttyS1,尝试互相发送数据,如果成功,说明串口连接无故障。

  2. 在Target上编译并安装需要调试的Linux源码,在make menuconfig时注意需要选择KGDB* , KGDB_SERIAL*, KGDB_USB*, DEBUG_INFO, DEBUG_INFO_DWARF4, MAGIC_SYSRQ这些内核编译选项,我使用的5.3.8默认包含这些选项.编译安装完成后,向/etc/default/grub中的GRUB_CMDLINE_LINUX添加nokaslr, 以保证内核代码不会随机改变位置,然后运行sudo update-grub,将添加的信息更新到grub中,重启Target.

  3. 完成以上工作之后,将源码目录下生成的vmlinux文件拷贝到Host

  4. 在Host上下载Agent-Proxy并编译,然后启动Agent-Proxy

    git clone http://git.kernel.org/pub/scm/utils/kernel/kgdb/agent-proxy.git

    cd agent-proxy;make

    sudo ./agent-proxy 5550^5551 0 /dev/ttyUSB0,115200

    Agent-Proxy是一款开源代理软件,能够将串口分割为多个功能,这里使用Host的5550和5551两个端口,5550用于连接Target的console, 5551用于连接Target的KGDB监听端口

开始调试

  1. 在Host上连接Target的console
sudo telnet localhost 5550
  1. 在Target上进入kgdb模式
sudo su
echo ttyS1,115200 >  /sys/module/kgdboc/parameters/kgdboc

然后就可以在dmesg里看到Registered I/O driver kgdboc,如果没看到说明哪些地方出问题了。

然后利用下面的命令将Target的运行暂停,这样就可以在host上使用gdb连接并调试。

echo g >/proc/sysrq-trigger

如果想要调试系统启动早期代码,则需要在Target启动时暂停,如果有这种需求,可以在Target的启动选项中加入console=tty0 console=ttyS1,115200 kgdbwait kgdboc=ttyS1,115200

  1. 然后就可以在步骤1的窗口中看到类似于下面的内容:

    Entering kdb (current=0xcb846c80, pid 2301) on processor 3 due to Keyboard Entry

    [3]kdb>

    在[3]kdb> 后输入kgdb,这之后Target就会进入等待远程gdb连接的状态。

  2. 在Host上新开一个Terminal窗口,进入从源码目录,输入gdb vmlinux,这里的vmlinux就是之前在Target上生成,拷贝到Host上的Linux符号文件。

    该步骤的全部输入如下:

    gdb vmlinux

    (gdb) target remote localhost:5551
    Remote debugging using localhost:5551

    kgdb_breakpoint () at kernel/debug/debug_core.c:1072
    1072 wmb(); /* Sync point after breakpoint */
    (gdb)

然后就可以快乐地使用gdb进行调试Target了。

-----update on 11.5.2020-------------------

使用上面的步骤,确实可以调试一些功能,但是最近想调试kvm的代码,发现经过上面的步骤,进入到gdb之后,无法在kvm中设置断点,例如:

(gdb) b handle_io
不存在handle_io

此时要怎么做呢?

kvm和kvm_intel都是内核的module,vmlinux默认情况下是不加载module的符号的,需要手动加载。

  1. 在target上找到kvm和kvm_intel的.text段地址
cat /sys/module/kvm/sections/.text
cat /sys/module/kvm_intel/sections/.text

我获得的target上kvm和kvm_intel两个module在内核中的.text位置分别为:

0xffffffffc0205000,0xffffffffc0335000.

  1. 在host上的gdb调试窗口将这两个地址的符号加载进gdb

注意这一步中,gdb vmlinux运行的目录为从target复制过来的Linux源码的目录

(gdb) add-symbol-file arch/x86/kvm/kvm.ko 0xffffffffc0205000

(gdb) add-symbol-file arch/x86/kvm/kvm.ko 0xffffffffc0335000

然后就可以在类似于handle_io的kvm代码出下断点了。