Flutter on ARMv6

May 27, 2024

Hannes Winkler

uint32_t extend_ub(uint32_t byte) {
    return (uint8_t) byte;
}
int32_t extend_sb(int32_t byte) {
    return (int8_t) byte;
}

uint32_t extend_uw(uint32_t word) {
    return (uint16_t) word;
}
int32_t extend_sw(int32_t word) {
    return (int16_t) word;
}
extend_ub:
        uxtb    r0, r0
        bx      lr
extend_sb:
        sxtb    r0, r0
        bx      lr

extend_uw:
        uxth    r0, r0
        bx      lr
extend_sw:
        sxth    r0, r0
        bx      lr
void Assembler::ExtractClassIdFromTags(Register result,
                                       Register tags,
                                       Condition cond) {
  ASSERT(target::UntaggedObject::kClassIdTagPos == 12);
  ASSERT(target::UntaggedObject::kClassIdTagSize == 20);
  ubfx(result, tags, target::UntaggedObject::kClassIdTagPos,
       target::UntaggedObject::kClassIdTagSize, cond);
}
int load_atomic(atomic_int *a) {
    return atomic_load_explicit(a, memory_order_seq_cst);
}
load_atomic:
        mov     r1, #0
        ldr     r0, [r0]
        mcr     p15, #0, r1, c7, c10, #5
        bx      lr

This content can not be viewed with respect to your privacy. Allow statistics and marketing in your or view on YouTube.

export LLVM_CHECKOUT=~/llvm-project
export LLVM_INSTALL=~/llvm-install

apt update && apt install cmake ninja-build clang

# This would be to clone the exact commit that flutter 3.19 uses:
#  git clone -n https://llvm.googlesource.com/llvm-project.git $LLVM_CHECKOUT
#  pushd $LLVM_CHECKOUT
#  git checkout 725656bdd885483c39f482a01ea25d67acf39c46
#  popd

# However I'll use upstream clang 18.1.0 here, as that's close enough and we can
# do a shallow clone.
git clone --depth 1 -b llvmorg-18.1.0 https://github.com/llvm/llvm-project.git $LLVM_CHECKOUT

# Normally, you could install g++-arm-linux-gnueabihf and get some
# cross-compilation headers/libc, however debians `arm-linux-gnueabihf` is
# actually armv7, so we have to use our own here.
curl https://nextcloud.kdab.com/s/ncfRECJwZXgzA4d/download/armv6-linux-gnueabihf-sysroot.tar.xz | tar -xJ
export SYSROOT=$PWD/armv6-linux-gnueabihf-sysroot

