使用QEMU虚拟机调试Linux内核

    作为一名初级的嵌入式底层开发人员,若想要深入学习Linux内核,需要谨慎地选择开发环境和开发工具。与应用层开发不同的是,Linux内核的学习和开发调试通常是比较困难的,原因之一在于可用的Linux内核调试工具比较少。很久以前笔者注意到安卓工程中使用到了QEMU虚拟机模拟了安卓硬件设备,可以完整地运行安卓系统(从物理按键和显示、触摸屏,到Linux内核,直至Framework层)。还有QEMU支持与GDB建立连接,可以通过常用的调试手段加深对Linux内核的理解。当然,现在关于QEMU虚拟机运行、调试Linux内核的文章也很多,笔者在这里简要地增加一例,忝列门墙,不胜惶悚。

    笔者使用的操作系统为ubuntu 16.04,系统官方提供的QEMU版本相对较低,于是使用以下配置参数来编译qemu的3.1.0版本。如果执行configure的过程中出错,请使用apt安装出错信息指出缺少的库;当configure成功返回后,即可执行make && make install完成qemu的编译和安装:

#!/bin/bash

./configure '--target-list=aarch64-softmmu,aarch64-linux-user,arm-softmmu,arm-linux-user' \
    '--cc=/usr/bin/gcc-5' '--cxx=/usr/bin/g++-5' '--extra-cflags=-O2' '--extra-cxxflags=-O2' \
    --disable-strip --disable-sdl --enable-gnutls --enable-virtfs --enable-curl --enable-fdt \
    --enable-tools --enable-linux-aio --enable-cap-ng --enable-attr --enable-spice \
    --enable-libiscsi --enable-libnfs --enable-libusb --enable-usb-redir --enable-libssh2 \
    --enable-lzo --enable-snappy --enable-seccomp --enable-coroutine-pool --enable-glusterfs \
    --enable-libxml2 --enable-replication --prefix=/opt/qemu3 # --enable-bzip2 --enable-vde --enable-rbd

    接下来需要编译Linux内核。下载最新版本的Linux内核源码,笔者通过该URL下载了v5.2.4版本的Linux内核源码:http://mirrors.ustc.edu.cn/kernel.org/linux/kernel/v5.x/linux-5.2.4.tar.xz 。下载后解压Linux内核源码,并修改arch/arm/configs/vexpress_defconfig,在CONFIG_DEVTMPFS=y下面加入一行配置:CONFIG_DEVTMPFS_MOUNT=y。之后,依次执行:

    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vexpress_defconfig
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8 zImage dtbs

    这样就能够编译得到QEMU虚拟机需要的两个文件,路径分别为:

  Kernel: arch/arm/boot/Image is ready
  OBJCOPY arch/arm/boot/zImage
  Kernel: arch/arm/boot/zImage is ready
  DTC     arch/arm/boot/dts/vexpress-v2p-ca5s.dtb
  DTC     arch/arm/boot/dts/vexpress-v2p-ca9.dtb
  DTC     arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dtb
  DTC     arch/arm/boot/dts/vexpress-v2p-ca15_a7.dtb

    运行Linux内核依赖一个完整的根文件系统,笔者已提供了一个使用buildroot生成的根文件系统rootfs.ext2.xz(实际上,下面运行QEMU的简单脚本也源自于buildroot),可以在笔者的资源下载区找到。下载后解压得到rootfs.ext2,可以将rootfs.ext2重命名为rootfs.ext4。随后,将zImage/vexpress-v2p-ca9.dtb/rootfs.ext2放置于同一目录下,使用下面的脚本运行qemu虚拟机:

yejq@ubuntu:~/program/arm-qemu$ cat run-vm.sh 
#!/bin/bash

export PATH="/opt/qemu3/bin:$PATH"

exec qemu-system-arm -M vexpress-a9 -smp 1 -m 256 -kernel zImage \
	-dtb vexpress-v2p-ca9.dtb -drive 'file=rootfs.ext4,if=sd,format=raw' \
	-append 'console=ttyAMA0,115200 root=/dev/mmcblk0' -serial stdio \
	-net 'nic,model=lan9118' -net user -gdb 'tcp::2019'

yejq@ubuntu:~/program/arm-qemu$ ./run-vm.sh
audio: Could not init `oss' audio driver
VNC server running on 127.0.0.1:5900
Booting Linux on physical CPU 0x0
Linux version 5.2.4+ (yejq@ubuntu) (gcc version 6.4.1 20180425 [linaro-6.4-2018.05 revision 7b15d0869c096fe39603ad63dc19ab7cf035eb70] (Linaro GCC 6.4-2018.05)) #1 SMP Sat Aug 3 23:17:25 CST 2019
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
OF: fdt: Machine model: V2P-CA9
Memory policy: Data cache writeback
Reserved memory: created DMA memory pool at 0x4c000000, size 8 MiB
OF: reserved mem: initialized node vram@4c000000, compatible id shared-dma-pool
cma: Reserved 16 MiB at 0x6f000000
CPU: All CPU(s) started in SVC mode.
percpu: Embedded 20 pages/cpu s49484 r8192 d24244 u81920
Built 1 zonelists, mobility grouping on.  Total pages: 65024
Kernel command line: console=ttyAMA0,115200 root=/dev/mmcblk0

    内核加载完成后,会提示豋录,用户名为root,密码为123456:

Starting logging: OK
Initializing random number generator... done.
Starting network: OK
Starting telnetd: OK

Welcome to Buildroot
buildroot login: root
Password: 
# ls
# uname -a
Linux buildroot 5.2.4+ #1 SMP Sat Aug 3 23:17:25 CST 2019 armv7l GNU/Linux
# 

    最后,使用gdb调试器与QEMU建立连接,这样就可以像调试普通应用一样调试Linux内核了:

yejq@ubuntu:~/program/linux-5.2.4$ arm-linux-gnueabihf-gdb ./vmlinux
GNU gdb (Linaro_GDB-2018.05) 8.1.0.20180611-git
Copyright (C) 2018 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 "--host=x86_64-unknown-linux-gnu --target=arm-linux-gnueabihf".
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"...
Reading symbols from ./vmlinux...done.
(gdb) target remote localhost:2019
Remote debugging using localhost:2019
cpu_v7_do_idle () at arch/arm/mm/proc-v7.S:78
78		ret	lr
(gdb) bt
#0  cpu_v7_do_idle () at arch/arm/mm/proc-v7.S:78
#1  0x80108fb0 in arch_cpu_idle () at arch/arm/kernel/process.c:71
#2  0x8014e6d8 in cpuidle_idle_call () at kernel/sched/idle.c:154
#3  do_idle () at kernel/sched/idle.c:263
#4  0x8014ea38 in cpu_startup_entry (state=CPUHP_ONLINE) at kernel/sched/idle.c:354
#5  0x8072fc70 in rest_init () at init/main.c:451
#6  0x80a00a24 in arch_call_rest_init () at init/main.c:548
#7  0x80a00ea0 in start_kernel () at init/main.c:761
#8  0x00000000 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章