QEMU使用和调试¶
本文档主要用于介绍如何获得qemu的二进制,qemu的运行,以及如何基于qemu进行一些简单的调试。
qemu使用¶
下面介绍如何在自己的机器上编译并运行一个linux镜像。
获取二进制¶
获取qemu的二进制可以通过如下方式:
基于openEuler社区 qemu 代码自行编译:
首先下载对应的代码并切换到stable-5.0分支:
git clone https://gitee.com/openeuler/qemu.git qemu cd qemu git checkout -b stable-5.0 remotes/origin/stable-5.0
编译生成对应的二进制:
./configure --target-list=arm-softmmu,aarch64-softmmu --disable-werror make -j 8 make install #调试不需要
编译完成后会生成arm-softmmu/qemu-system-arm、aarch64-softmmu/qemu-system-aarch64两个文件。
Note
configure执行过程中,可能会有诸如”glib-2.48 gthread-2.0 is required to compile QEMU“的失败打印,请按照提示自行安装升级对应的软件包。
configure时可以通过不同的参数来enable/disable一些qemu的特性或编译选项,如示例中增加的–disable-werror可以允许编译warning;如想要体验openEuler Embedded共享文件系统场景,需要在configure时增加–enable-virtfs来使能对应功能。
openEuler Embedded镜像¶
参照 快速上手 部分。
qemu运行¶
一个简单的qemu执行命令如下:
qemu-system-aarch64 -M virt-4.0 -m 1G -cpu cortex-a57 -nographic -kernel zImage -initrd initrd
执行之后等待OS加载完成,很快就能看到登陆提示:
Authorized uses only. All activity may be monitored and reported.
openeuler login:
这也意味你已经成功在机器上启动了openEuler Embedded的系统。
这里也介绍一些常用的qemu启动参数:
-M virt: 指定需要使用的machine类型,virt是qemu提供的一个通用machine,可以同时支持arm32和arm64(部分cortex不支持),
-M help
可以列出所有支持的machine列表-m 1G: 可选,可以通过修改此参数来增大OS的可用内存
-cpu cortex-a57: 指定模拟的cpu类型,指定
-M
的情况下可以使用-cpu help
查看当前machine支持的cpu类型-smp 2: 可选,可以修改OS的cpu数量,默认为1
-append: 可选,指定内核的启动参数(cmdline)
-kernel、-initrd: 分别用于指定OS的内核和文件系统
-dtb: 可选,用于指定dtb(device tree)文件
-d in_asm -D qemu.log: 可选,输出qemu在tcg模式下的”指令流”。
-d
选择指令流类型,可以用-d help
查看支持的选项列表;-D
指定输出的文件名-s -S: 可选,调试参数。
-S
可以让qemu加载OS的zImage、initrd到指定位置后停止运行,等待gdb连接;-s
等价于--gdb tcp::1234
,启动gdb server并默认监听1234端口-serial: 可选,用于串口重定向。不指定时默认为
-serial stdio
,即打印到标准输入输出。也可以重定向到tcp:-serial tcp::1111,server,nowait
,通过telnet localhost 1111
连接
内核调试¶
qemu的另一大优势便是可以使用gdb来对内核进行调试,这对于嵌入式开发者来说能极大的提高开发效率。 在原有的开发环境中,如果想调试内核,只能在出问题的代码附近加上printk打印,重编内核,将镜像烧到开发板上查看打印信息,如果出问题的阶段非常早,无法调用printk,还需要手动实现往串口物理地址打印的代码。
而有了gdb之后,可以直接在内核需要调试的位置下断点,查看对应的寄存器和变量的值。
调试准备¶
除了上面使用部分需要的东西之外,我们还需要一个vmlinux文件,一般在编译linux内核后在内核的根目录下就能找到。可以自行从openEuler社区下载linux的源码并编译生成。另外请确保vmlinux和zImage是由同一份内核源码和同一份内核config生成。
实例¶
以aarch64为例,介绍如何使用qemu进行内核调试。
Note
调试自解压部分时,需要使用arch/arm64/boot/compressed/vmlinux,并在gdb加载vmlinux时,使用
(gdb) add-symbol-file vmlinux 0x40080000
的方式来加载符号信息,0x40080000对应的地址实际上就是qemu加载zImage的位置,可以在qemu的控制台通过 info roms
查看
在终端执行如下命令:
qemu-system-aarch64 -M virt-4.0 -m 1G -cpu cortex-a57 -nographic -kernel zImage -initrd initrd "-S -s"
可以发现此时命令行不再继续往下执行,我们另外打开一个窗口,启动gdb并连接qemu:
gdb
(gdb) file vmlinux
(gdb) target remote :1234
(gdb) b start_kernel
(gdb) c
上面的 target remote :1234
用于连接qemu启动的gdb server; file vmlinux
用于加载符号信息。
在执行完 c
之后,内核会开始运行,遇到我们在start_kernel下的断点后会再次停止,此时可以通过 p / bt
等方式查看变量或调用栈。
一些调试内核时常用的gdb命令:
p: 打印通用寄存器或者变量。
p $x1
或者p command_line
x/32wx addr: 以16进制,按word(32位)为单位,打印从addr开始的32个值
disas [addr]: 反汇编,可以结合qemu.log和objdump后的vmlinux一起查看
n, s / ni, si: 单步执行,ni,si 针对汇编
info registers: 打印寄存器
bt: 查看调用栈
b [addr] [if condition]: 断点,某些场景下可能需要条件断点来过滤部分(如想查看某个中断是否上报可以在中断入口处增加调试断点,减少非预期的停止)