cmake \
  -S $LLVM_CHECKOUT/llvm \
  -B $LLVM_CHECKOUT/build \
  -GNinja \
  -DCMAKE_BUILD_TYPE=Release \
  -DLLVM_TARGETS_TO_BUILD=ARM \
  -DLLVM_DEFAULT_TARGET_TRIPLE=armv6-unknown-linux-gnueabihf \
  -DLLVM_ENABLE_PROJECTS="clang;lld" \
  -DLLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi;libunwind" \
  -DCLANG_DEFAULT_LINKER=lld \
  -DCLANG_DEFAULT_OBJCOPY=llvm-objcopy \
  -DCLANG_DEFAULT_RTLIB=compiler-rt \
  -DCLANG_DEFAULT_UNWINDLIB=libunwind \
  -DCLANG_DEFAULT_CXX_STDLIB=libc++ \
  -DLLVM_BUILTIN_TARGETS=armv6-unknown-linux-gnueabihf \
  -DBUILTINS_armv6-unknown-linux-gnueabihf_CMAKE_SYSTEM_NAME=Linux \
  -DBUILTINS_armv6-unknown-linux-gnueabihf_CMAKE_SYSROOT=$SYSROOT \
  -DBUILTINS_armv6-unknown-linux-gnueabihf_PYTHON_EXECUTABLE:PATH=$(which python) \
  -DBUILTINS_armv6-unknown-linux-gnueabihf_Python_EXECUTABLE:PATH=$(which python) \
  -DBUILTINS_armv6-unknown-linux-gnueabihf_Python3_EXECUTABLE:PATH=$(which python3) \
  -DBUILTINS_armv6-unknown-linux-gnueabihf_CMAKE_C_FLAGS="-march=armv6 -mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mfpu=vfp" \
  -DBUILTINS_armv6-unknown-linux-gnueabihf_CMAKE_CXX_FLAGS="-march=armv6 -mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mfpu=vfp" \
  -DBUILTINS_armv6-unknown-linux-gnueabihf_CMAKE_ASM_FLAGS="-march=armv6 -mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mfpu=vfp" \
  -DLLVM_RUNTIME_TARGETS=armv6-unknown-linux-gnueabihf \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_CMAKE_SYSTEM_NAME=Linux \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_CMAKE_SYSROOT=$SYSROOT \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_CMAKE_C_FLAGS="-march=armv6 -mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mfpu=vfp" \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_CMAKE_CXX_FLAGS="-march=armv6 -mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mfpu=vfp" \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_CMAKE_ASM_FLAGS="-march=armv6 -mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mfpu=vfp" \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_PYTHON_EXECUTABLE:PATH=$(which python) \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_Python_EXECUTABLE:PATH=$(which python) \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_Python3_EXECUTABLE:PATH=$(which python3) \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_COMPILER_RT_USE_BUILTINS_LIBRARY=OFF \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_COMPILER_RT_ENABLE_STATIC_UNWINDER=ON \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_COMPILER_RT_STATIC_CXX_LIBRARY=ON \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_COMPILER_RT_BUILD_BUILTINS=ON \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_COMPILER_RT_BUILD_LIBFUZZER=OFF \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_COMPILER_RT_BUILD_MEMPROF=OFF \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_COMPILER_RT_BUILD_PROFILE=OFF \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_COMPILER_RT_BUILD_SANITIZERS=OFF \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_COMPILER_RT_BUILD_XRAY=OFF \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_LIBUNWIND_ENABLE_SHARED=OFF \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_LIBUNWIND_USE_COMPILER_RT=ON \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_LIBCXXABI_USE_COMPILER_RT=ON \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_LIBCXXABI_ENABLE_SHARED=OFF \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_LIBCXXABI_USE_LLVM_UNWINDER=ON \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_LIBCXXABI_ENABLE_STATIC_UNWINDER=ON \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_LIBCXX_USE_COMPILER_RT=ON \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_LIBCXX_ENABLE_SHARED=OFF \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_LIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_LIBCXX_ENABLE_ABI_LINKER_SCRIPT=OFF \
  -DRUNTIMES_armv6-unknown-linux-gnueabihf_LIBCXX_ABI_VERSION=2 \
  -DCMAKE_INSTALL_PREFIX=$LLVM_INSTALL

ninja $LLVM_CHECKOUT/build install
apt update && apt install python-is-python3 git curl xz-utils pkg-config

# Setup depot_tools
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH="$PWD/depot_tools":"$PATH"
gclient --version  # This sets up gclient / depot_tools.

export ENGINE_ROOT=$HOME/engine

# Fetch engine (stable 3.19.6)
mkdir -p $ENGINE_ROOT && cd $ENGINE_ROOT
gclient config --spec 'solutions = [
  {
    "custom_deps": {},
    "deps_file": "DEPS",
    "managed": False,
    "name": "src/flutter",
    "safesync_url": "",
    "url": "https://github.com/flutter/engine.git",
  },
]
'
gclient sync --rev src/flutter@3.19.6

# Install sysroot for linux arm
$ENGINE_ROOT/src/build/linux/sysroot_scripts/install_sysroot.py --arch=arm
export PATCHES=$HOME/flutter-armv6-patches
git clone https://github.com/ardera/flutter-armv6-patches $PATCHES

