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编译流程。你看一遍,基本就能理解整个过程的脉络了。

QEMU 源码编译流程 ① 准备环境 安装依赖库 ② 下载源码 git clone + checkout ③ 配置 --target-list 核心 ④ 编译 make -j$(nproc) ⑤ 安装 make install ⑥ 验证 配置选项详解 --target-list=arm-softmmu --enable-debug / --enable-gdb --prefix=/opt/qemu-arm --disable-werror 常见问题解决 ❌ 缺少libfdt → apt install ❌ Ninja未找到 → pip install ❌ Werror错误 → --disable-werror

这张图把整个流程分成了6步。注意看中间那个「配置」步骤,我用橙色标出来了——这是整个编译过程里最需要花心思的地方。你只要把--target-list搞明白了,其他都是体力活。

3.9 小结

嗯,这一节的内容就这些。说白了,QEMU源码编译就三步:配环境、配参数、make。但每一步都有细节,尤其是--target-list,一定要搞清楚你的目标架构是什么。我见过太多人图省事全量编译,结果编译完发现硬盘少了5个G,还多了一堆用不上的模拟器。

下一节,我们会用编译好的QEMU来跑第一个固件。到时候你就知道,前面这些功夫没白花。

最后提醒一句:如果你在编译过程中遇到任何问题,先检查./configure的输出日志。QEMU的configure脚本很友好,缺什么依赖会直接告诉你。别自己瞎猜,看日志是最快的。


公众号:蓝海资料掘金营,微信deep3321