300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 利用QEMU+GDB搭建Linux内核调试环境

利用QEMU+GDB搭建Linux内核调试环境

时间:2018-10-26 18:05:34

相关推荐

利用QEMU+GDB搭建Linux内核调试环境

前言

对用户态进程,利用gdb调试代码是很方便的手段。而对于内核态的问题,可以利用crash等工具基于coredump文件进行调试。

其实我们也可以利用一些手段对Linux内核代码进行gdb调试,qemu就是一种。

qemu是一款完全软件模拟(Binary translation)的虚拟化软件,在虚拟化的实现中性能相对较差。但利用它在测试环境中gdb调试Linux内核代码,是熟悉Linux内核代码的一个好方法。

本文实验环境:

ubuntu 20.04busybox-1.32.1Linux kernel 4.9.3QEMUGDB 10.1

编译内核源码

git clone git:///pub/scm/linux/kernel/git/stable/linux.gittar -xvzf linux-4.9.301.tar.gzcd linux-4.9.301make menuconfig

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

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

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

以上配置完成后会在当前目录生成.config文件,我们可以使用grep进行验证:

grep CONFIG_DEBUG_INFO .configCONFIG_DEBUG_INFO=y

编译内核

make bzImage -j4

编译完成后,会在当前目录下生成vmlinux,这个在 gdb 的时候需要加载,用于读取 symbol 符号信息,包含了所有调试信息,所以比较大。

压缩后的镜像文件为bzImage, 在arch/x86/boot/目录下。

➜ linux-4.9.301 ls -hl vmlinux-rwxrwxr-x 1 ubuntu ubuntu 578M Apr 15 08:14 vmlinux➜ linux-4.9.301 ls -hl ./arch/x86_64/boot/bzImagelrwxrwxrwx 1 ubuntu ubuntu 22 Apr 15 08:15 ./arch/x86_64/boot/bzImage -> ../../x86/boot/bzImage➜ linux-4.9.301 ls -hl ./arch/x86/boot/bzImage -rw-rw-r-- 1 ubuntu ubuntu 9.3M Apr 15 08:15 ./arch/x86/boot/bzImage

几种linux内核文件的区别:

vmlinux 编译出来的最原始的内核文件,未压缩。

zImage 是vmlinux经过gzip压缩后的文件。

bzImage bz表示“big zImage”,不是用bzip2压缩的。两者的不同之处在于,zImage解压缩内核到低端内存(第一个640K)。

bzImage解压缩内核到高端内 存(1M以上)。如果内核比较小,那么采用zImage或bzImage都行,如果比较大应该用bzImage。

uImage U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的tag。

vmlinuz 是bzImage/zImage文件的拷贝或指向bzImage/zImage的链接。

initrd 是“initial ramdisk”的简写。一般被用来临时的引导硬件到实际内核vmlinuz能够接管并继续引导的状态。

编译busybox

Linux系统启动阶段,boot loader加载完内核文件vmlinuz后,内核紧接着需要挂载磁盘根文件系统,但如果此时内核没有相应驱动,无法识别磁盘,就需要先加载驱动。

而驱动又位于/lib/modules,得挂载根文件系统才能读取,这就陷入了一个两难境地,系统无法顺利启动。

于是有了initramfs根文件系统,其中包含必要的设备驱动和工具,bootloader加载initramfs到内存中,内核会将其挂载到根目录/,然后运行/init脚本,挂载真正的磁盘根文件系统。

这里借助BusyBox构建极简initramfs,提供基本的用户态可执行程序。

可以从busybox官网地址下载最新版本,或者直接使用wget下载我使用的版本。

wget /downloads/busybox-1.32.1.tar.bz2$ tar -xvf busybox-1.32.1.tar.bz2$ cd busybox-1.32.1/$ make menuconfig

在编译busybox之前,我们需要对其进行设置,执行make menuconfig,如下

这里一定要选择静态编译,编译好的可执行文件busybox不依赖动态链接库,可以独立运行,方便构建initramfs。

之后选择Exit退出,到这里我们就可以编译busybox了,执行下面的命令

make -j 8# 安装完成后生成的相关文件会在 _install 目录下make && make install

构建initramfs根文件系统

[root@localhost temp]# lsbusybox-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.32.1/_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]# lsbin dev init proc sbin sys usr

其中init的内容如下

#!/bin/busybox shecho "{==DBG==} INIT SCRIPT"mount -t proc none /procmount -t sysfs none /sysecho -e "{==DBG==} Boot took $(cut -d' ' -f1 /proc/uptime) seconds"exec /sbin/init

打包initramfs

find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz[root@localhost initramfs]# ls ../busybox-1.29.0 busybox-1.29.0.tar.bz2 initramfs initramfs.cpio.gz

