文章目录
- 编译内核
- 下载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"勾选:
(以上是别人博客中找到,确实类似gcc编译要gdb得加-g,但是我按照默认安装最后也默认了允许调试,算是现在新版本的一个福音吧。不然就重新再编译将近一小时吧(不过不知道打了ccache
这下要跑多久,闲了可以测试测试))
先检查自己之前编译的内核有没有该信息。
# grep CONFIG_DEBUG_INFO .config
CONFIG_DEBUG_INFO=y
下载qemu
qemu有两种安装方式,第一种是使用命令行直接下载安装,第二种是通过源码进行编译安装,推荐命令行直接下载。
开始直接编译到最后安装的时候要Nanja
但是我的源下不了这个。
所以我采用了直接安装qemu
sudo apt-get install qemu
直接安装也是最方便省事的,依赖文件现在也打包好了似乎。
构建initramfs根文件系统
1.编译Busybox
- https://busybox.net/ 下载并解压源码
wget https://busybox.net/downloads/busybox-1.34.0.tar.bz2
首先安装静态依赖,否则会有报错。
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
看上面的英文是按y
表示确认,确认后[]
内有*
退出,提示保存,选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/* .
设置初始化进程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
直接把这个文件权限拉满
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
至此,我们已经完成了RAM Disk中相关文件的配置,可以压缩生成文件镜像了。
cd ramdisk
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.img
最后生成的initramfs.img
就是我们的根文件系统。
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
检查是否挂载成功。
关机输入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
此时,开启另一个terminal,运行如下命令:
gdb /home/yan/linux-5.4.1/vmlinux (修改成自己的vmlinux路径)
target remote:1234 (默认端口是1234,进行远程连接)
b start_kernel (设置断点)
c (continue 运行到断点处)
可以发现这次失败了。因为断点没有停下来。
经过一番查阅:启动加上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
即可。不过我是关了随机地址可以,硬件断点也没用。
这次成功啦!
继续实验:
(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这下可以调试内核了!
参考文献
https://www.ebpf.top/post/qemu_gdb_busybox_debug_kernel/