diff --git a/CLAUDE.md b/CLAUDE.md index 486e826..f7e6aa0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,11 +7,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co SDLamp2 is a simple SDL2-based audio player written in C, designed for a child to use. It plays long m4a/mp3 files (fairy tale cassette rips) with a cassette-player-like UI: rewind, stop, play, fast-forward, next tape. Inspired by Winamp. See `docs/sdlamp2-fsd.md` for the full functional specification and changelog. -See `docs/TOOLCHAIN.md` for cross-compilation toolchain details and the aarch64 migration plan. +See `docs/TOOLCHAIN.md` for target device details and the arm64 Docker build container. ## Build and Run -**Dependencies:** SDL2, SDL2_image, FFmpeg libraries (libavformat, libavcodec, libavutil, libswresample). Installed via system package manager (native) or into the Buildroot sysroot (cross-compile). +**Dependencies:** SDL2, SDL2_image, FFmpeg libraries (libavformat, libavcodec, libavutil, libswresample). Installed via system package manager (native) or via apt inside the arm64 Docker container. ```sh make # native build — uses pkg-config @@ -19,10 +19,12 @@ make clean # remove build artifacts ./build/sdlamp2 [audio_directory] ``` -Cross-compilation inside the Docker toolchain container (where `CROSS_COMPILE` and `PREFIX` are set by the environment): +Building for the arm64 target device via the Docker container (from the `docker-arm64/` directory): ```sh -make # picks up CROSS_COMPILE and PREFIX automatically +make build # one-shot: build sdlamp2 inside arm64 container +make shell # interactive shell inside the arm64 container +make clean # remove the Docker image ``` The controls spritesheet (`controls.png`) is embedded in the binary. If an external `controls.png` exists in the current working directory it takes precedence, allowing custom skins. Audio directory defaults to cwd if not specified. diff --git a/docs/TOOLCHAIN.md b/docs/TOOLCHAIN.md index 32e56fb..df4583f 100644 --- a/docs/TOOLCHAIN.md +++ b/docs/TOOLCHAIN.md @@ -1,6 +1,6 @@ # Cross-Compilation Toolchain -This document describes the current Docker/Buildroot cross-compilation toolchain, the architecture mismatch with the target device, and possible approaches to fix it. +This document describes the target device and the arm64 Docker build container used to produce binaries for it. ## Target Device @@ -39,175 +39,35 @@ All required shared libraries are pre-installed. Most are at `/usr/lib/`, some a - A native aarch64 compile (e.g. inside an arm64 Docker container) produces working binaries - Must link against glibc ≤ 2.35 (use GCC ≤ 12 to avoid GLIBCXX version mismatches) -## Current Toolchain Architecture +## Build Container -### Pipeline Overview +The `docker-arm64/` directory contains a Docker setup that runs an arm64 Ubuntu 22.04 container via QEMU user-mode emulation. This matches the target device exactly — same distro, same glibc 2.35, same library versions. The project's native `make` (using `pkg-config`) works as-is inside the container, no cross-compilation flags needed. -The toolchain is built via a Docker container defined in `docker/`: +### Prerequisites -1. **`docker/Dockerfile`** — Debian Buster (EOL) base image, installs build dependencies (bc, build-essential, cmake, git, etc.), copies support scripts, runs `build-toolchain.sh`, sources `setup-env.sh` in `.bashrc` -2. **`docker/support/build-toolchain.sh`** — Downloads Buildroot 2017.11, applies patches, loads `rg35xx-buildroot-2017.11.config`, runs `make world`. If a pre-built `rg35xx-toolchain.tar.xz` exists, extracts that instead of rebuilding. -3. **`docker/support/install-toolchain.sh`** — Copies host tools from `buildroot/output/host/usr/` to `/opt/rg35xx-toolchain/usr/`, copies `hwcap.h` into the sysroot, runs `relocate-sdk.sh` to fix embedded paths. -4. **`docker/support/setup-env.sh`** — Sets environment variables: - ```sh - export PATH="/opt/rg35xx-toolchain/usr/bin:${PATH}:/opt/rg35xx-toolchain/usr/arm-buildroot-linux-gnueabihf/sysroot/bin" - export CROSS_COMPILE=/opt/rg35xx-toolchain/usr/bin/arm-buildroot-linux-gnueabihf- - export PREFIX=/opt/rg35xx-toolchain/usr/arm-buildroot-linux-gnueabihf/sysroot/usr - export UNION_PLATFORM=rg35xx - ``` -5. **`docker/support/package-toolchain.sh`** — Archives `/opt/rg35xx-toolchain` as `rg35xx-toolchain.tar.xz` for caching. -6. **`docker/support/relocate-sdk.sh`** — Rewrites hardcoded paths in SDK text files so the toolchain can live at a different filesystem location. -7. **`docker/Makefile`** — `make shell` builds the Docker image (tag `rg35xx-toolchain`) and drops into an interactive bash shell with `./workspace` mounted at `/root/workspace`. +QEMU binfmt handlers must be registered on the host (one-time setup, persists across reboots): -### How the Main Makefile Uses It - -The project `Makefile` detects `PREFIX` to switch modes: - -- **Cross-compile** (PREFIX set): uses `-I$(PREFIX)/include` and `-L$(PREFIX)/lib` directly. -- **Native** (no PREFIX): uses `pkg-config` to find SDL2/FFmpeg. - -`CROSS_COMPILE` is prepended to `gcc` to select the cross-compiler (e.g. `arm-buildroot-linux-gnueabihf-gcc`). - -## The Problem - -The Buildroot config targets **32-bit ARM Cortex-A9** but the Anbernic RG35XX runs a **64-bit aarch64 userland**. - -The cross-compiled binary is `ELF 32-bit LSB executable, ARM, EABI5` while the device only has 64-bit aarch64 shared libraries. The binary cannot run — the dynamic linker and every `.so` it needs are the wrong architecture. - -Key mismatched settings in the config: - -| Setting | Current Value | Needed | -|---------|--------------|--------| -| `BR2_arm` | `y` (32-bit ARM) | `BR2_aarch64=y` | -| `BR2_cortex_a9` | `y` | Cortex-A53 or similar | -| `BR2_ARM_EABIHF` | `y` | LP64 ABI | -| `BR2_GCC_TARGET_ABI` | `aapcs-linux` | `lp64` | -| `BR2_GCC_TARGET_CPU` | `cortex-a9` | `cortex-a53` | -| `BR2_GCC_TARGET_FPU` | `neon` | (implicit in AArch64) | -| Sysroot triple | `arm-buildroot-linux-gnueabihf` | `aarch64-buildroot-linux-gnu` | - -## Buildroot Config Details - -**File:** `docker/support/rg35xx-buildroot-2017.11.config` - -### Architecture - -``` -BR2_arm=y -BR2_cortex_a9=y -BR2_ARM_EABIHF=y -BR2_ARM_FPU_NEON=y -BR2_ARM_INSTRUCTIONS_THUMB2=y +```sh +docker run --privileged --rm tonistiigi/binfmt --install arm64 ``` -### Toolchain Versions +### Usage -| Component | Version | -|-----------|---------| -| Buildroot | 2017.11 | -| GCC | 7.2.0 (`BR2_GCC_VERSION_7_X`) | -| Binutils | 2.28.1 (`BR2_BINUTILS_VERSION_2_28_X`) | -| Kernel headers | 3.10.108 (`BR2_KERNEL_HEADERS_3_10`) | -| glibc | 2.28 (`BR2_TOOLCHAIN_BUILDROOT_GLIBC`) | +From the `docker-arm64/` directory: -### Packages Enabled - -- **FFmpeg** — all encoders, decoders, muxers, demuxers, parsers, protocols, filters; nonfree codecs enabled -- **SDL2** — with KMS/DRM support, SDL2_image (PNG), SDL2_mixer, SDL2_net, SDL2_TTF -- **SDL 1.2** (legacy) — with framebuffer backend, SDL_image, SDL_TTF -- **ALSA** — PCM, mixer, raw MIDI, HWDEP, sequencer plugins -- **Graphics** — FreeType, libjpeg (SIMD), libpng, libdrm, zlib -- **Utilities** — evtest, tree - -### Other Settings - -- Optimization: size (`BR2_OPTIMIZE_S`) -- LTO and OpenMP enabled -- No stack smashing protection (`BR2_SSP_NONE`) -- Shared libraries only (`BR2_SHARED_LIBS`) -- Extra libs: libasan (`BR2_TOOLCHAIN_EXTRA_LIBS="libasan"`) - -## Patches Applied - -All patches live in `docker/support/patches/`. - -### glibc 2.28 Compatibility Fixes - -Three packages fail to build against glibc 2.28 because `_IO_ftrylockfile` was removed from the private API: - -- **bison** (`package/bison/110-glibc-change-work-around.patch`) — replaces `_IO_ftrylockfile` checks with `_IO_EOF_SEEN`, adds `_IO_IN_BACKUP` macro -- **m4** (`package/m4/000-fix-fseeko.patch`) — same pattern across fflush.c, fpending.c, fpurge.c, freadahead.c, freading.c, fseeko.c, stdio-impl.h -- **e2fsprogs** (`package/e2fsprogs/000-fix-gcc-conflict.patch`) — renames `copy_file_range()` to `copy_file_chunk()` to avoid conflict with the new glibc function of the same name - -### SDL 1.2 RG35XX Patches - -- **`package/sdl/0003-rg35xx-sdlk-additions.patch`** — adds 9 RG35XX-specific scancodes and key mappings (KATAKANA, HIRAGANA, HENKAN, POWER, etc.) to SDL 1.2's framebuffer input driver. **Not relevant for SDL2.** -- **`package/sdl/0004-modernize-SDL_FBCON_DONT_CLEAR.patch`** — changes framebuffer clearing behavior: skips palette restoration and pixel clearing when `dontClearPixels` is set or `SDL_DOUBLEBUF` is used. **Not relevant for SDL2.** - -### Buildroot Framework Patch - -- **`toolchain-expose-BR2_TOOLCHAIN_EXTRA_EXTERNAL_LIBS-for-all-toolchain-types-2017.11.1.diff`** — backports `BR2_TOOLCHAIN_EXTRA_LIBS` config option so it works with the internal Buildroot toolchain (not just external). Required for the `libasan` extra lib. - -## Possible Approaches - -> **Update (2026-02):** Now that we've inspected the device (see [Target Device](#target-device)), approach A is confirmed to work — a native arm64 Docker container build produces binaries that run on the device with no issues. The Buildroot toolchain is unnecessary since all required shared libraries are already present on the device. Approaches B and C are kept below for historical reference. - -### A: Native arm64 Docker Container Build (Confirmed Working) - -Use `apt` with `dpkg --add-architecture arm64` inside a Debian Docker container to install aarch64 cross-compiler and libraries directly from Debian repos. - -**Pros:** -- Modern toolchain (GCC 12+, recent glibc, recent FFmpeg/SDL2) -- No Buildroot config to maintain, no patches needed -- Fast setup — apt install instead of multi-hour Buildroot build -- Libraries match what's on the device (Debian-based firmware) - -**Cons:** -- Library versions must match the device's firmware (or ship them) -- Less control over exact build options - -**Outline:** -```dockerfile -FROM debian:bookworm -RUN dpkg --add-architecture arm64 && apt-get update && \ - apt-get install -y gcc-aarch64-linux-gnu \ - libsdl2-dev:arm64 libsdl2-image-dev:arm64 \ - libavformat-dev:arm64 libavcodec-dev:arm64 \ - libavutil-dev:arm64 libswresample-dev:arm64 +```sh +make build # one-shot: build sdlamp2 inside the container +make shell # interactive bash shell for development +make clean # remove the Docker image ``` -### B: Fix Buildroot Config for aarch64 +The container mounts the project root at `/workspace`. Output binary is `build/sdlamp2` (`ELF 64-bit ARM aarch64`). -Stay with Buildroot but update the defconfig to target aarch64 instead of ARM. +### Image Contents -**Pros:** -- Minimal conceptual change — same pipeline, just different arch settings -- Full control over every library version +- **Base:** `ubuntu:22.04` (arm64) +- **Build tools:** `build-essential`, `pkg-config` +- **SDL2:** `libsdl2-dev`, `libsdl2-image-dev` +- **FFmpeg:** `libavformat-dev`, `libavcodec-dev`, `libavutil-dev`, `libswresample-dev` -**Cons:** -- Buildroot 2017.11 is ancient and may not have good aarch64 support -- Would likely need to upgrade Buildroot to a modern release (2024.x) -- Upgrading Buildroot may break existing patches or require new ones -- Multi-hour rebuild from scratch -- Still maintaining a custom Buildroot config long-term - -**Key config changes needed:** -``` -BR2_aarch64=y -# Remove all BR2_arm, BR2_cortex_a9, BR2_ARM_* settings -# Update kernel headers to match device -``` - -### C: Skip Docker, Cross-Compile on Host Directly - -Install `aarch64-linux-gnu-gcc` and aarch64 libraries on the host system (e.g. Arch Linux) without Docker. - -**Pros:** -- Simplest setup — just `pacman -S aarch64-linux-gnu-gcc` and aarch64 libs -- No Docker overhead -- Fast iteration - -**Cons:** -- Not reproducible across machines without documenting exact packages -- Harder to match device library versions -- Host distro package availability varies (Arch has good cross-compile support, others may not) +The image is tagged `arm64-dev` and is generic enough to reuse for other projects targeting the same device. diff --git a/docs/sdlamp2-fsd.md b/docs/sdlamp2-fsd.md index e3e82b0..25cfd7a 100644 --- a/docs/sdlamp2-fsd.md +++ b/docs/sdlamp2-fsd.md @@ -43,6 +43,10 @@ This document specifies the functional requirements for an SDL2 based media play ## 6. Changelog +### 2026-02-13 — arm64 Docker build container + +- **New build container**: Replaced the broken Buildroot cross-compilation toolchain (`docker/`) with an arm64 Ubuntu 22.04 Docker container (`docker-arm64/`). Runs via QEMU user-mode emulation on x86 hosts and matches the target device exactly (same distro, same glibc, same library versions). The project's native `make` works as-is inside the container — no cross-compilation flags needed. + ### 2026-02-13 — Replace build scripts with Makefile - **Makefile**: Replaced `build.sh` (macOS) and `build_aarch64.sh` (Linux ARM) with a single `Makefile`. Native builds use `pkg-config` to resolve SDL2 and FFmpeg flags; cross-compilation uses `CROSS_COMPILE` and `PREFIX` environment variables set by the Docker/Buildroot toolchain.