sdlamp2/docs/TOOLCHAIN.md
Michael Smith b1c72ef876 Document cross-compilation toolchain findings and aarch64 mismatch
The Docker/Buildroot toolchain produces 32-bit ARM binaries but the
RG35XX target runs a 64-bit aarch64 userland. Add docs/TOOLCHAIN.md
covering the full pipeline, config details, patches, and three possible
approaches to fix the architecture mismatch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 13:04:12 +01:00

7.7 KiB

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.

Current Toolchain Architecture

Pipeline Overview

The toolchain is built via a Docker container defined in docker/:

  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:
    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/Makefilemake shell builds the Docker image (tag rg35xx-toolchain) and drops into an interactive bash shell with ./workspace mounted at /root/workspace.

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

Toolchain Versions

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)

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

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:

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

B: Fix Buildroot Config for aarch64

Stay with Buildroot but update the defconfig to target aarch64 instead of ARM.

Pros:

  • Minimal conceptual change — same pipeline, just different arch settings
  • Full control over every library version

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)