<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

文章分类

导航

订阅

Ubuntu内核调试要点(中)——配置目标机和建立调试会话

  虽然如上一部分所讲,原生的Ubuntu内核在编译时已经包含了内核调试支持(KGDB),但是它默认是不工作,如果是使用,那么需要先启用。这与Windows的内核调试是一样的。

  开启内核调试的方法不难,只要在内核命令行增加一些参数。基本步骤如下:

  1,切换到/etc/grub.d目录下,以sudo方式打开40-custom文件,即:

   sudo gedit /ect/grub.d/40-custom

    然后从/boot/grub/grub.cfg中复制一个菜单项(menuentry)过来,再把菜单名中增加调试信息,然后在内核命令行中增加KGDB选项,即下面这样:

    menuentry 'Ubuntu, with Linux 3.12.2 Kernel Debug Serial' --class ubuntu --class gnu-linux --class gnu --class os {    
    recordfail
    gfxmode $linux_gfx_mode
    insmod gzio
    insmod part_msdos
    insmod ext2
    set root='(hd0,msdos1)'
    search --no-floppy --fs-uuid --set=root eb5d8aee-6a0f-4257-b5b8-8d6731ba6764
    echo 'Loading Linux 3.12.2 with KGDB built by GEDU lab...'
    linux/boot/vmlinuz-3.12.2 root=UUID=eb5d8aee-6a0f-4257-b5b8-8d6731ba6764 ro quiet splash nomodeset $vt_handoff print-fatal-signals=1 kgdbwait kgdb8250=io,03f8,ttyS0,115200,4 kgdboc=ttyS0,115200 kgdbcon nokaslr
    echo 'Loading initial ramdisk ...'
    initrd/boot/initrd.img-3.12.2
    }

    新增部分:kgdbwait kgdb8250=io,03f8,ttyS0,115200,4 kgdboc=ttyS0,115200 kgdbcon nokaslr

    其中,kgdboc=ttyS0,115200 kgdbcon是关键。其中,kgdboc是KGDB over console的缩写,后面的ttyS0代表通过1号串口进行通信,波特率为115200。后面的nokaslr是禁止内核空间的地址随机化,我们将在第三部分详细解释。

   如果调试目标是虚拟机,那么模拟串口很方便,像上面这样设置就可以了。如果调试目标是物理机器,那么今天很多机器都不带串口,这时可以考虑使用PCIe的扩展串口卡(对于台式机)。实在没有串口,可以考虑使用网络连接方式,即所谓的kgdboe(KGDB over ethernet),我们以后再介绍。

  修改grub的配置后,需要执行sudo update-grub来更新。更新后目标机器就准备好了。

  对于主机端,如果使用虚拟机,那么可以把目标机克隆一份。如果是真实机器,那么最好选择相同版本的Ubuntu。

  对于串口方式,因为默认用户没有串口设备的使用权限,所以应该先使用如下命令来增加权限:

!sudo usermod -a -G dialout gychang

  注意dialout后面是用户名,应该替换成你使用的用户名。如果不调整权限,那么也可以使用sudo方式来启动gdb,但是如果忘记使用sudo了,就要退出重来。     

  注意,上诉权限修改需要logout再登录才生效。

  接下来应该通过stty命令来设置串口的通信速率。这里需要说明一下,在某些版本的GDB中,可以通过set remotebaud 115200这样的命令在GDB中设置波特率,但是在最近的一些版本中,这条命令不存在了,这时便需要使用Linux的命令来设置了,即: stty -F /dev/ttyS0 115200

    gychang@gychang-HP-Pro-3380-MT:~/dbg$ stty -F /dev/ttyS0    
    speed 9600 baud; line = 0;
    min = 0; time = 10;
    -brkint -icrnl -imaxbel
    -opost -onlcr
    -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke
    gychang@gychang-HP-Pro-3380-MT:~/dbg$ stty -F /dev/ttyS0 115200
    gychang@gychang-HP-Pro-3380-MT:~/dbg$ stty -F /dev/ttyS0
    speed 115200 baud; line = 0;
    min = 0; time = 10;
    -brkint -icrnl -imaxbel
    -opost -onlcr
    -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke

  上面先是显示旧的设置,再设置,最后再观察确认。        

   主机和目标机都准备好后,现在主机端启动以如下命令启动gdb:

   gdb -s /usr/lib/debug/boot/vmlinux-4.10.0-28-generic

   GDB启动后,会加载内核文件中的符号,加载完毕后,输入以下命令让GDB开始等待远程目标:      

   target remote /dev/ttyS0

  

  让主机端的GDB进入等待后,重启目标机器,启动时如果grub的菜单没有自动弹出,那么请按住shift键。然后在菜单中选择我们前面配置的调试项。

  如果一切顺利的话,目标机会主动中断到主机端的GDB,因为我们在命令行参数中的kgdbwait命令的含义就是在启动时等待kgdb和中断。这个启动早期的中断一般是通过一条断点指令来实现的,被称为初始断点,下面因初始断点中断后的场景:


  但是有时可能不顺利,比如GDB报告收到意外的包,忽略之类,这时可以按Ctrl + C让GDB退出等待状态,然后再执行target remote /dev/ttyS0让其重新与目标机握手和对话。

  初始断点命中后,就可以执行各种GDB命令了,最常用的就是bt,观察栈回溯,如下图所示:

  

  上图中,kgdb_breakpoint函数就是触发断点指令(int 3)的函数,它所在的源文件名为debug_core.c,是KDB和KGDB共享的核心调试逻辑,其作用相当于NT内核中的KD(调试引擎)。  

  接下来可以使用dir命令来设置源文件位置,设置好后,就可以使用l命令来观察源代码了,比如:

  接下来,可以执行n和s命令单步,在内核空间里逛一逛,也可以在感兴趣的位置埋个断点(命令b),让内核跑起来(c),遇到断点再停下来。这种自由控制内核的感觉是其它方式无法达到的。

  怎么样,是不是很简单呢?不过实际调试时,还会有一些实际的问题,比如如何处理64位和KASLR(内核空间随机化)等干扰。我们将在下一讲继续介绍。





posted on 2018年3月10日 13:02 由 admin

Powered by Community Server Powered by CnForums.Net