已经是会员了,怎么提出文字首字母
903
2022-05-28
前言
对用户态进程,利用gdb调试代码是很方便的手段。而对于内核态的问题,可以利用crash等工具基于coredump文件进行调试。
其实我们也可以利用一些手段对Linux内核代码进行gdb调试,qemu就是一种。
qemu是一款完全软件模拟(Binary translation)的虚拟化软件,在虚拟化的实现中性能相对较差。但利用它在测试环境中gdb调试Linux内核代码,是熟悉Linux内核代码的一个好方法。
本文实验环境:
ubuntu 20.04
busybox-1.32.1
Linux kernel 4.9.3
QEMU
GDB 10.1
编译内核源码
git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git tar -xvzf linux-4.9.301.tar.gz cd linux-4.9.301 make 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 .config CONFIG_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/bzImage lrwxrwxrwx 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 https://busybox.net/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]# 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 echo "{==DBG==} INIT SCRIPT" mount -t proc none /proc mount -t sysfs none /sys echo -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 https://ftp.gnu.org/gnu/gdb/gdb-10.1.tar.gz tar -xzvf gdb-10.1.tar.gz cd gdb-10.1 ./configure # 必需要安装这两个库 sudo apt-get install texinfo sudo apt-get install build-essential make -j 8 sudo 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:1234 Remote debugging using localhost:1234 warning: Can not parse XML target description; XML support was disabled at compile time Remote 'g' packet reply is too long (expected 560 bytes, got 608 bytes): 0000000000000000000000000000000000000000000000006306000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ff0000000000000200000000f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000801f0000 (gdb) Remote debugging using localhost:1234 Undefined 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上找到了修复方法:具体可以参考下面两篇文章。
https://stackoverflow.com/questions/48620622/how-to-solve-qemu-gdb-debug-error-remote-g-packet-reply-is-too-long
https://wiki.osdev.org/QEMU_and_GDB_in_long_mode
文章中给出了三种修复方法,我这里只列出了一种,即修改GDB源码,重新编译安装。
--- gdb/remote.c 2016-04-14 11:13:49.962628700 +0300 +++ gdb/remote.c 2016-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 (http://forum.osdev.org/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 used as a heuristic when determining the max size of packets that the
cd gdb-10.1 ./configure make -j 8 sudo make install
接着就可以敲gdb 启动调试。
➜ linux-4.9.301 gdb GNU gdb (GDB) 10.1 Copyright (C) 2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later
在start_kernel 和 rest_init 打了两个断点, 两个断点都成功命中了。
本文参考
https://www.shuzhiduo.com/A/kjdw2a2q5N/
https://cloud.tencent.com/developer/article/1793157
https://blog.csdn.net/alexanderwang7/article/details/113180447
https://blog.csdn.net/sjc2870/article/details/122017247
https://stackoverflow.com/questions/8662468/remote-g-packet-reply-is-too-long
https://stackoverflow.com/questions/4943857/linux-kernel-live-debugging-how-its-done-and-what-tools-are-used/42316607#42316607
ARM C 语言 Linux 嵌入式 虚拟化
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。