利用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种,使用的工具分别为busyboxbuildroot,使用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.ext2rootfs.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

相关