velxio/docs/BUILD-QEMU.md

6.2 KiB
Raw Blame History

Building QEMU from source for Velxio

The Velxio docker image ships with prebuilt libqemu-xtensa and libqemu-riscv32 libraries so ESP32 / ESP32-S3 / ESP32-C3 simulation works out of the box. If you'd rather not use the prebuilts — whether for licensing reasons, audit requirements, or simply because you prefer to compile what you run — this guide walks you through building both libraries from the upstream lcgamboa/qemu fork.

Velxio itself is AGPLv3, and that includes the binary linker contract: you have the source, you can rebuild, you can verify the chain end-to-end.

The prebuilts at velxio.dev/license/signup and on GitHub Releases are a convenience, not a gate. Everything in this guide produces the exact same .so / .dll / .dylib files those bundles contain, byte-for-byte modulo timestamps.

Audience

This guide assumes a Linux or macOS workstation with a working C toolchain. Windows builds are possible via MSYS2 / MinGW but are out of scope here — start from lcgamboa/qemu's own README if you need them.

What you'll produce

File Architecture Used for
libqemu-xtensa.so Xtensa LX6 / LX7 ESP32, ESP32-S3, ESP32-CAM, Arduino Nano ESP32
libqemu-riscv32.so RISC-V RV32IMC ESP32-C3, XIAO ESP32-C3, CH32V003

The Velxio backend dlopen's these at runtime through the dynamic linker (backend/app/services/qemu_runtime.py). Drop the freshly built files into /app/lib/ inside the container (or the host path that mounts to it) and Velxio will use them on the next simulation start.

1. Clone the fork

git clone https://github.com/lcgamboa/qemu.git
cd qemu
git checkout 822927b6  # the commit Velxio's prebuilts are anchored to

The exact commit ID may move forward over time. The Velxio docker image's Dockerfile.standalone records the canonical SHA — grep -E 'qemu.*checkout|QEMU_REF' Dockerfile.standalone to read it.

2. Install build dependencies

Debian / Ubuntu:

sudo apt-get update
sudo apt-get install -y \
    git ninja-build pkg-config \
    libglib2.0-dev libpixman-1-dev \
    python3 python3-venv python3-pip \
    flex bison

Arch / Manjaro:

sudo pacman -S --needed \
    git ninja pkgconf \
    glib2 pixman \
    python flex bison

macOS (Homebrew):

brew install ninja pkg-config glib pixman

3. Configure and build — Xtensa (ESP32 / ESP32-S3)

mkdir build-xtensa && cd build-xtensa
../configure \
    --target-list=xtensa-softmmu \
    --disable-werror \
    --enable-shared-lib \
    --disable-tools \
    --disable-docs
ninja

This produces libqemu-xtensa.so under build-xtensa/. Verify:

file libqemu-xtensa.so
# ELF 64-bit LSB shared object, x86-64, dynamically linked

ls -lh libqemu-xtensa.so
# ~46 MB on Linux x86_64

Go back to the repo root:

cd ..

4. Configure and build — RISC-V (ESP32-C3)

mkdir build-riscv32 && cd build-riscv32
../configure \
    --target-list=riscv32-softmmu \
    --disable-werror \
    --enable-shared-lib \
    --disable-tools \
    --disable-docs
ninja
cd ..

You now have build-riscv32/libqemu-riscv32.so.

5. Drop the binaries into Velxio

If you self-host Velxio via the official docker image:

docker cp build-xtensa/libqemu-xtensa.so   velxio:/app/lib/libqemu-xtensa.so
docker cp build-riscv32/libqemu-riscv32.so velxio:/app/lib/libqemu-riscv32.so
docker restart velxio

If you're running Velxio from source:

cp build-xtensa/libqemu-xtensa.so   /path/to/velxio/backend/app/lib/
cp build-riscv32/libqemu-riscv32.so /path/to/velxio/backend/app/lib/
# restart the backend

The next ESP32 / ESP32-C3 simulation start will use your libraries. Check the backend logs for a line like:

[qemu_runtime] loaded libqemu-xtensa.so build=<your-hash>

6. ESP32 ROM blobs

The QEMU build does not produce the ESP32 ROM dumps Velxio also needs (esp32-v3-rom.bin, esp32-v3-rom-app.bin, esp32c3-rom.bin). Those come straight from Espressif's open-source toolchain and are redistributable verbatim. The image already includes them at /app/lib/; you only need to replace them if you're working from a custom esp-idf version.

7. Troubleshooting

No such file or directory: glib-2.0 — apt missed libglib2.0-dev. Re-run the dependencies step.

error: CONFIG_USER_ONLY is not defined — you forgot --target-list=. The softmmu suffix is required for the system emulator Velxio uses.

Shared library is too small (a few hundred KB) — you built without --enable-shared-lib. The default QEMU output is the qemu-system-* binary, not the library Velxio loads.

Simulation starts but the board boots into "qemu: fatal: Trying to execute code outside RAM or ROM" — wrong commit. lcgamboa/qemu master moves; Velxio is pinned. Check out the commit listed in Dockerfile.standalone.

License notes

lcgamboa/qemu is GPL-2.0 (same as upstream QEMU). Velxio is AGPLv3. The dlopen boundary keeps the two licenses orthogonal: QEMU stays GPL'd, Velxio stays AGPL'd, neither contaminates the other. If you distribute a modified Velxio image with self-built QEMU binaries, you owe the QEMU sources to your recipients (GPL-2.0 obligation), and you owe the Velxio modifications under AGPLv3 if the deployment is networked.

Why use the prebuilts at all?

For ~99% of self-hosters the prebuilts at velxio.dev/license/signup or the GitHub release are the path of least resistance — they're the same files this guide produces, signed-by-sha256 in their manifest, ready to drop in. The build itself takes 15-30 minutes on a modern laptop and ~3 GB of disk for the build tree.

Building from source matters when:

  • You're auditing the supply chain for a regulated deployment.
  • You need to patch QEMU (e.g. add a peripheral the fork doesn't emulate) and want to ship the patched library.
  • You don't want any third-party prebuilts on your machine.
  • You're on a platform we don't ship a binary for (e.g. BSD, ARM on macOS Intel boxes, exotic libc).

All four are legitimate. The license module on velxio.dev exists to distribute prebuilts conveniently and to detect bulk-abuse patterns; it never tries to be the only path.