使用GDB和VSCode调试内核

背景

上一篇,已经搭建好QEMU的内核调试环境:https://notes.z-dd.online/2024/03/06/%E5%9F%BA%E4%BA%8EQEMU%E7%9A%84%E5%86%85%E6%A0%B8%E8%B0%83%E8%AF%95%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/

这篇主要在前面的基础上尝试用gdb来调试内核,躺了不少坑。

安装GDB

这里安装支持多种硬件体系架构的GDB版本,也可直接使用系统自带的默认gdb。如架构不同,需使用交叉编译工具链中的gdb,后面有时间会更新不同架构下的调试。

sudo apt-get install -y gdb-multiarch	# 支持多种硬件体系架构的GDB版本

准备调试内核

在以前的基础上需修改一些内核配置才能调试:

  • 打开CONFIG_GDB_SCRIPTS:在内核主目录下生成gdb脚本文件vmlinux-gdb.py,该脚本实现了一些便于内核调试的命令,它们都以lx-开头,这个暂时先没用,后面再研究看看
  • 打开CONFIG_DEBUG_INFO:该选项用于在内核编译时开启-g标志,保证编译的内核带有调试信息,这个很重要!
  • 关闭CONFIG_DEBUG_INFO_REDUCED
  • 关闭CONFIG_RANDOMIZE_BASE:该选项用于关闭 KASLR,或者可以在内核启动参数中添加nokaslr,这个会导致地址随机化,从而无法调试,这个很重要!

运行qemu

这里只调试内核,就没有带根文件系统,也可以带上,这个看自己。

$ qemu-system-x86_64 -s -S  \
	-kernel ./bzImage \
    -hda /dev/zero \
	-append "root=/dev/zero console=ttyS0" \
	-nographic

说明:

  • -s 选项是 -gdb 的简写,会在本地的1234端口启动一个 GDB 服务
  • -S 代表暂停虚拟机,等待 GDB 执行 continue 指令

GDB命令行调试测试

# 重开一个shell,进入上面内核的源码目录
$ cd /path/to/your/linux
$ gdb-multiarch ./vmlinux

#以下进行调试
(gdb) target remote:1234
Remote debugging using :1234
0x000000000000fff0 in exception_stacks ()

#打断点,这里打在C语言入口函数(start_kernel),能看到后面的文件名及行数,说明内核可以调试
(gdb) b start_kernel
Breakpoint 1 at 0xffffffff83220960: file init/main.c, line 875.
(gdb) c
Continuing.

Breakpoint 1, start_kernel () at init/main.c:875
875	{
(gdb) c
Continuing.

VSCode图形化调试

  1. VSCode打开上面运行内核的源码文件夹

  2. 修改VSCode调试配置
    运行和调试 -> 创建launch.json文件 -> 添加配置 -> C/C++: gdb 启动, 会自动生成相关配置,做一些相关修改如下:

    {
        // 使用 IntelliSense 了解相关属性。 
        // 悬停以查看现有属性的描述。
        // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
        "version": "0.2.0",
        "configurations": [
            {
                "name": "kernel-debug",
                "type": "cppdbg",
                "request": "launch",
    
                //主要添加修改下面3项
                "program": "${workspaceFolder}/vmlinux",
                "miDebuggerServerAddress": "127.0.0.1:1234",
                "miDebuggerPath": "gdb-multiarch",
    
                "args": [],
                "stopAtEntry": true,
                "cwd": "${fileDirname}",
                "environment": [],
                "externalConsole": false,
                "MIMode": "gdb",
                "setupCommands": [
                    {
                        "description": "为 gdb 启用整齐打印",
                        "text": "-enable-pretty-printing",
                        "ignoreFailures": true
                    },
                    {
                        "description": "将反汇编风格设置为 Intel",
                        "text": "-gdb-set disassembly-flavor intel",
                        "ignoreFailures": true
                    }
                ]
            }
            
        ]
    }
  3. 启动调试
    先打好断点,比如在init/main.c中的start_kernel函数中, 再QEMU启动内核,VSCode按“F5”或点击启动调试即可开始调试内核,比如单步运行等。

vscode-gdb-kernel.png

现在是手动先运行QEMU再启动VSCode调试,有没有办法在直接启动VSCode的时候自动启动QEMU呢?
可以使用VSCode的task功能,这里就不详述了,对于调试功能影响不大。

参考