3、QEMU安装与编译:源码编译QEMU、配置选项详解(--target-list)、常见安装问题解决
说到QEMU,很多做嵌入式开发的朋友第一反应就是「装个包就完事了」。嗯,用apt-get或者brew确实方便,但我要说——源码编译才是真正理解QEMU的开始。我自己在项目里踩过不少预编译包的坑,比如某个调试特性没编译进去,或者版本对不上目标架构。所以这一节,咱们就手把手把QEMU从源码撸一遍。
核心观点:源码编译QEMU,不是为了炫技,而是为了精准控制。你想想看,一个固件模拟环境,连CPU型号、外设数量都要精确匹配,QEMU的编译选项当然也得「量身定制」。
3.1 为什么非要源码编译?
我刚开始做固件模拟的时候,图省事直接apt install qemu。结果呢?模拟一个Cortex-M3的固件,死活跑不起来。查了半天,发现系统自带的QEMU压根没编译ARM的SoftMMU模式。说白了,包管理器给你的,是「大众版」,而我们做嵌入式调试,需要的是「定制版」。
源码编译的好处很明显:
- 精准控制目标架构:只编译你需要的架构,省时省力
- 开启调试特性:比如GDB stub、trace日志、semihosting等
- 打补丁方便:遇到QEMU的bug,可以直接改源码再编译
- 版本可控:项目组统一用某个版本,避免「我这儿能跑你那儿不行」的尴尬
我的习惯:每个嵌入式项目,我都会在项目目录下放一个build_qemu.sh脚本。这样新同事入职,跑一下脚本就能得到完全一致的模拟环境。省心。
3.2 编译前的准备工作
别急着./configure,先把依赖装好。我记得有一次在Ubuntu 20.04上编译,缺了libglib2.0-dev,结果报错信息看得我一脸懵。后来学乖了,先跑一遍依赖检查。
Ubuntu/Debian系:
sudo apt-get update
sudo apt-get install -y \
build-essential \
git \
python3 \
python3-pip \
libglib2.0-dev \
libpixman-1-dev \
libfdt-dev \
zlib1g-dev \
libssl-dev \
ninja-build
CentOS/RHEL系:
sudo yum groupinstall "Development Tools"
sudo yum install -y \
glib2-devel \
pixman-devel \
libfdt-devel \
zlib-devel \
openssl-devel \
ninja-build
macOS(Homebrew):
brew install pkg-config glib pixman ninja
注意:QEMU从5.0版本开始,强制使用Ninja作为构建系统。如果你还在用老版本的Makefile方式,记得升级。我曾经在旧系统上折腾了半天,结果发现是Ninja没装。
3.3 下载源码与版本选择
QEMU的官方仓库在git.qemu.org,但国内访问速度你懂的。我一般用GitHub的镜像:
git clone https://github.com/qemu/qemu.git
cd qemu
git checkout v8.0.0 # 选一个稳定版本
版本怎么选?我个人建议:别追最新,也别用太老。最新版可能有未发现的bug,太老的版本又缺特性。我一般选次新稳定版,比如现在v8.0.0就挺好。如果你要模拟的固件是ARM Cortex-M系列,v7.0.0以上都支持得不错。
3.4 配置选项详解:--target-list 是核心
这是整个编译过程里最关键的环节。./configure 的参数很多,但对我们嵌入式开发来说,--target-list 是重中之重。
什么叫target-list?说白了,就是告诉QEMU:「我要模拟哪些CPU架构」。默认情况下,QEMU会编译所有支持的架构——x86、ARM、RISC-V、MIPS……加起来几十个。你想想看,这得编译多久?我试过全量编译,在8核的机器上跑了快40分钟。
正确的做法:只编译你需要的。
常见的target列表:
| 目标架构 | target-list 参数 | 典型用途 |
|---|---|---|
| ARM Cortex-M (无MMU) | arm-softmmu | STM32、NXP LPC系列固件模拟 |
| ARM Cortex-A (有MMU) | aarch64-softmmu | 树莓派、高通平台固件模拟 |
| RISC-V 32位 | riscv32-softmmu | 国产芯片、开源处理器模拟 |
| RISC-V 64位 | riscv64-softmmu | 高性能RISC-V平台模拟 |
| x86 32位 | i386-softmmu | 老式PC固件、BIOS模拟 |
| x86 64位 | x86_64-softmmu | 标准PC、UEFI固件模拟 |
举个例子,如果你只做ARM Cortex-M的固件模拟:
./configure --target-list=arm-softmmu \
--enable-debug \
--enable-gdb \
--enable-trace-backends=log
这里我额外加了几个选项:
- --enable-debug:开启调试符号,方便后续用GDB调试QEMU本身
- --enable-gdb:启用GDB stub,这样我们可以用GDB远程调试固件
- --enable-trace-backends=log:开启日志跟踪,排查固件运行时的奇怪行为
避坑指南:我曾经在编译时忘了加--enable-gdb,结果模拟器跑起来后,GDB怎么都连不上。查了半天才发现是编译时没开这个选项。所以,调试特性一定要在configure阶段就打开,后期再加就得重新编译。
3.5 其他常用配置选项
除了--target-list,还有几个选项我经常用:
| 选项 | 说明 | 推荐场景 |
|---|---|---|
| --enable-kvm | 启用KVM加速(仅Linux x86) | 模拟x86固件时大幅提升速度 |
| --enable-slirp | 用户态网络协议栈 | 固件需要网络功能时使用 |
| --enable-virtfs | 主机与模拟器文件共享 | 调试时方便传输文件 |
| --disable-werror | 不把警告当错误 | 编译器版本较新时避免编译失败 |
| --prefix=/opt/qemu | 指定安装路径 | 多版本QEMU共存时很有用 |
我个人习惯用--prefix来隔离不同版本的QEMU。比如:
./configure --target-list=arm-softmmu \
--prefix=/opt/qemu-arm \
--enable-debug \
--enable-gdb
这样编译出来的QEMU就装在/opt/qemu-arm下,跟系统自带的完全不冲突。
3.6 编译与安装
配置完成后,编译就简单了:
make -j$(nproc) # 用所有CPU核心并行编译
sudo make install
如果只编译特定目标,可以进一步缩小范围:
make -j$(nproc) arm-softmmu/all # 只编译ARM softmmu
编译完成后,验证一下:
/opt/qemu-arm/bin/qemu-system-arm --version
看到版本号输出,就说明成功了。
小技巧:如果你经常编译QEMU,可以建一个build目录,在build目录里执行configure和make。这样源码目录保持干净,想重新编译时直接删掉build目录就行。
mkdir build && cd build
../configure --target-list=arm-softmmu --enable-debug
make -j$(nproc)
3.7 常见安装问题解决
编译QEMU的过程中,我遇到过不少问题。挑几个典型的说说:
问题1:缺少libfdt
报错信息类似「ERROR: glib-2.56 gthread-2.0 is required to compile QEMU」。其实不光是glib,libfdt也经常缺。解决方案就是装包:
sudo apt-get install libfdt-dev
问题2:Ninja未找到
QEMU 5.0+强制使用Ninja。如果你系统里没有,要么apt install ninja-build,要么用pip装:
pip3 install ninja
问题3:编译时出现Werror错误
新版本的GCC比较严格,会把一些警告当成错误。我遇到过好几次,解决办法是configure时加上--disable-werror:
./configure --target-list=arm-softmmu --disable-werror
问题4:Python版本不兼容
QEMU的构建系统依赖Python 3。如果你系统默认Python是2.7,会报错。可以用:
./configure --python=/usr/bin/python3
曾经踩过的坑:有一次我在Docker容器里编译QEMU,容器是Alpine Linux,缺了一堆开发库。我一个个装,装到怀疑人生。后来学聪明了,直接用Ubuntu的Docker镜像,一条apt-get命令搞定所有依赖。所以,编译环境尽量用成熟的发行版,别在精简系统上折腾。
3.8 本章核心逻辑图
下面这张图,是我自己总结的QEMU编译流程。你看一遍,基本就能理解整个过程的脉络了。
这张图把整个流程分成了6步。注意看中间那个「配置」步骤,我用橙色标出来了——这是整个编译过程里最需要花心思的地方。你只要把--target-list搞明白了,其他都是体力活。
3.9 小结
嗯,这一节的内容就这些。说白了,QEMU源码编译就三步:配环境、配参数、make。但每一步都有细节,尤其是--target-list,一定要搞清楚你的目标架构是什么。我见过太多人图省事全量编译,结果编译完发现硬盘少了5个G,还多了一堆用不上的模拟器。
下一节,我们会用编译好的QEMU来跑第一个固件。到时候你就知道,前面这些功夫没白花。
最后提醒一句:如果你在编译过程中遇到任何问题,先检查./configure的输出日志。QEMU的configure脚本很友好,缺什么依赖会直接告诉你。别自己瞎猜,看日志是最快的。
公众号:蓝海资料掘金营,微信deep3321