【强转】QEMU+GDB调试linux内核全过程

写在最前面

昨天更新了一篇名为《QEMU+GDB调试linux内核全过程》[link][https://blog.csdn.net/weixin_37867857/article/details/88138432]的博客,发现排版比较混乱,而且思维也比较混乱。咋一看下来简直是惨不忍睹,而且会给读者在安装过程中一种云里雾里的感觉,加上将近一个半月没有更新博客,导致对于博客的写作比较生疏了。所以决定重新更新一篇博客,以弥补昨天的遗憾。
为了更新本博客,于今天特意安装了一款虚拟机,重头开始指导并且总结怎么调试linux内核,不足之处敬请大家谅解。

1.需要下载的工具或者源码

使用qemu+gdb调试linux内核需要下载以下源码包或者软件包:

	1. ubuntu14.04;
	2. qemu;  
	3. linux内核,  版本2.6.32.20
	4. gcc,  版本4.4
	5. gdb, 版本7.9  
	6. busybox, 版本1.25.0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

下面先解释一下各个软件的作用:

1. 综述

1.1 ubuntu14.04

是我们需要编译Linux内核和各个调试软件运行的操作系统,建议虚拟机安装即可,物理机太浪费;虚拟机安装方法网上教程比较多,不在一一赘述。

1.2 qemu

是我们在虚拟机里面运行编译好的内核的镜像的一种方式,具体使用方法见以下使用。

1.3 linux内核

是我们主要调试的代码,安装gcc是为了编译linux内核,请注意,我们如果想在qemu里面调试特定版本的内核代码,一定要编译,编译好的镜像才有可能使用(为什么说有可能?是因为不稳定的内核代码有可能会产生panic)。

1.4 gdb

这个是我们主要的调试工具。

1.5 gcc

这个是我们主要的linux内核代码编译工具。

1.6 busybox

这个需要重点说明一下,是制作我们根文件系统的主要工具。什么是跟文件系统?
根文件系统首先是内核启动时所mount的第一个文件系统,内核代码映像文件保存在根文件系统中,而系统引导启动程序会在根文件系统挂载之后从中把一些基本的初始化脚本和服务等加载到内存中去运行。

2. 工具及源码下载

ubuntu14.04桌面版下载不需要赘述。网上很多。

2.1 qemu下载

sudo apt-get install qemu
  • 1

如果有提示输入密码,输入密码即可。

2.2 linux内核

内核下载网址为:
[link][https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/]
或者使用终端命令:

wget https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/linux-2.6.32.20.tar.gz
  • 1

2.3 gcc下载

由于ubuntu14.04默认安装gcc为4.8的版本,对于编译2.6.32.20的内核有点高,所以需要下载gcc源码或者更换gcc版本。

对于gcc的版本查看使用如下命令: 
  • 1
alex@ubuntu:~$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
...此处省略
gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

对于特定版本的gcc的安装如下:

sudo apt-get install gcc-4.4
ls /usr/bin -l | grep gcc #可以看到gcc是一个链接文件,连接到gcc-4.8版本;可以把本链接删除,重新链接gcc到gcc-4.4版本;
sudo rm -rf /usr/bin/gcc-4.8
sudo ln /usr/bin/gcc-4.4 /usr/bin/gcc
sudo gcc -v # 查看最终链接好的版本。
  • 1
  • 2
  • 3
  • 4
  • 5

最后链接好的gcc版本如下:

alex@ubuntu:~$ gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.4.7-8ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --disable-libmudflap --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.4.7 (Ubuntu/Linaro 4.4.7-8ubuntu1) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这样gcc就安装完成了。

2.4 . gdb源码下载

ubuntu原生的gdb有bug,需要下载gdb源码并且修改这个bug后才能使用,所以需要下载gdb源码,并把gdb事先删除掉,删除命令为sudo apt-get autoremove gdb等待删除完成即可。
gdb源码下载为7.9版本。源码下载命令为:

wget http://ftp.gnu.org/gnu/gdb/gdb-7.9.tar.gz
  • 1

2.5. busybox源码下载。

	wget https://busybox.net/downloads/busybox-1.25.0.tar.bz2
  • 1

3. 已经下载源码的编译

对于以上源码有些bug需要fix,有些可以直接编译使用。所以需要编译才能使用。
安装之前一定要下载以下库文件,否则编译gdb或者linux内核时候可能会失败。

	sudo apt-get install aptitude
	sudo aptitude install libncurses5-dev
	sudo apt-get install libssl-dev
  • 1
  • 2
  • 3

3.1 gdb的修改以及编译。

编译之前我们已经在2.4节中对于gdb已经remve掉,如果没有确定是否reove掉可以使用gdb -v命令查看,如果还有版本信息则说明没有remove掉,需要重新remove掉。
gdb源码下载解压之后,以下操作全部在gdb解压目录完成。
需要更改gdb/remote.c中如下源码:

if (buf_len > 2 * rsa->sizeof_g_packet)
error (_("Remote 'g' packet reply is too long: %s"), rs->buf);
  • 1
  • 2

为如下:

if (buf_len > 2 * rsa->sizeof_g_packet) {
    rsa->sizeof_g_packet = buf_len;
    for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
    {
        if (rsa->regs[i].pnum == -1)
            continue;
        if (rsa->regs[i].offset >= rsa->sizeof_g_packet)
            rsa->regs[i].in_g_packet = 0;
        else
            rsa->regs[i].in_g_packet = 1;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

对于此段的说明如下:修改gdb本地链接时候的栈区长度,把原先的错误判断更改为灵活栈区长度。对于gdb的能力不变。
运行如下命令配置,编译,安装gdb。

sudo ./configure 
sudo apt-get install libncurses5-dev ## 一定要安装此库,否则编译失败。
sudo make 
sudo make install
  • 1
  • 2
  • 3
  • 4

编译后的效果如下:
在这里插入图片描述
make install 命令之后效果:
在这里插入图片描述
验证:
gdb -v命令版本验证:

alex@ubuntu:~/Desktop/gdb-7.9$ gdb -v
GNU gdb (GDB) 7.9
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

gdb7.9版本安装完毕。

3.2 linux内核编译

内核安装配置,make menuconfig
选择:kernel hacking,将frame size由1024改成2048即可。
如下图:
在这里插入图片描述
在这里插入图片描述

内核编译:
make -j4 数字根据自己的物理内核来判断。
编译后的效果图:
在这里插入图片描述

3.3 busybox的编译

解压完busybox并进入解压目录之后完成makefile的配置文件如下:

make defconfig
make menuconfig 
  • 1
  • 2

make menuconfig 选择配置时候应勾选如下配置:
跟配置选择“Busybody settings”–>“Build Options”–>“Build BusyBox as a static binary (no shared libs)”
具体每一步配置如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最后使用make -j4命令编译busybox;
测试busy_box可以在编译好的busybox跟目录使用如下命令测试,可以使用./busybox ls,如果测试命令跟ls命令效果一致,则说明busybox编译OK,否则找我也行,找度娘也行。
测试busybox效果如下:
在这里插入图片描述
上图可以看到编译时候产生了一个错误,不过没有关系,一样可以编译是OK的,只要有ls的效果就说明编译OK。
然后使用make install安装busybox,可以看到busybox跟目录下有一个"_install"目录。这个目录对于以后我们制作根文件系统很重要。

4.根文件系统的制作以及测试

4.1 根文件系统的制作

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

 cd ..
 mkdir ramdisk
 cd ramdisk
 cp -r ../busy-1.25.0/_install/*  .
  • 1
  • 2
  • 3
  • 4

设置初始化进程init(建立一个软链接,一定不能直接复制过去)

cd ramdisk
ln -s bin/busybox init
  • 1
  • 2

设置开机启动程序

首先,我们需要先设定一些程序运行所需要的文件夹

mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin},dev}
  • 1

init程序首先会访问etc/inittab文件,因此,我们需要编写inittab,指定开机需要启动的所有程序

cd etc
vim inittab
  • 1
  • 2

inittab文件的内容如下所示:

::sysinit:/etc/init.d/rcS   
::askfirst:-/bin/sh    
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

赋予可执行权限

chmod +x inittab
  • 1

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

mkdir init.d
cd init.d
vim rcS
  • 1
  • 2
  • 3

rcS文件的内容如下所示:

#!/bin/sh

mount proc
mount -o remount,rw /
mount -a    
clear                               
echo "My Tiny Linux Starting, press enter to active"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

赋予可执行权限

chmod +x rcS
  • 1

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

cd ramdisk/etc/
vim fstab
  • 1
  • 2

要挂载临时文件系统,固定的文件名为fstab, fstab文件内容如下:

#/etc/fstab
proc            /proc        proc    defaults          0       0
sysfs           /sys         sysfs   defaults          0       0
devtmpfs        /dev         devtmpfs  defaults          0       0
  • 1
  • 2
  • 3
  • 4

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

cd ramdisk
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.img
  • 1
  • 2

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

4.2 根文件系统的测试

运行如下命令测试根文件系统

qemu-system-x86_64 -kernel ./linux-2.6.32.20/arch/x86_64/boot/bzImage -initrd ./initramfs.img -append "console=ttyS0" -nographic
  • 1

测试效果如下:

在这里插入图片描述
按"enter"键进入用户界面。
下面对于qemu命令的一个解释:

	qemu-system-x86_64 命令对于你编译器对应的CPU版本,启动,也可以制作虚拟磁盘,下文会介绍;
	-kernel参数对应编译好的内核镜像的位置。
	-initrd参数对应的是根文件系统的位置。
	-append "console=ttyS0"参数为内核启动后传入参数,启动哪个termnal. 
	-nographic	无界面启动,启动在ubuntu本地终端里面。
  • 1
  • 2
  • 3
  • 4
  • 5

4.3 启动总结。

在本章中我们使用了第一章中讨论的除了gdb之外的全部文件,这些文件对应了我们在ubuntu14.04中使用编译好的镜像和制作好的根文件系统中的技术细节。
下一章重点讨论启动磁盘以及gdb调试问题。

5 gdb调试。

在第四章中我们讨论了在根文件系统中启动内核镜像的问题,在本章中我们重点介绍GDB调试以及qemu在gdb调试中的一些注意事项问题。
在4.2节中我们掌握了如何启动内核的问题。其实qemu在gdb调试中只要增加两个参数就可以:-S -s两个参数,总体命令如下:

qemu-system-x86_64 -kernel ./linux-2.6.32.20/arch/x86_64/boot/bzImage -initrd ./initramfs.img -append "console=ttyS0" -nographic
  • 1

这时候我们看到的启动的内核是没有打印输出的,是因为需要gdb配合才能够有打印输出。我们的gdb该出场了。我们使用如下命令:

	gdb vmlinux 
	### 参数对应的是编译好的内核根目录下的vmlinux文件。
  • 1
  • 2

效果图如下:

在这里插入图片描述
在没有GDB调试的情况下QEMU带-S -s参数默认阻塞。
需要启动gdb,如下:
在这里插入图片描述
附图中我按了两次continue,是因为我第一次continue之前做了一次break start_kernel操作,如果想一下子启动到终端界面,可以直接按continue。
如果想打断点,则需要ctrl +c暂定gdb即可设置断点。

OK,至此qemu+gdb调试内核代码全部介绍完毕,下一篇我们会介绍内核块设备驱动原理,届时会使用gdb调试内核代码。敬请期待。
<完>

posted @ 2021-01-24 14:20  方东信  阅读(1409)  评论(0编辑  收藏  举报