Files
mckero f938d0b3bd Build overlay apps without the Arm toolchain, and say how far verification got
pip install ziglang cross-compiles to thumb-freestanding-eabi, which is enough to build a .app with no arm-none-eabi-gcc and no Docker. Measured refusals: --defsym, -Ttext and --section-start come back as unsupported linker args, so the VMA is resolved into a copy of app.ld; -T is forwarded (a missing script errors); --image-base is accepted but page-aligns the segments into 0x200103C8 and 0x20020C94. tools/elf2bin.py extracts allocated sections rather than program headers, because lld maps the ELF header and phdr table as a 180-byte LOAD of its own -- following the headers starts the image at 0x20000000 and APP_ERR_VMA. One division pulled in __aeabi_uidiv, which a -nostdlib blob cannot have: Minesweeper now avoids division entirely. app_main carries the .text.entry attribute upstream's apps use, so the entry is first for the loader's jump to offset 0.

Minesweeper builds to 2408 bytes of code against a 4096-byte budget. Installed through the page, the firmware reads the slot header twice and then exactly code_size bytes from slot+0x1000 -- the only read of that size in the boot log -- so the blob shape, header, CRC, VMA and offset are all accepted. Whether control reaches the overlay is unproven: the 100 ms PC probe saw no overlay address, no APP ERROR screen appears, and the app does not draw. test_elf2bin pins the phantom-header-segment lesson; both AGENTS files record the rest.
2026-10-02 08:32:22 +08:00

49 lines
2.3 KiB
Bash

#!/usr/bin/env bash
# Build one overlay-app blob (.app) locally, without Docker.
#
# Upstream runs this same file inside the uvk1-uvk5v3 image; it already prefers
# arm-none-eabi-gcc from PATH when there is one, which is what makes a local build
# possible. Link VMA is pinned at 0x20000280 and must match the firmware's
# __mb_workspace_start -- the loader compares them and refuses with APP_VMA.
set -euo pipefail
APP="$(basename "$PWD")"
APP_NAME="Minesweeper" # <-- the only per-app line: the label in the 64-byte header
APP_VER="1.0"
APP_API_MIN=1
APP_VMA=${APP_VMA:-0x20000280}
OUT="${APP_NAME// /}"
# Two routes, because only one of them was available where this was written:
# 1. the Arm GNU Toolchain, which is what upstream's own build.sh expects
# 2. Zig's bundled clang (pip install ziglang), which cross-compiles to
# thumb-freestanding-eabi and needs no arm-none-eabi-gcc at all
ZIG=""
if ! CC=${CC:-$(command -v arm-none-eabi-gcc 2>/dev/null)} || [ -z "$CC" ]; then
ZIG=${ZIG:-$(command -v zig 2>/dev/null || echo "python -m ziglang")}
CC="$ZIG cc"
echo "no arm-none-eabi-gcc; using $ZIG cc"
fi
CFLAGS="-mcpu=cortex-m0plus -mthumb -Os -std=gnu11 -ffreestanding -fno-builtin -fno-common \
-fomit-frame-pointer -ffunction-sections -fdata-sections -Wall -Wextra"
# Zig refuses --defsym, -Ttext and --section-start, so the VMA is resolved into a copy of
# the linker script instead, and the entry is pinned by the section attribute the source
# already carries (.text.entry, exactly as upstream's apps do).
sed "s/APP_VMA *= *DEFINED(APP_VMA) *? *APP_VMA *: *0x[0-9A-Fa-f]*;/APP_VMA = ${APP_VMA};/" \
app.ld > app-resolved.ld
LDFLAGS="-nostdlib -T app-resolved.ld -Wl,-e,app_main -Wl,--gc-sections -Wl,--build-id=none"
rm -f ./*.app ./*.elf ./*.bin
# shellcheck disable=SC2086
$CC $CFLAGS $LDFLAGS -o "${APP}.elf" "${APP}_app.c"
# objcopy if there is one, otherwise the section-based extractor from this repository
if command -v arm-none-eabi-objcopy >/dev/null 2>&1; then
arm-none-eabi-objcopy -O binary "${APP}.elf" "${APP}.bin"
else
python3 "$(dirname "${BASH_SOURCE[0]}")/../../../tools/elf2bin.py" "${APP}.elf" "${APP}.bin" --min "${APP_VMA}"
fi
python3 pack_app.py "${APP}.bin" "${OUT}.app" --name "$APP_NAME" --ver "$APP_VER" \
--vma "$APP_VMA" --api-min "$APP_API_MIN"
ls -l "${OUT}.app"