This document describes the ARM64 cross-compilation setup for Strom, including lessons learned during implementation.
Strom can be cross-compiled from x86_64 Linux to ARM64 targets (aarch64). Two build approaches are available:
- Zig-based build (
cargo-zigbuild) - RECOMMENDED - Uses Zig for cross-compilation with specific glibc version targeting - Traditional glibc build (
aarch64-unknown-linux-gnu) - Dynamic linking with glibc (requires complex multi-arch setup)
# One-time setup - Run BOTH scripts (in order):
./scripts/cross-compile/setup-zig-cross.sh # 1. Install Zig and cargo-zigbuild
./scripts/cross-compile/setup-arm64-cross.sh # 2. Install ARM64 GStreamer libraries (REQUIRED!)
# Build for ARM64 targeting specific glibc version
./scripts/cross-compile/build-zig-arm64.sh 2.36 # For Raspberry Pi OS 12
./scripts/cross-compile/build-zig-arm64.sh 2.31 # For older Debian/Ubuntu
./scripts/cross-compile/build-zig-arm64.sh 2.17 # Maximum compatibilityKey advantage: You can target any glibc version without needing that version installed on your build system!
Important: Both setup scripts are required. Zig handles the cross-compilation toolchain, but ARM64 GStreamer libraries are still needed for pkg-config during the build process.
# One-time setup (installs cross-compiler and ARM64 libraries)
./scripts/cross-compile/setup-arm64-cross.sh
# Build for ARM64 with glibc (uses build system's glibc version)
./scripts/cross-compile/build-arm64.shBinaries are output to:
target/aarch64-unknown-linux-gnu/release/stromtarget/aarch64-unknown-linux-gnu/release/strom-mcp-server
- Ubuntu 24.04 (or compatible Debian-based distribution)
- Rust toolchain installed via rustup
- Trunk for frontend builds:
cargo install trunk - sudo access for installing system packages
Use when:
- You need to target a specific glibc version different from your build system
- You want simpler setup without multi-arch apt complexity
- You need maximum control over glibc compatibility
Advantages:
- Target specific glibc versions (e.g., build for glibc 2.36 on a system with 2.39)
- No complex multi-arch setup - avoids Python package conflicts
- Simple installation - just install Zig and cargo-zigbuild
- Better reproducibility - explicit version targeting
How it works:
- Uses Zig's built-in cross-compilation toolchain
- Specify glibc version via target suffix:
aarch64-unknown-linux-gnu.2.36 - Zig provides glibc headers/libs - no need to install them on build system
Limitations:
- Still need ARM64 GStreamer libraries for pkg-config (can use multi-arch setup for this)
- Slightly newer tool (but actively maintained and widely used)
Use when:
- You're already set up with multi-arch and it's working
- Target system has matching or newer glibc version than build system
- Need best compatibility with GStreamer ecosystem
Limitations:
- Inherits build system's glibc version - Ubuntu 24.04 uses glibc 2.39
- Will fail with "version GLIBC_X.XX not found" on older systems (e.g., Raspberry Pi OS with 2.36)
- Complex setup - requires multi-arch apt, potential Python conflicts
| Build Method | Best For | glibc Version Control | Setup Complexity |
|---|---|---|---|
| Zig | Most users, production builds | ✅ Full control (target 2.17-2.39+) | ⭐ Low |
| Traditional glibc | Already set up, matching glibc | ❌ Uses build system's version | ⭐⭐⭐ High |
The Zig-based setup is much simpler than traditional cross-compilation:
-
Download and install Zig
- Downloads Zig from ziglang.org
- Extracts to
~/.local/zig - Adds to PATH in
~/.bashrc
-
Install cargo-zigbuild
cargo install --locked cargo-zigbuild
-
Add Rust ARM64 target
rustup target add aarch64-unknown-linux-gnu
-
For GStreamer support (optional)
- Run
setup-arm64-cross.shto get ARM64 GStreamer pkg-config files - Or build in Docker with ARM64 GStreamer pre-installed
- Run
That's it! No multi-arch apt configuration, no Python conflicts, no complex toolchain setup.
The setup-arm64-cross.sh script performs the following:
sudo dpkg --add-architecture arm64Enables multi-arch support in dpkg/apt.
Challenge: Ubuntu 24.04 uses deb822 format in /etc/apt/sources.list.d/ubuntu.sources
Solution: Add Architectures: amd64 to existing sources, add ARM64 sources separately:
# /etc/apt/sources.list.d/ubuntu.sources gets:
Types: deb
Architectures: amd64 # <- Added
URIs: http://archive.ubuntu.com/ubuntu/
...
# /etc/apt/sources.list.d/arm64-cross.list gets:
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble main universe
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates main universe
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-security main universe
Key insight: ARM64 packages are on ports.ubuntu.com, not archive.ubuntu.com.
Problem: GStreamer libraries depend on Python. When installing libgstreamer1.0-dev:arm64, apt tries to satisfy dependencies by:
- Removing
python3:amd64(your working Python) - Installing
python3:arm64(which can't run on x86_64) - This breaks your system and the installation fails
Solution: Create /etc/apt/preferences.d/block-arm64-python:
Package: python3*:arm64
Pin: release *
Pin-Priority: -1
This tells apt to never install ARM64 Python packages. Python dependencies are satisfied by existing amd64 Python.
gcc-aarch64-linux-gnu # ARM64 C/C++ cross-compiler
g++-aarch64-linux-gnu # ARM64 C++ cross-compiler
pkg-config # For finding library pathslibcairo2-dev:arm64
libgstreamer1.0-dev:arm64
libgstreamer-plugins-base1.0-dev:arm64
libgstreamer-plugins-bad1.0-dev:arm64These are header files and .so libraries for ARM64, not executables.
rustup target add aarch64-unknown-linux-gnu
rustup target add aarch64-unknown-linux-muslCreates .cargo/config.toml with linker settings:
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
[target.aarch64-unknown-linux-musl]
linker = "aarch64-linux-gnu-gcc"
rustflags = ["-C", "target-feature=-crt-static", "-C", "link-arg=-lm"]
[env]
PKG_CONFIG_SYSROOT_DIR_aarch64_unknown_linux_gnu = "/usr/aarch64-linux-gnu"
PKG_CONFIG_PATH_aarch64_unknown_linux_gnu = "/usr/lib/aarch64-linux-gnu/pkgconfig"cd frontend
trunk build --releaseWASM is architecture-independent, built once for all targets.
export PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
cargo build --release --package strom --target aarch64-unknown-linux-gnuIssue: Script assumed old /etc/apt/sources.list format with simple deb http://... lines.
Reality: Ubuntu 24.04+ uses deb822 format in /etc/apt/sources.list.d/ubuntu.sources:
Types: deb
URIs: http://archive.ubuntu.com/ubuntu/
Suites: noble noble-updates
Components: main universe
Solution: Detect and handle deb822 format by adding Architectures: amd64 field.
Issue: Most complex problem encountered. Installing ARM64 libraries triggers Python removal:
The following packages will be REMOVED:
python3 python3-minimal python3.12 python3.12-minimal
# ... and 738 other packages including desktop environment!
Root cause:
libgstreamer-dev:arm64depends onpython3:any- apt interprets this as "any architecture"
- Decides to "upgrade" by removing
python3:amd64and installingpython3:arm64 - ARM64 Python can't execute on x86_64, installation fails
Solution: apt pinning to block ARM64 Python entirely. This forces apt to satisfy dependencies with existing amd64 Python.
Issue: Binaries compiled on Ubuntu 24.04 (glibc 2.39) won't run on older systems (e.g., Raspberry Pi OS 12 with glibc 2.36):
./strom: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found
Solutions:
- Use Zig (recommended) - Target specific glibc version (e.g., 2.36 for Raspberry Pi)
- Use Docker with older base (Debian 12 Bookworm = glibc 2.36)
- Compile on system with matching glibc version
To remove cross-compilation setup:
./scripts/cross-compile/cleanup-arm64-cross.shThis will:
- Remove ARM64 package sources
- Remove Python blocking preferences
- Restore original ubuntu.sources (from backup)
- Optionally remove arm64 architecture and packages
Your build system has newer glibc than target. Use Zig to target specific glibc version:
./scripts/cross-compile/build-zig-arm64.sh 2.36ARM64 GStreamer dev packages not installed. Run setup script again.
If you see Python removal warnings, the Python blocking isn't working. Check:
cat /etc/apt/preferences.d/block-arm64-pythonShould show Pin-Priority: -1 for python3*:arm64.
Changing rustflags invalidates entire build cache. This is one-time; subsequent builds will be incremental.
This setup is designed for Ubuntu 24.04. For other distributions:
- Ubuntu 22.04/23.04: Change "noble" to "jammy"/"lunar" in sources
- Debian 12/13: Use Debian repositories instead of Ubuntu
- Fedora/RHEL: Use
dnf, different package names, no multi-arch - Arch Linux: Use AUR for cross-compilers, different approach
For glibc version compatibility, use Docker with matching Debian version:
# Modify Dockerfile to use Debian Bookworm (glibc 2.36)
docker buildx build --platform linux/arm64 -t strom:arm64 .
# Extract binary
docker create --name temp strom:arm64
docker cp temp:/app/strom ./strom-arm64
docker rm tempImprovements to cross-compilation setup welcome! Please test thoroughly on target hardware before submitting PRs.