文章目录

  • 编译内核
  • 下载qemu
  • 构建initramfs根文件系统
  • 1.编译Busybox
  • 2.生成initrd
  • 3.测试根文件系统
  • gdb调试内核
  • 参考文献


编译内核

# make menuconfig // 调整编译选项

在内核编译选项中,开启如下"Compile the kernel with debug info"

Kernel hacking —>
Compile-time checks and compiler options —>
[ ] Compile the kernel with debug info

示意图如下,利用键盘选中debug选项,然后敲"Y"勾选:

qemu 挂载内核 qemu 调试内核_linux

(以上是别人博客中找到,确实类似gcc编译要gdb得加-g,但是我按照默认安装最后也默认了允许调试,算是现在新版本的一个福音吧。不然就重新再编译将近一小时吧(不过不知道打了ccache这下要跑多久,闲了可以测试测试))

先检查自己之前编译的内核有没有该信息。

# grep CONFIG_DEBUG_INFO .config
CONFIG_DEBUG_INFO=y

qemu 挂载内核 qemu 调试内核_linux_02

下载qemu

qemu有两种安装方式,第一种是使用命令行直接下载安装,第二种是通过源码进行编译安装,推荐命令行直接下载。

开始直接编译到最后安装的时候要Nanja但是我的源下不了这个。

所以我采用了直接安装qemu

sudo apt-get install qemu

直接安装也是最方便省事的,依赖文件现在也打包好了似乎。

构建initramfs根文件系统

1.编译Busybox

wget https://busybox.net/downloads/busybox-1.34.0.tar.bz2

qemu 挂载内核 qemu 调试内核_ubuntu_03

首先安装静态依赖,否则会有报错。

sudo apt-get install glibc-static -y

会发现ubuntu找不到软件。

参考1:

参考2:

  • centos
sudo yum install glibc-static
  • ubuntu
sudo apt-get install libc6-dev
sudo apt-get install libc-dev-bin

解压压缩包后

make menuconfig

qemu 挂载内核 qemu 调试内核_qemu 挂载内核_04

看上面的英文是按y表示确认,确认后[]内有*

qemu 挂载内核 qemu 调试内核_内核_05

退出,提示保存,选Yes

开始编译

make -j4
make install

make 期间会遇到许多的warning,不用管它,对安装影响不大。

此时可以在busybox-1.34.0/中看到生成的_install目录。通过下面的命令可以验证busybox是否安装正确:./busybox ls

2.生成initrd

首先将上一步生成的_install文件夹复制到其他位置。

cd ..

mkdir ramdisk

cd ramdisk

