Linux内核之GDB基本调试方法

时间:2022-05-18 15:09:19
单板软件Linux内核出现crash,如何采用GDB工具进行调试? 基本方法过程如下:

1. 为了测试GDB操作,故意在kernel/linux/fs/ioctl.c文件的do_vfs_ioctl方法中加入空指针操作代码,然后编译image烧入单板,启动单板,内核crash,部分log如下:

CPU 0 Unable to handle kernel paging request at virtual address 00000000, epc == 800a73b8, ra == 800a793c
Oops[#1]:
Cpu 0
$ 0   : 00000000 10008d00 00000000 ffffffea
$ 4   : fffffdfd 10008d01 00000001 00000000
$ 8   : 00000000 7fed2e40 00001cb2 00000b3b
$12   : 00031c7f 2ab5ead7 2aaac7c9 15010000
$16   : 7fed2e18 878ca5a0 00000000 00000001
$20   : 2ab84980 00000000 00000007 00000000
$24   : 00000000 2ab62090                 
$28   : 8782c000 8782fe98 7fed2fc8 800a793c
Hi    : 0000002a
Lo    : 000311fc
epc   : 800a73b8 do_vfs_ioctl+0x88/0x5c8
    Not tainted
ra    : 800a793c sys_ioctl+0x44/0x98
Status: 10008d03    KERNEL EXL IE
Cause : 00000008
BadVA : 00000000
PrId  : 0002a080 (Broadcom4350)
Modules linked in:

Process init (pid: 1, threadinfo=8782c000, task=8782bb68, tls=00000000)
Stack : 878ca1a0 00000004 00000000 10008d00 00000603 2aabcfff 87b0bea8 00000001
        87beeaf0 2aabc000 2aabd000 87aa9cb0 2aabd000 2aabd000 8782ff08 fffffff8
        00000001 7fed3258 00000007 00000000 878ca5a0 0000540d 00000000 00000001
        2ab84980 800a793c 08100871 00000001 87bea41c 0000fff2 00000000 2abbdff0
        7fed2e18 00000001 7fed2e60 2abbdff0 00000120 8001ba7c 00000000 00000000
        ...
Call Trace:(--Raw--
[<800a793c>] sys_ioctl+0x44/0x98
[<8001ba7c>] stack_done+0x20/0x3c

Call Trace:
[<800a73b8>] do_vfs_ioctl+0x88/0x5c8
[<800a793c>] sys_ioctl+0x44/0x98
[<8001ba7c>] stack_done+0x20/0x3c

Code: 0c029c9f  02003021  8fbf0064 <8c020000> 8fb40060  8fb3005c  8fb20058  8fb10054  8fb00050
Disabling lock debugging due to kernel taint
Kernel panic - not syncing: Attempted to kill init!
Rebooting in 1 seconds..<6>
stopping CPU 1
kerSysMipsSoftReset: called on cpu 0
    
2. 启动GDB, 直接在主机(开发机)控制台运行gdb工具(或./mips-linux-uclibc-gdb),然后敲入 file  ...../vmlinux启动带调试信息的内核,注意此时要配置内核debug开关重新编译内核生成文件vmlinux(比之前的文件大,50M以上),配置内核debug开关如下,
Kernel hacking  --->
    [*] Kernel debugging
    [*] Compile the kernel with debug info
编译内核命令:make kernelbuild,要在project的根目录下运行该命令进行编译。
启动GDB命令如下:
[myname@localhost bin]$ ./mips-linux-uclibc-gdb
(gdb) file /opt/mydir/kernel/linux/vmlinux

3. GDB调试定位
Checked call trace log, the most important part in log is “epc” (exception program counter), it is where the crash happened. In this example, the “epc” is 0xc0d1d488. For X1000 and X3500, the address like 0x8XXXXXXXX is in kernel, and the address like 0xcXXXXXXX is in some module.
地址0x800a73b8就是对应于地址(do_vfs_ioctl+0x88),后者表示位于函数do_vfs_ioctl的偏移地址0x88处。
调试过程如下:

(gdb) list *(0x800a73b8)
0x800a73b8 is in do_vfs_ioctl (fs/ioctl.c:569).
564                             error = vfs_ioctl(filp, cmd, arg);
565                     break;
566             }
567             error = *test;
568             return error;
569     }
570
571     SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
572     {
573             struct file *filp;
(gdb) list*(do_vfs_ioctl+0x88)
0x800a73b8 is in do_vfs_ioctl (fs/ioctl.c:569).
564                             error = vfs_ioctl(filp, cmd, arg);
565                     break;
566             }
567             error = *test;
568             return error;
569     }
570
571     SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
572     {
573             struct file *filp;
(gdb) list*(sys_ioctl+0x44)


可以分析该函数附近代码发现错误位置在line567,test为空指针(故意在前面赋值NULL)。

注:如果list*(0x80xxxxxx)命令提示如下信息:No source file for address 0x80xxxxxx. 原因是没有在make menuconfig中打开对应的调试开关进行编译。