文章目录
一直想搭建一个debug kernel的环境,这样的话就可以对kernel启动的各个部分,运行过程中的特定部分进行定位,跟踪,调试,尤其是启动阶段。如果没有debug kernel的环境,那么我们只有使用其他方式来进行调试,例如printk。当然还有kgdb,不过此次不讨论此方式。
下面我们来进行详细的环境搭建。
1 busybox软件
1.1 busybox下载
可以从busybox官网地址下载最新版本,或者直接使用wget下载我使用的版本
[root@localhost ~]# wget https://www.busybox.net/downloads/busybox-1.29.0.tar.bz2
命令执行效果如下:
1.2 busybox编译安装
在编译busybox之前,我们需要对其进行设置,如下所示
首先对压缩包进行解压,之后执行make menuconfig,如下
[root@localhost temp]# tar jxvf busybox-1.29.0.tar.bz2
............
[root@localhost busybox-1.29.0]# make menuconfig
之后选择Exit退出,到这里我们就可以编译busybox了,执行下面的命令
[root@localhost busybox-1.29.0]# make -j 4
..............
[root@localhost busybox-1.29.0]# make install
./_install//bin/arch -> busybox
./_install//bin/ash -> busybox
./_install//bin/base64 -> busybox
....................
....................
./_install//usr/sbin/ubirsvol -> ../../bin/busybox
./_install//usr/sbin/ubiupdatevol -> ../../bin/busybox
./_install//usr/sbin/udhcpd -> ../../bin/busybox
--------------------------------------------------
You will probably need to make your busybox binary
setuid root to ensure all configured applets will
work properly.
--------------------------------------------------
[root@localhost busybox-1.29.0]# ls -l _install/
total 8
drwxr-xr-x 2 root root 4096 Jan 26 20:02 bin
lrwxrwxrwx 1 root root 11 Jan 26 20:02 linuxrc -> bin/busybox
drwxr-xr-x 2 root root 4096 Jan 26 20:02 sbin
drwxr-xr-x 4 root root 27 Jan 26 20:02 usr
2 构建initramfs根文件系统
创建initramfs,其中包含BusyBox可执行程序、必要的设备文件、启动脚本init。这里没有内核模块,如果需要调试内核模块,可将需要的内核模块包含进来。init脚本只挂载了虚拟文件系统procfs和sysfs,没有挂载磁盘根文件系统,所有调试操作都在内存中进行,不会落磁盘。
[root@localhost temp]# ls
busybox-1.29.0 busybox-1.29.0.tar.bz2
[root@localhost temp]# mkdir initramfs
[root@localhost temp]# cd initramfs
[root@localhost initramfs]# cp ../busybox-1.29.0/_install/* -rf ./
[root@localhost initramfs]# mkdir dev proc sys
[root@localhost initramfs]# sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/
[root@localhost initramfs]# rm -f linuxrc
[root@localhost initramfs]# vim init
[root@localhost initramfs]# chmod a+x init
[root@localhost initramfs]# ls
bin dev init proc sbin sys usr
其中init的内容如下
#!/bin/busybox sh
mount -t proc none /proc
mount -t sysfs none /sys
exec /sbin/init
打包initramfs:
[root@localhost initramfs]# find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz
.
./bin
./bin/busybox
./bin/arch
............
............
./dev/tty3
./dev/tty4
./proc
./sys
./init
5285 blocks
[root@localhost initramfs]# ls ../
busybox-1.29.0 busybox-1.29.0.tar.bz2 initramfs initramfs.cpio.gz
3 kernel代码下载及编译
3.1 kernel代码下载
可以从kernel官网下载需要的kernel版本,我这里使用的是linux-4.14.95版本。执行下面的命令下载即可
[root@localhost temp]# wget https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.14.95.tar.gz
--2021-01-26 20:21:12-- https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.14.95.tar.gz
Resolving mirrors.edge.kernel.org (mirrors.edge.kernel.org)... 147.75.95.133, 2604:1380:3000:1500::1
Connecting to mirrors.edge.kernel.org (mirrors.edge.kernel.org)|147.75.95.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 156176622 (149M) [application/x-gzip]
Saving to: ‘linux-4.14.95.tar.gz’
100%[====================================================================================================================>] 156,176,622 12.2MB/s in 16s
2021-01-26 20:21:29 (9.30 MB/s) - ‘linux-4.14.95.tar.gz’ saved [156176622/156176622]
3.2 kernel生成需要的.config文件
之后就是tar包的解压,完成后进入目录执行make menuconfig,如下
[root@localhost temp]# tar -zxf linux-4.14.95.tar.gz
[root@localhost temp]# cd linux-4.14.95/
[root@localhost linux-4.14.95]# ls
arch certs CREDITS Documentation firmware include ipc Kconfig lib Makefile net samples security tools virt
block COPYING crypto drivers fs init Kbuild kernel MAINTAINERS mm README scripts sound usr
[root@localhost linux-4.14.95]# make menuconfig
我们需要选择开启两个选项:Compile the kernel with debug info 和 Provide GDB scripts for kernel debugging。
Compile the kernel with debug info设置路径:
Provide GDB scripts for kernel debugging设置路径:
3.3 编译kernel代码
[root@localhost linux-4.14.95]# make -j4
[root@localhost linux-4.14.95]# make modules
[root@localhost linux-4.14.95]# make modules_install
[root@localhost linux-4.14.95]# make install
4 调试
4.1QEMU程序的命令行
#!/bin/bash
KERNEL_MIRROR=kernel_source/linux-4.14.95/arch/x86_64/boot/bzImage
/usr/local/bin/qemu-system-x86_64 \
-s \
-smp 2 \
-kernel $KERNEL_MIRROR \
-initrd initramfs.cpio.gz \
-nographic \
-append "console=ttyS0 nokaslr"
- -s是-gdb tcp::1234缩写,监听1234端口,在GDB中可以通过target remote localhost:1234连接
- -smp 设置多个CPU
- -kernel指定编译好的调试版内核;
- -initrd指定制作的initramfs;
- -nographic取消图形输出窗口,使QEMU成简单的命令行程序;
- -append "console=ttyS0"将输出重定向到console,将会显示在标准输出stdio。
4.2 启动gdb
我们需要使用源码编译出来的vmlinux,此文件在kernel源代码的root目录,我们需要到此目录执行gdb vmlinux命令
[root@localhost kernel_source]# cd linux-4.14.95/
[root@localhost linux-4.14.95]# ls
arch certs crypto firmware include Kbuild lib mm Module.symvers samples sound usr vmlinux-gdb.py
block COPYING Documentation fs init Kconfig MAINTAINERS modules.builtin net scripts System.map virt vmlinux.o
built-in.o CREDITS drivers gdb.debug ipc kernel Makefile modules.order README security tools vmlinux
[root@localhost linux-4.14.95]# gdb vmlinux
启动gdb后可以使用下面的命令来连接之前的qemu vm,成功后执行bt,如下
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0xffffffff81734122 in native_safe_halt () at ./arch/x86/include/asm/irqflags.h:57
57 asm volatile("sti; hlt": : :"memory");
(gdb) bt
#0 0xffffffff81734122 in native_safe_halt () at ./arch/x86/include/asm/irqflags.h:57
#1 0xffffffff81733df8 in arch_safe_halt () at ./arch/x86/include/asm/paravirt.h:94
#2 default_idle () at arch/x86/kernel/process.c:564
#3 0xffffffff810ce286 in cpuidle_idle_call () at kernel/sched/idle.c:156
#4 do_idle () at kernel/sched/idle.c:246
#5 0xffffffff810ce4ef in cpu_startup_entry (state=CPUHP_ONLINE) at kernel/sched/idle.c:351
#6 0xffffffff81726d56 in rest_init () at init/main.c:435
#7 0xffffffff8252711b in start_kernel () at init/main.c:710
#8 0xffffffff810000d5 in secondary_startup_64 () at arch/x86/kernel/head_64.S:240
#9 0x0000000000000000 in ?? ()
(gdb) b do_IRQ
Breakpoint 1 at 0xffffffff81802620: file arch/x86/kernel/irq.c, line 217.
(gdb) c
Continuing.
Breakpoint 1, do_IRQ (regs=0xffffffff82003de8 <init_thread_union+15848>) at arch/x86/kernel/irq.c:217
217 {
(gdb) bt
#0 do_IRQ (regs=0xffffffff82003de8 <init_thread_union+15848>) at arch/x86/kernel/irq.c:217
#1 0xffffffff81800947 in common_interrupt () at arch/x86/entry/entry_64.S:571
#2 0xffffffff82003de8 in init_thread_union ()
#3 0x0000000000000000 in ?? ()
至此环境搭建完毕。
参考文档
1 使用QEMU和GDB调试Linux内核
2 Linux内核调试 之 qemu+gdb
3 2020-02-27-Linux内核0-使用QEMU和GDB调试Linux内核