cp -r ../busy-1.34.0/_install/* .

qemu 挂载内核 qemu 调试内核_qemu 挂载内核_06

设置初始化进程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
::sysinit:/etc/init.d/rcS   
::askfirst:-/bin/sh    
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a

qemu 挂载内核 qemu 调试内核_linux_07

直接把这个文件权限拉满

chmod 777 inittab

编写系统初始化命令。

从inittab文件中可以看出,首先执行的是/etc/init.d/rcS脚本,因此,我们生成初始化脚本。

在此新建文件夹并编写脚本

mkdir init.d
cd init.d
vim rcS

内容如下

#!/bin/sh

mount proc
mount -o remount,rw /
mount -a    
clear                               
echo "My Tiny Linux Start :D ......"

同样权限给拉满。chmod 777 rcS

在rcS脚本中,mount -a 是自动挂载 /etc/fstab 里面的东西,可以理解为挂在文件系统,因此我们还需要编写 fstab文件来设置我们的文件系统。

ramdisk/ect/创建文件fstab

# /etc/fstab


proc            /proc        proc    defaults          0       0

sysfs           /sys         sysfs   defaults          0       0

devtmpfs        /dev         devtmpfs  defaults          0       0

qemu 挂载内核 qemu 调试内核_qemu 挂载内核_08

至此,我们已经完成了RAM Disk中相关文件的配置,可以压缩生成文件镜像了。

cd ramdisk

find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.img

最后生成的initramfs.img就是我们的根文件系统。

qemu 挂载内核 qemu 调试内核_ubuntu_09

3.测试根文件系统

我的编译好的内核在的路径/home/yan/linux-5.4.1/arch/x86_64/boot/bzImage

每个人要去看自己的内核编好的放哪里了。

busybox-1.34.0目录下:

qemu-system-x86_64 -kernel /home/yan/linux-5.4.1/arch/x86_64/boot/bzImage -initrd ../initramfs.img
  • -kernel 指定编译好的调试版内核;
  • -initrd 指定制作好的initramfs;

按Enter键后,就可以进入到文件系统中,运行命令输入ls /dev检查是否挂载成功。

qemu 挂载内核 qemu 调试内核_内核_10

关机输入poweroff

gdb调试内核

qemu-system-x86_64 -kernel /home/yan/linux-5.4.1/arch/x86_64/boot/bzImage -initrd ../initramfs.img -smp 2  -S -s

先使用命令启动qemu。

kernel 是指定一个大内核文件,当仁不让的是bzImage。
initrd 是指定一个 initrd.img文件,这个文件就是我们使用busybox生成的initramfs.img。
smp 可以从名字猜想,它是给qemu指定几个处理器,或者是几个线程。
gdb则是启动qemu的内嵌gdbserver,监听的是本地tcp端口1234—如果这样写: -gdb tcp:192.168.1.100:1234 ,似乎也是没问题的。
S 就是挂起gdbserver,让gdb remote connect it。
s 默认使用1234端口进行远程调试,和-gdb tcp::1234类似。
m 2048 指定内存大小为2048M

qemu 挂载内核 qemu 调试内核_qemu 挂载内核_11

此时,开启另一个terminal,运行如下命令:

gdb /home/yan/linux-5.4.1/vmlinux (修改成自己的vmlinux路径)

target remote:1234 (默认端口是1234,进行远程连接)

b start_kernel (设置断点)

c (continue 运行到断点处)

qemu 挂载内核 qemu 调试内核_qemu 挂载内核_12

qemu 挂载内核 qemu 调试内核_qemu 挂载内核_13

可以发现这次失败了。因为断点没有停下来。

经过一番查阅:启动加上nokaslr

即:

qemu-system-x86_64 -kernel /home/yan/linux-5.4.1/arch/x86_64/boot/bzImage -initrd ../initramfs.img -smp 2  -S -s -append nokaslr

原因是kaslr开启随机地址,这样会导致 gdb 断点不能命中。加入nokaslr 参数,防止内核起始地址随机化,gdb断点就能找到了。

另外有说法老版本存在bug:打断点的时候使用hb start_kernel即可。不过我是关了随机地址可以,硬件断点也没用。

qemu 挂载内核 qemu 调试内核_ubuntu_14

这次成功啦!

继续实验:

(gdb) target remote:1234 
Remote debugging using :1234
0x000000000000fff0 in entry_stack_storage ()
(gdb) b register_filesystem
Breakpoint 1 at 0xffffffff812f8ff0: file fs/filesystems.c, line 73.
(gdb) c
Continuing.

Thread 1 hit Breakpoint 1, register_filesystem (
    fs=0xffffffff82720180 <sysfs_fs_type>) at fs/filesystems.c:73
73	{

可以看到内核此时停住了。gdb这下可以调试内核了!

qemu 挂载内核 qemu 调试内核_ubuntu_15

参考文献

https://blog.didiyun.com/index.php/2020/08/03/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8cgdb-qemu%E8%B0%83%E8%AF%95linux%E5%86%85%E6%A0%B8%E6%A8%A1%E5%9D%97/

https://www.ebpf.top/post/qemu_gdb_busybox_debug_kernel/