2. QEMU源码编译与安装:从源码编译QEMU、配置目标架构(ARM/RISC-V)、常见编译错误排查

说实话,很多搞嵌入式开发的朋友,一开始都习惯直接 apt install qemu-system-arm 完事。但做汽车电子调试,尤其是要对接硬件寄存器级的行为时,预编译的 QEMU 往往不够用。你得自己编译,才能打开那些调试开关,才能定制目标板。

这一章,我就带你走一遍完整的编译流程。从拉源码到跑起第一个 ARM 虚拟机,中间那些坑,我一个一个指给你看。

2.1 为什么非要自己编译?

预编译包和源码编译,差距其实挺大的。我列个表,你一看就明白:

对比项 预编译包(apt/pacman) 源码编译
调试支持 通常不带 --enable-debug 可开启 GDB 单步跟踪 QEMU 内部
目标架构 固定几个主流架构 可精确指定 ARMv7、RISC-V 64 等变体
性能优化 通用优化,不针对你的 CPU 可加 -march=native 榨干性能
外设模型 只包含通用设备 可加入自定义板级支持包(BSP)

我在做 ADAS 系统仿真时,就遇到过预编译 QEMU 不支持某款 Infineon 外设的情况。折腾了两天,最后自己编译加了个模块,十分钟搞定。所以,别嫌麻烦,这步省不了。

2.2 编译环境准备

先看看你的 Linux 发行版。我习惯用 Ubuntu 22.04 LTS,但 Debian、Fedora 也都行。关键是把依赖装全。

2.2.1 安装基础工具链

# Ubuntu / Debian
sudo apt update
sudo apt install git build-essential ninja-build \
                 pkg-config libglib2.0-dev libpixman-1-dev \
                 libslirp-dev libfdt-dev zlib1g-dev

# Fedora / RHEL
sudo dnf install git gcc gcc-c++ ninja-build \
                 pkgconfig glib2-devel pixman-devel \
                 libslirp-devel libfdt-devel zlib-devel

这里有个小细节:libslirp 是用户态网络栈,如果你不做网络仿真,可以不加。但我建议装上,因为后面调试网络协议栈时很有用。

我的习惯:每次编译前,先跑一遍 sudo apt build-dep qemu-system-arm,它能自动拉满所有编译依赖,省得你一个一个查。

2.3 获取源码与选择版本

QEMU 的源码托管在 GitLab 上。我个人建议用稳定分支,别追 master,除非你想当小白鼠。

git clone https://gitlab.com/qemu-project/qemu.git
cd qemu
git checkout stable-8.2  # 或者 v8.2.0 标签

为什么选 8.2?因为它在 ARMv8.4 和 RISC-V 向量扩展上支持得比较全,汽车电子里常用的 Cortex-A76、RISC-V 64GC 都能跑。

2.4 配置目标架构

配置这一步,是编译的核心。说白了,就是告诉 QEMU:「我要编译哪些架构的模拟器,要不要调试功能,优化到什么程度」。

2.4.1 最小化配置(ARM 单架构)

mkdir build && cd build
../configure --target-list=arm-softmmu,aarch64-softmmu \
             --enable-debug \
             --enable-slirp \
             --enable-kvm
  • --target-list:只编译 ARM 和 AArch64 的完整系统模拟器,省时间。
  • --enable-debug:开启 QEMU 自身的调试符号,方便后续故障定位。
  • --enable-kvm:如果宿主机支持 KVM,可以加速。

2.4.2 加入 RISC-V 支持

../configure --target-list=arm-softmmu,aarch64-softmmu,riscv32-softmmu,riscv64-softmmu \
             --enable-debug \
             --enable-slirp

嗯,这里要注意:RISC-V 的 softmmu 模式是完整系统模拟,如果你只想跑用户态程序,可以用 riscv64-linux-user。但做汽车电子,我们通常需要模拟整个 SoC,所以选 softmmu。

我曾经踩过的坑:第一次编译 RISC-V 时,忘了加 --enable-fdt,结果设备树传递一直失败。后来才发现,QEMU 的 RISC-V 板级模型依赖 libfdt 来生成设备树。所以,libfdt-dev 一定要装,配置时也要确认它被检测到了。

2.5 编译与安装

配置没问题后,直接开编:

make -j$(nproc)
sudo make install

-j$(nproc) 是用所有 CPU 核心并行编译。我见过有人用 -j1 编译,等了四十分钟……其实没必要,现代机器开满核心就行。

安装后,验证一下:

qemu-system-aarch64 --version
qemu-system-riscv64 --version

如果能正常输出版本号,说明编译成功了。

2.6 常见编译错误排查

编译过程中,你十有八九会遇到报错。别慌,我把最常见的几种列出来,附上解决方案。

错误信息 原因 解决办法
ERROR: glib-2.56 gthread-2.0 is required glib 版本太低或未安装 sudo apt install libglib2.0-dev
ERROR: pixman >= 0.21.8 required pixman 缺失 sudo apt install libpixman-1-dev
ERROR: DTC (libfdt) not present 设备树编译器未安装 sudo apt install libfdt-dev
ninja: build stopped: subcommand failed. 编译过程中断,通常是内存不足 减少并行数:make -j2 或增加 swap
../meson.build:1:0: ERROR: Unknown compiler(s) 缺少 C++ 编译器 sudo apt install g++

2.6.1 一个真实案例

我记得有一次,在编译 RISC-V 的 spike 板级模型时,报了个 undefined reference to 'qemu_fdt_setprop'。查了半天,发现是 --enable-fdt 没生效,因为系统里装了 device-tree-compiler 但没装 libfdt-dev。这两个包不一样!前者是命令行工具,后者是开发库。装上 libfdt-dev 后重新 configure,问题解决。

2.7 知识结构总览

下面这张图,把整个编译流程串起来了。你可以把它当作一个检查清单:

QEMU 源码编译与安装流程 QEMU 源码编译与安装流程 1. 环境准备 2. 获取源码 3. 配置目标架构 4. 编译 (make) 5. 安装 (make install) 6. 验证版本 编译错误?排查依赖 配置错误?检查 --target-list 链接错误?检查 libfdt 💡 核心要点: • 先装依赖,再 configure,最后 make,顺序别乱。 • 遇到错误先看 config.log,里面记录了检测失败的详细原因。 • 汽车电子场景建议开启 --enable-debug 和 --enable-slirp。

2.8 验证编译结果

编译安装完成后,我建议你跑一个最小的 ARM 虚拟机,验证一下:

qemu-system-aarch64 -M virt -cpu cortex-a76 -smp 2 -m 1G \
  -kernel /path/to/Image -initrd /path/to/initrd.img \
  -append "console=ttyAMA0" -nographic

如果能正常启动内核,看到控制台输出,那恭喜你,编译环境完全 OK。

一个小技巧:如果你只是临时测试,可以用 -M none 加上 -device loader 来加载裸机程序,不需要完整的文件系统。这样验证速度更快。

好了,编译这关过了,后面就可以放心地做调试和故障定位了。记住,自己编译的 QEMU,就像自己改装的工具车,哪里不顺手就改哪里,这才是嵌入式工程师该有的态度。