使用qemu 和 GDB 调试 Linux 5.19 内核
准备阶段
软件下载
- linux 内核下载
下载busybox
wget https://busybox.net/downloads/busybox-1.34.0.tar.bz2
软件安装
- 安装依赖库和构建套件
sudo apt install git -y # 代码管理工具
sudo apt install build-essential -y # 编译所需的常用软件,如gcc等
sudo apt install flex bison bc kmod pahole -y # 内核编译所需软件
sudo apt-get install libelf-dev libssl-dev libncurses-dev -y # 内核源码编译依赖的库
sudo apt install zstd -y
sudo apt-get install glibc-static -y # 编译busybox 依赖
- 安装qemu
sudo apt-get install qemu qemu-kvm qemu-system -y # qemu虚拟机相关软件
sudo apt-get install virt-manager -y # docker中不需要安装,虚拟机图形界面,会安装iptables,可能需要重启才能以非root用户启动virt-manager,当然对于内核开发来说安装这个软件是为了生成自动生成virbr0网络接口
编译并运行Linux kernel
编译Linux kernel
# cd 进入下载有linux 内核源码压缩包的目录下
tar -zxvf linux-5.19.tar.gz && sync
cd linux-5.19
# 生成linux 编译配置文件 .config
make menuconfig
参考配置如下图
空格键 开启/关闭选项
回车 进入下级目录
连续两次按 esc 退出当前目录/退出menuconfig
File system ------->
开启ext4 文件系统debug 支持
kernel hacking -----> printk and dmesg option ------>
调试信息时间戳 Show timing information on printks
调用函数显示 Show caller information on printk
编译
make -j12 # -j 后面接的数字表示本次编译使用的线程数,一般是设备核心数的1-2倍
编译busybox
# 进入下载有busybox压缩包的目录
tar -xvf busybox-1.36.0.tar.bz2 && sync
cd busybox-1.36
make menuconfig
参考配置
空格键 开启/关闭选项
回车 进入下级目录
连续两次按 esc 退出当前目录/退出menuconfig
Settings -----> 生成设置
make -j12 && make install# 编译busybox 并将编译好好的文件安装到_install 文件夹下
sync
ls _install # 此时在安装目录下会出现如下文件 :
# bin linuxrc sbin usr
创建initramfs
创建带有BusyBox可执行程序、必要的设备文件、启动脚本 `init`的初始跟文件系统。这里没有内核模块,如果需要调试内核模块,可将需要的内核模块包含进来。`init`脚本只挂载了虚拟文件系统 `procfs`和 `sysfs`,没有挂载磁盘根文件系统,所有调试操作都在内存中进行,不会落磁盘。
mkdir initramfs
cd initramfs
cp ../_install/* -rf ./
mkdir dev proc sys
sudo cp -a /dev/{null, console, tty, tty1, tty2, tty3, tty4} dev/ # 此处用bash 执行可能会报错,可以用touch 挨个创建大括号中的文件
rm linuxrc
vim init #编写init 脚本,内容见下
init 启动脚本
#!/bin/busybox sh
mount -t proc none /proc
mount -t sysfs none /sys
exec /sbin/init
chmod a+x init # 给init 启动脚本可执行权限
ls
# bin dev init proc sbin sys usr
打包根文件系统
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz
使用qemu 启动linux 内核
cd busybox-1.36.0
qemu-system-x86_64 -s -kernel /path/to/vmlinux -initrd initramfs.cpio.gz -nographic -append "console=ttyS0"
# 启动内核后 ls 查看系统目录
/ # ls
bin dev init proc root sbin sys usr
-s
是-gdb tcp::1234
缩写,监听1234端口,在GDB中可以通过target remote localhost:1234
连接;-kernel
指定编译好的调试版内核;-initrd
指定制作的initramfs;-nographic
取消图形输出窗口,使QEMU成简单的命令行程序;-append "console=ttyS0"
将输出重定向到console,将会显示在标准输出stdio。
使用GDB 连接qemu
cd linux-5.19
/usr/local/bin/gdb vmlinux
(gdb) target remote localhost:1234
调试Linux 内核
```bash
# 在函数cmdline_proc_show设置断点,虚拟机中运行cat /proc/cmdline命令即会触发。
(gdb) b cmdline_proc_show
Breakpoint 1 at 0xffffffff81298d99: file fs/proc/cmdline.c, line 9.
(gdb) c
Continuing.
Breakpoint 1, cmdline_proc_show (m=0xffff880006695000, v=0x1 <irq_stack_union+1>) at fs/proc/cmdline.c:9
9 seq_printf(m, "%s\n", saved_command_line);
(gdb) bt
#0 cmdline_proc_show (m=0xffff880006695000, v=0x1 <irq_stack_union+1>) at fs/proc/cmdline.c:9
#1 0xffffffff81247439 in seq_read (file=0xffff880006058b00, buf=<optimized out>, size=<optimized out>, ppos=<optimized out>) at fs/seq_file.c:234
#2 0xffffffff812908b3 in proc_reg_read (file=<optimized out>, buf=<optimized out>, count=<optimized out>, ppos=<optimized out>) at fs/proc/inode.c:217
#3 0xffffffff8121f174 in do_loop_readv_writev (filp=0xffff880006058b00, iter=0xffffc900001bbb38, ppos=<optimized out>, type=0, flags=<optimized out>) at fs/read_write.c:694
#4 0xffffffff8121ffed in do_iter_read (file=0xffff880006058b00, iter=0xffffc900001bbb38, pos=0xffffc900001bbd00, flags=0) at fs/read_write.c:918
#5 0xffffffff81220138 in vfs_readv (file=0xffff880006058b00, vec=<optimized out>, vlen=<optimized out>, pos=0xffffc900001bbd00, flags=0) at fs/read_write.c:980
#6 0xffffffff812547ed in kernel_readv (offset=<optimized out>, vlen=<optimized out>, vec=<optimized out>, file=<optimized out>) at fs/splice.c:361
#7 default_file_splice_read (in=0xffff880006058b00, ppos=0xffffc900001bbdd0, pipe=<optimized out>, len=<optimized out>, flags=<optimized out>) at fs/splice.c:416
#8 0xffffffff81253c7c in do_splice_to (in=0xffff880006058b00, ppos=0xffffc900001bbdd0, pipe=0xffff8800071a1f00, len=16777216, flags=<optimized out>) at fs/splice.c:880
#9 0xffffffff81253f77 in splice_direct_to_actor (in=<optimized out>, sd=0xffffc900001bbe18, actor=<optimized out>) at fs/splice.c:952
#10 0xffffffff812540e3 in do_splice_direct (in=0xffff880006058b00, ppos=0xffffc900001bbec0, out=<optimized out>, opos=<optimized out>, len=<optimized out>, flags=<optimized out>) at fs/splice.c:1061
#11 0xffffffff8122147f in do_sendfile (out_fd=<optimized out>, in_fd=<optimized out>, ppos=0x0 <irq_stack_union>, count=<optimized out>, max=<optimized out>) at fs/read_write.c:1434
#12 0xffffffff812216f5 in SYSC_sendfile64 (count=<optimized out>, offset=<optimized out>, in_fd=<optimized out>, out_fd=<optimized out>) at fs/read_write.c:1495
#13 SyS_sendfile64 (out_fd=1, in_fd=3, offset=0, count=<optimized out>) at fs/read_write.c:1481
#14 0xffffffff8175edb7 in entry_SYSCALL_64_fastpath () at arch/x86/entry/entry_64.S:203
#15 0x0000000000000000 in ?? ()
(gdb) p saved_command_line
可能遇到的问题
- cp -a {} 报错
<p> sudo cp -a /dev/{null, console, tty, tty1, tty2, tty3, tty4} dev/ # 此处用bash 执行可能会报错,可以用touch 挨个创建大括号中的文件 </p>
- make bzImage -j12 构建内核镜像报错 缺少证书停止构建
No rule to make target ‘debian/canonical-certs.pem‘, needed by ‘certs/x509\_certificate\_list‘
cd linux-5.19
vim .config
# 注释掉.config 文件以下内容:
# CONFIG_SYSTEM_TRUSTED_KEYS="debian/canonical-certs.pem"
# CONFIG_SYSTEM_REVOCATION_KEYS="debian/canonical-revoked-certs.pem"
# 编辑完成后按esc + 冒号输入 wq! + 回车 退出并保存
参考
https://chenxiaosong.com/courses/kernel/kernel-dev-environment.html
https://blog.csdn.net/hknaruto/article/details/133672892
https://consen.github.io/2018/01/17/debug-linux-kernel-with-qemu-and-gdb/
https://blog.51cto.com/u_12947/10201710