当前位置: 首页 > article >正文

QEMU 和 GDB 调试 Linux 内核

使用 QEMU 和 GDB 调试 Linux 内核是一种非常强大的方法,可以帮助开发人员调试和分析内核的行为。下面将详细介绍如何设置和使用 QEMU 和 GDB 来调试 Linux 内核。

环境准备

::: tip 系统环境

  • 22.04.3-Ubuntu
  • gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
  • GNU gdb (Ubuntu 12.1-0ubuntu1~22.04.2) 12.1 :::
编译 Linux 内核源码

# 获取 Linux 内核源码
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.10.6.tar.xz
# 解压
tar -xvf linux-6.10.6.tar.xz
cd linux-6.10.6

# 配置内核(使用默认配置)
make defconfig #运行结束生成 .config 文件在当前目录

# 生成调试符号并编译内核
make -j$(nproc) vmlinux

# 拷贝编译好的镜像备用
cp vmlinux ../
cp arch/x86/boot/bzImage ../

::: tip

  • vmlinux: 一个包含调试符号的未压缩内核映像,GDB 调试时会用到它,在当前目录linux-6.10.6
  • bzImage: 编译后的镜像 bzImage, 路径: linux-6.10.6/arch/x86/boot/bzImage :::
启动文件系统制作

wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xvf busybox-1.36.1.tar.bz2
cd busybox-1.36.1

make menuconfig
#-> Settings
# --- Build Options
#  [*] Build static binary (no shared libs) #进行静态编译 (CONFIG_STATIC)

# 安装完成后生成的相关文件会在 _install 目录下
make && make install   
#busybox-1.36.1/_install
cd _install 
mkdir proc
mkdir sys

# init 为内核启动的初始化程序, 内容在下面
vim init 
# 必须设置成可执行文件
chmod +x init  

# 打包启动的文件系统
find . | cpio -o --format=newc > ../../rootfs.img

::: tip init 文件内容

#!/bin/sh
echo "{==DBG==} INIT SCRIPT"
mkdir /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mount -t tmpfs none /tmp

mdev -s 
echo -e "{==DBG==} Boot took $(cut -d' ' -f1 /proc/uptime) seconds"

# normal user
setsid /bin/cttyhack setuidgid 1000 /bin/sh

:::

安装 QEMU

apt install qemu qemu-utils qemu-kvm virt-manager libvirt-daemon-system libvirt-clients bridge-utils

使用 QEMU 启动内核

::: tip

  • 确保已经安装QEMU
  • 确保以下文件准备好:
    • rootfs.img
    • bzImage
    • vmlinux :::

使用 QEMU 启动内核,并开启 QEMU 的 GDB 远程调试功能。

#ls 当前目录文件
#busybox-1.36.1 
#busybox-1.36.1.tar.bz2 
#rootfs.img
#bzImage
#vmlinux  
#linux-6.10.6 
#linux-6.10.6.tar.xz

qemu-system-x86_64 -kernel ./bzImage -initrd  ./rootfs.img -append "nokaslr console=ttyS0" -s -S -nographic

::: tip 参数说明

  • -kernel ./bzImage:指定启用的内核镜像;
  • -initrd ./rootfs.img:指定启动的文件系统;
  • -append "nokaslr console=ttyS0" :附加参数,其中 nokaslr 参数必须添加进来,防止内核起始地址随机化,这样会导致 gdb 断点不能命中;
  • -s :监听在 gdb 1234 端口;
  • -S :表示启动后就挂起,等待 gdb 连接;
  • -nographic:不启动图形界面,调试信息输出到终端与参数 console=ttyS0 组合使用 :::

此时,QEMU 已经启动并等待 GDB 的连接,CPU 被暂停。

使用 GDB 调试内核

在另一个终端中启动 GDB,加载 vmlinux 文件(未压缩的内核映像),以便使用调试符号。

gdb vmlinux

在 GDB 中,使用 target remote 命令连接到 QEMU 提供的 GDB 服务器:

(gdb) target remote :1234

此时,GDB 已经连接到 QEMU,并可以控制虚拟机的执行。可以在内核的特定位置设置断点,并开始调试。例如,可以设置断点在 start_kernel 函数:

(gdb) b start_kernel

然后使用 continue 命令继续运行内核:

(gdb) c

当内核运行到 start_kernel 时,GDB 会暂停并命中断点,接下来你可以逐步执行代码 (step 或 next 命令),查看变量的值等。

常用 GDB 命令

  • b <function>:在函数入口处设置断点。
  • b *<address>:在特定内存地址设置断点。
  • info breakpoints:查看当前设置的断点。
  • c:继续执行程序,直到下一个断点。
  • step:单步执行代码,进入函数内部。
  • next:单步执行代码,跳过函数调用。
  • print <variable>:打印变量的值。
  • x/<n> <address>:查看指定地址的内存内容。

::: important 注意事项

  • 内核编译时需要启用调试符号 (.config 文件CONFIG_DEBUG_INFO=y)。
  • QEMU 的 -s 和 -S 参数用于设置 GDB 远程调试的端口和暂停启动。 :::

参考:

  • 官方文档
  • 其他资料1
  • 其他资料2

http://www.kler.cn/a/519201.html

相关文章:

  • [极客大挑战 2019]BuyFlag1
  • 2025.1.24总结
  • Golang Gin系列-8:单元测试与调试技术
  • 使用 Docker 运行 Oracle Database 23ai Free 容器镜像并配置密码与数据持久化
  • 【电磁兼容】CE 传导骚扰
  • PyQt6医疗多模态大语言模型(MLLM)实用系统框架构建初探(下.代码部分)
  • RedisTemplate优化指南
  • 前端Vue2项目使用md编辑器
  • 1.25寒假作业
  • IDEA社区版(免费版)创建spring boot项目
  • Linux--权限
  • Spring Boot应用中实现基于JWT的登录拦截器,以保证未登录用户无法访问指定的页面
  • 语言集成查询LINQ
  • YOLOv8改进,YOLOv8检测头融合DynamicHead,并添加小目标检测层(四头检测),适合目标检测、分割等,全网独发
  • 洛谷P1003
  • Flink (十二) :Table API SQL (一) 概览
  • 安装最小化的CentOS7后,执行yum命令报错Could not resolve host mirrorlist.centos.org; 未知的错误
  • 驱动程序中的物理内存通过mmap机制映射到用户空间,用户空间得到虚拟内存地址然后进行相关数据的读写操作
  • 如何打造一个高并发系统?
  • Redis实战(黑马点评)——涉及session、redis存储验证码,双拦截器处理请求
  • Docker + Nginx 部署个人静态博客指南
  • 如何将xps文件转换为txt文件?xps转为pdf,pdf转为txt,提取pdf表格并转为txt
  • Python基于Django的宠物服务管理系统(附源码,文档说明)
  • 计算机组成原理——数据运算与运算器(一)
  • 使用 Redis 实现分布式锁的基本思路
  • gitlabgit分支合并