利用qemu+gdb在ubuntu下搭建调试kernel的环境
编译内核
修改配置文件
本文使用的内核为linux-5.3.8,以下命令在源码根目录下执行.
生成针对x86_64架构
的配置文件:
make ARCH=x86_64 x86_64_defconfig
如果编译时所处的平台架构为x86,则无需添加ARCH=x86_64
选择需要编译的x86_64架构
的内核相应功能:
make ARCH=x86_64 menuconfig
如果编译时所处的平台架构为x86,则无需添加ARCH=x86_64
在出现的UI界面中选择:
Kernel-hacking
-> Compile-time checks and compiler options
选择Compile the kernel with debug info
条目(按Y键)
然后会出现该条目下的子条目,在子条目中
选择Provide GDB scripts for kernel debugging
(按Y键)
如果Reduce debugging information
是开着的,就在Reduce debugging information
上按N键取消该条目.
选择最下面的Exit退出,系统会提示你是否保存到配置文件,点YES.
编译
make -j4
其中4为你为本次编译提供的物理核心数,核心越多,编译越快.
编译完成会生成2个很重要的文件,一个为arch/x86/boot/
路径下的bzImage
,另一个是源码根目录下的vmlinux
.
利用qemu运行编译生成的内核
安装qemu
sudo apt-get install qemu qemu-system
利用qemu运行之前编译生成的内核
在kernel源码根目录运行运行以下命令:
qemu-system-x86_64 -kernel arch/x86/boot/bzImage -hda /dev/zero -append "root=/dev/zero console=ttyS0" -serial stdio -display none
会出现的现象是,kernel起初正常运行,但是在与文件系统的交互时出了问题,最后terminal中显示:
[ 4.394062] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---
这是正常的,因为还我们还没有制作挂载在qemu虚拟系统中的文件系统镜像
.
制作文件系统
制作文件系统的方法有2种,使用的工具分别为busybox
和buildroot
,使用busybox
需要我们自行创建部分文件,生成的是最小文件系统; 而使用buildroot
只需要编译,缺点是编译时间太久.
用busybox制作文件系统
这里使用busybox建立一个最小文件系统
编译busybox
在这里下载busybox源码,以busybox1.25.0为例,逐行执行以下命令
cd busybox-1.25.0
make defconfig
make menuconfig
在menuconfig跳出的UI界面中依次选择
-> Busybox settings
-> Build Options
对选项Build BusyBox as a static binary (no shared libs)
按Y键使之开头出现星号.
make -j4
sudo make install
如果安装成功可以在Busybox源码根目录中看到_install目录.
生成initrd
使用以下命令将_install
文件夹复制到其它位置
# 进入Busybox源码的父目录
cd ..
mkdir ramdisk
cd ramdisk
# 注意最后的"."
cp -r ../busy-1.25.0/_install/* .
使用以下命令建立初始化进程init.
cd ramdisk
ln -s bin/busybox init
使用以下命令设置开机启动程序
# 建立程序运行所需文件夹
mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin},dev}
init程序首先会访问etc/inittab文件,以获取开机要启动的程序列表,因此我们得编写一个inittab
cd etc
vim inittab
将以下内容填入inittab
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
赋予inittab可执行权限
chmod +x inittab
可以看到inittab中最先执行的是rcS,因此我们要建立一个rcS
mkdir init.d
cd init.d
vim rcS
将以下内容填入rcS
#!/bin/sh
mount proc
mount -o remount,rw /
mount -a
clear
echo "My Tiny Linux Start :D ......"
赋予rcS可执行权限
chmod +x rcS
rcS中,mount -a 是自动挂载 /etc/fstab 中的内容,因此我们需要一个fstab文件,以设置需要挂载的系统.
cd ramdisk/etc/
vim fstab
fstab的内容如下
# /etc/fstab
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
devtmpfs /dev devtmpfs defaults 0 0
最小文件系统完成! 将其压缩成文件镜像即可.
cd ramdisk
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.img
最后生成的initramfs.img
就是我们的根文件系统.
用buildroot制作文件系统
获取buildroot源码并设置配置文件
git clone git://git.buildroot.net/buildroot
cd buildroot
make menuconfig
- 在跳出的UI界面依次选择
Target options
Target Architecture (x86_64)
在x86_64
上按空格键,以选择该选项
- 再返回与
Target options
同一级的界面
选择Filesystem images
在ext2/3/4 root filesystem
按Y,并进入ext2/3/4 variant (ext4)
选择ext4
退出并保存配置.
编译buildroot
make -j4
编译完成之后,在buildroot/output/images/目录中可以获得文件系统镜像rootfs.ext2
和rootfs.ext4
运行qemu和gdb调试kernel
我们有2种文件系统,用哪一种都行.
第一种: 使用rootfs.ext2
作为文件系统运行qemu
# 在A Terminal中运行以下命令
qemu-system-x86_64 -kernel bzImage -boot c -m 1024 -hda rootfs.ext2 -append "root=/dev/sda rw console=ttyS0, 115200 acpi=off nokaslr" -serial stdio -display none -s -S
如果需要调试内核的kvm模块,则向上述命令添加 -enable-kvm
# 在B Terminal中运行以下命令,并在成功启动gdb后输入
# target remote localhost:1234
# 即可开始使用gdb debug我们之前编译的内核
gdb vmlinux
第二种:使用initramfs.img
作为文件系统运行qemu
# 在A Terminal中运行以下命令
qemu-system-x86_64 -kernel bzImage -boot c -m 1024 -initrd initramfs.img -append "root=/dev/sda rw console=ttyS0, 115200 acpi=off nokaslr" -serial stdio -display none -s -S
如果需要调试内核的kvm模块,则向上述命令添加 -enable-kvm
# 在B Terminal中运行以下命令,并在成功启动gdb后输入
# target remote localhost:1234
# 即可开始使用gdb debug我们之前编译的内核
gdb vmlinux