Introduction
Most Pyvorin Edge deployments target ARM64 Linux devices (Raspberry Pi, NVIDIA Jetson, custom boards), but CI/CD often runs on x86_64. Cross-compilation lets you build .so artifacts on an x86 host and ship them to the edge without requiring native ARM64 build nodes.
Toolchain Setup
On Ubuntu/Debian install the cross-compiler and standard library headers for ARM64:
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu libc6-dev-arm64-cross
Verify the compiler is on PATH:
aarch64-linux-gnu-gcc --version
# aarch64-linux-gnu-gcc (Ubuntu 13.2.0) 13.2.0
Cross-Compiling a Kernel
Use the same flags as a native build, but swap the compiler prefix. The -march flag should match your target SoC. For a generic ARMv8-A device:
aarch64-linux-gnu-gcc \
-shared -fPIC -O3 -march=armv8-a+fp+simd \
-o libvecadd_arm64.so vec_add_neon.c
For a Cortex-A72 specific optimization (Raspberry Pi 4, Jetson Nano):
aarch64-linux-gnu-gcc \
-shared -fPIC -O3 -mcpu=cortex-a72+fp+simd -mtune=cortex-a72 \
-o libvecadd_arm64.so vec_add_neon.c
Verifying Compiled Artifacts
Always confirm the architecture of the output before deployment. The file utility reads the ELF header and reports the target machine.
file libvecadd_arm64.so
# libvecadd_arm64.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked
Also inspect exported symbols to ensure the ABI contract matches:
aarch64-linux-gnu-nm -D libvecadd_arm64.so | grep vec_add_f32
# 0000000000000730 T vec_add_f32
Docker buildx with --platform linux/arm64
Docker buildx is the most reliable way to cross-compile in CI. You write a multi-stage Dockerfile that builds inside an ARM64 container running under QEMU, or you use a native cross-compiler image.
Fast Cross-Build Dockerfile (using cross-compiler)
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM debian:bookworm-slim AS builder
RUN apt-get update && apt-get install -y gcc-aarch64-linux-gnu libc6-dev-arm64-cross
WORKDIR /src
COPY vec_add_neon.c .
RUN aarch64-linux-gnu-gcc \
-shared -fPIC -O3 -march=armv8-a+fp+simd \
-o /out/libvecadd_arm64.so vec_add_neon.c
FROM scratch AS export
COPY --from=builder /out/libvecadd_arm64.so /
Build and Extract
docker buildx build \
--platform linux/arm64 \
--target export \
--output type=local,dest=./artifacts \
.
Manifest Verification After Cross-Compile
After cross-compilation, generate a manifest with CompilerBridge and include the SHA-256 hash of the .so. The edge runtime verifies this hash before loading.
import hashlib
import json
from pathlib import Path
from pyvorin_edge.compiler_bridge import CompilerBridge
so_path = Path("./artifacts/libvecadd_arm64.so")
# Compute hash
hasher = hashlib.sha256()
with open(so_path, "rb") as fh:
while chunk := fh.read(8192):
hasher.update(chunk)
so_hash = hasher.hexdigest()
# Generate ABI + manifest
bridge = CompilerBridge()
abi = bridge.generate_abi(so_path, "vec_add_f32")
manifest = {
"version": "1.0.0",
"platform": "linux/arm64",
"modules": [
{
"name": "vec_add_f32",
"path": str(so_path.name),
"abi": abi.to_dict(),
"sha256": so_hash,
}
],
}
with open("manifest.json", "w") as fh:
json.dump(manifest, fh, indent=2)
print(json.dumps(manifest, indent=2))
CI/CD Pipeline Example (GitHub Actions)
The workflow below cross-compiles on every push, verifies the ELF architecture, generates the manifest, and uploads artifacts.
name: Cross-Compile ARM64 Kernels
on:
push:
branches: [main]
paths:
- "kernels/**/*.c"
- ".github/workflows/cross-compile.yml"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cross-compile with buildx
run: |
docker buildx build \
--platform linux/arm64 \
--target export \
--output type=local,dest=./artifacts \
-f kernels/Dockerfile .
- name: Verify ELF architecture
run: |
for so in ./artifacts/*.so; do
echo "Checking $so"
file "$so" | grep -q "ARM aarch64" || exit 1
done
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install -e ./edge_sdk
- name: Generate manifest
run: python kernels/generate_manifest.py
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: arm64-kernels
path: |
artifacts/*.so
manifest.json
Runtime Verification on the Edge Device
When the edge agent boots, it loads the manifest and verifies each module before calling ModuleLoader.load(). The verify_signature() method in module_host/loader.py performs the SHA-256 check.
from pyv_edge_agent.module_host.loader import ModuleLoader
import json
manifest = json.load(open("manifest.json"))
loader = ModuleLoader()
for mod in manifest["modules"]:
loader.load(mod["path"], ABIContract.from_manifest(mod["abi"]))
if not loader.verify_signature(mod["sha256"]):
raise RuntimeError(f"Integrity check failed for {mod['name']}")
print(f"{mod['name']} verified and loaded.")
Summary
Cross-compilation for ARM64 is straightforward with aarch64-linux-gnu-gcc or Docker buildx. Always verify the ELF architecture with file, generate a manifest with SHA-256 hashes, and check those hashes at runtime via ModuleLoader.verify_signature(). Automate the entire flow in GitHub Actions for reproducible edge deployments.