安装QEMU

apt install qemu qemu-utils qemu-kvm virt-manager libvirt-daemon-system libvirt-clients bridge-utils

安装GDB

wget /gnu/gdb/gdb-10.1.tar.gztar -xzvf gdb-10.1.tar.gzcd gdb-10.1./configure# 必需要安装这两个库sudo apt-get install texinfosudo apt-get install build-essentialmake -j 8sudo make install

QEMU启动调试内核

➜ linux-4.9.301 qemu-system-x86_64 -kernel ./arch/x86/boot/bzImage -initrd ../initramfs.cpio.gz -append "nokaslr console=ttyS0" -s -S -nographic

-kernel ./arch/x86/boot/bzImage:指定启用的内核镜像;-initrd ../initramfs.cpio.gz:指定启动的内存文件系统;-append "nokaslr console=ttyS0":附加参数,其中nokaslr参数必须添加进来,防止内核起始地址随机化,这样会导致 gdb 断点不能命中;-s:监听在 gdb 1234 端口;-S:表示启动后就挂起,等待 gdb 连接;-nographic:不启动图形界面,调试信息输出到终端与参数console=ttyS0组合使用;

在另一个窗口中,输入gdb,即可开启调试。

(gdb) target remote localhost:1234Remote debugging using localhost:1234warning: Can not parse XML target description; XML support was disabled at compile timeRemote 'g' packet reply is too long (expected 560 bytes, got 608 bytes): 0000000000000000000000000000000000000000000000006306000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ff0000000000000200000000f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000801f0000(gdb) Remote debugging using localhost:1234Undefined command: "Remote". Try "help".(gdb) warning: Can not parse XML target description; XML support was disabled at compile timeQuit

但是,在启动GDP调试时报错了,在查阅了诸多资料后,很多博客都给出了修复方法:源码重新安装gdb,并修改gdb/remote.c文件的一段代码。但是我尝试了,发现行不通。

出现该问题的原因是:编译 的是64 位模式的内核代码,但是运行是在 32 位保护模式下。64 位代码将无法在该环境中正常运行。

终于在stackflow上找到了修复方法:具体可以参考下面两篇文章。

/questions/48620622/how-to-solve-qemu-gdb-debug-error-remote-g-packet-reply-is-too-long

/QEMU_and_GDB_in_long_mode

文章中给出了三种修复方法,我这里只列出了一种,即修改GDB源码,重新编译安装。

--- gdb/remote.c -04-14 11:13:49.962628700 +0300+++ gdb/remote.c-04-14 11:15:38.257783400 +0300@@ -7181,8 +7181,28 @@buf_len = strlen (rs->buf);/* Further sanity checks, with knowledge of the architecture. */+// HACKFIX for changing architectures for qemu. It's ugly. Don't use, unless you have to.+ // Just a tiny modification of the patch of Matias Vara (/viewtopic.php?f=13&p=177644)if (buf_len > 2 * rsa->sizeof_g_packet)- error (_("Remote 'g' packet reply is too long: %s"), rs->buf);+ {+warning (_("Assuming long-mode change. [Remote 'g' packet reply is too long: %s]"), rs->buf);+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;+ }++// HACKFIX: Make sure at least the lower half of EIP is set correctly, so the proper+// breakpoint is recognized (and triggered).+rsa->regs[8].offset = 16*8;+ }/* Save the size of the packet sent to us by the target. It is usedas a heuristic when determining the max size of packets that the

cd gdb-10.1./configuremake -j 8sudo make install

接着就可以敲gdb 启动调试。

➜ linux-4.9.301 gdbGNU gdb (GDB) 10.1Copyright (C) Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later </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-pc-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:</software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:</software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word".(gdb) file vmlinuxReading symbols from vmlinux...(gdb) target remote localhost:1234Remote debugging using localhost:1234warning: Can not parse XML target description; XML support was disabled at compile timewarning: Assuming long-mode change. [Remote 'g' packet reply is too long: PU]0x000000000000fff0 in exception_stacks ()(gdb) break start_kernelBreakpoint 1 at 0xffffffff81fc6a95: file init/main.c, line 486.(gdb) break rest_initBreakpoint 2 at 0xffffffff818aa1e1: file init/main.c, line 385.(gdb) cContinuing.Breakpoint 1, start_kernel () at init/main.c:486486 set_task_stack_end_magic(&init_task);(gdb) cContinuing.Breakpoint 2, rest_init () at init/main.c:385385{(gdb)

在start_kernel 和 rest_init 打了两个断点, 两个断点都成功命中了。

本文参考

/A/kjdw2a2q5N/

/developer/article/1793157

/alexanderwang7/article/details/113180447

/sjc2870/article/details/12247

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。