cd $ENGINE_ROOT/src
git am $PATCHES/buildroot-patches/*
cd $ENGINE_ROOT/src/flutter
git am $PATCHES/engine-patches/*
cd $ENGINE_ROOT/third_party/dart
git am $PATCHES/dart-patches/*
cd $ENGINE_ROOT/third_party/libpng
git am $PATCHES/libpng-patches/*
# For whatever reason, clang tries to link against the static libraries
# in that dir instead of the shared ones with same name (e.g. libm.a instead of
# libm.so), those are not compiled with -fPIC though so linking will fail.
#
# Adding -Bdynamic, -shared etc to the compiler command-line doesn't work.
# (And at least -shared is already there anyway)
rm $SYSROOT/lib/arm-linux-gnueabihf/*.a

# Configure, tune for the Pi Zero CPU
./flutter/tools/gn \
  --embedder-for-target \
  --no-build-embedder-examples \
  --disable-desktop-embeddings \
  --no-build-glfw-shell \
  --target-os linux \
  --linux-cpu arm \
  --arm-float-abi hard \
  --runtime-mode profile \
  --no-dart-version-git-info \
  --gn-args 'verify_sdk_hash=false' \
  --target-dir 'linux_profile_armv6' \
  --target-triple armv6-linux-gnueabihf \
  --target-toolchain $LLVM_INSTALL \
  --target-sysroot $SYSROOT \
  --gn-args 'system_libdir="lib/arm-linux-gnueabihf"' \
  --gn-args 'arm_target = ""' \
  --gn-args 'arm_arch="armv6"' \
  --gn-args 'arm_cpu="arm1176jzf-s"' \
  --gn-args 'arm_tune="arm1176jzf-s"' \
  --gn-args 'arm_fpu="vfp"' \
  --gn-args 'arm_use_neon = false' \
  --gn-args 'dart_target_arch="armv6"'

# Build
ninja -C out/linux_profile_armv6
# go to $HOME again
cd

# Install the flutter SDK
git clone --depth 1 -b 3.19.6 https://github.com/flutter/flutter.git
export PATH="$PATH":"$PWD/flutter/bin"
flutter precache

# Fetch the the wonders app
# This one has some patches to remove some unsupported plugins.
git clone https://github.com/ardera/flutter-wonderous-app.git wonders && cd wonders

## App build
# First, build the asset bundle. Normally there's `--local-engine`
# so we don't have to do the awkward stuff below, but for our cases this
# unfortunately doesn't work.
flutter build bundle

# Compile the app dart code for profile mode.
# Just a bunch of manual compiler invocations.
$(dirname $(which flutter))/cache/dart-sdk/bin/dartaotruntime \
  --disable-dart-dev \
  $(dirname $(which flutter))/cache/dart-sdk/bin/snapshots/frontend_server_aot.dart.snapshot \
  --aot \
  --tfa \
  --sdk-root $(dirname $(which flutter))/cache/artifacts/engine/common/flutter_patched_sdk/ \
 --target=flutter \
 --no-print-incremental-dependencies \
 -Ddart.vm.profile=true -Ddart.vm.product=false \
 --packages ./.dart_tool/package_config.json \
 --output-dill ./build/app.dill \
 --filesystem-scheme org-dartlang-root \
 --verbose \
 package:wonders/main.dart

$ENGINE_ROOT/src/out/linux_profile_armv6/clang_*/gen_snapshot \
	--deterministic \
	--snapshot_kind=app-aot-elf \
	--elf=build/flutter_assets/app.so \
	--strip \
	--sim-use-hardfp \
	./build/app.dill

3 comments on "Flutter on ARMv6"

hochmax

May 28, 2024, 9:49 AM

Impressive work

Reply to hochmax

Your Email address will not be published

aaron.yang

Dec 10, 2024, 1:37 AM

How about ARMv7, did you know how to fix ?

Reply to aaron.yang

Your Email address will not be published

Markus

May 23, 2025, 4:35 AM

Nice post! FWIW, SharedPreferences will work on custom embedders with the following somewhere at the beginning of main: if (Platform.isLinux) { PathProviderLinux.registerWith(); SharedPreferencesLinux.registerWith(); } I believe meta-flutter has another workaround for this that makes this obsolete, would have to check. While it's cool to see this running on armv6, I'm a bit sceptical about such undertakings: The next flutter release might always do something that you cannot easily patch anymore, then you'd be stuck with an ancient flutter version forever. It's a nice playground and cool to see that it's possible, but I wouldn't do anything work-related with it. Anyhow, what usecases do you see here?

Reply to Markus

Your Email address will not be published

Leave a Comment

Your Email address will not be published