-
Notifications
You must be signed in to change notification settings - Fork 0
compiler-rt: add bare metal ARM runtimes and picolibc #3488
Merged
Paul Menzel (pmenzel)
merged 4 commits into
master
from
compiler-rt-add-bare-metal-arm-runtimes
Oct 16, 2026
Merged
compiler-rt: add bare metal ARM runtimes and picolibc #3488
Paul Menzel (pmenzel)
merged 4 commits into
master
from
compiler-rt-add-bare-metal-arm-runtimes
Oct 16, 2026
+575
−1
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The standalone build only builds the runtimes for the host, so
/usr/lib/clang/23/lib contains nothing but linux/, and linking for a
bare-metal Arm target fails:
$ clang --target=arm-none-eabi -mcpu=cortex-m4 -mfloat-abi=hard \
-fuse-ld=lld -nostartfiles -nolibc -Wl,-e,_start -o t.elf t.c
ld.lld: error: cannot open /usr/lib/clang/23/lib/arm-unknown-none-eabi/libclang_rt.builtins.a: No such file or directory
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Build the builtins a second time for six Cortex-M variants, named like
those of Arm’s LLVM Embedded Toolchain:
armv6m_soft_nofp Cortex-M0/M0+/M1/M23
armv7m_soft_nofp Cortex-M3, and any later core with
-mfloat-abi=soft
armv7em_soft_fpv4_sp_d16 Cortex-M4/M7, -mfloat-abi=softfp (default)
armv7em_hard_fpv4_sp_d16 Cortex-M4/M7, -mfloat-abi=hard
armv8m.main_soft_fp Cortex-M33/M35P, -mfloat-abi=softfp
armv8m.main_hard_fp Cortex-M33/M35P, -mfloat-abi=hard
They are installed into /usr/lib/clang-runtimes/arm-none-eabi/<variant>,
clang’s default bare-metal sysroot, together with a multilib.yaml there.
clang then picks the variant from -mcpu, -mfloat-abi and -mfpu, so
--target=arm-none-eabi, armv7em-none-eabihf and thumbv7em-none-eabihf
all work. With per-target directories in the resource directory instead,
the --target spelling would have to match the directory name, and
--target=arm-none-eabi -mfloat-abi=hard would silently link the
soft-float library.
The softfp variants are needed because clang defaults to
-mfloat-abi=softfp for a core with FPU. That code uses the FPU, but the
crt0 of a variant without FPU does not enable it, so with only the
soft-float variants, picolibc’s hello world built with
-mcpu=cortex-m33 faulted in QEMU on its first VFP instruction, a VLDR
(encoding ed9f 0b17):
ARM fault: hardfault
LR: 0x100000b1
PC: 0x100001e2
The variants without FPU therefore require -mfpu=none, and a core
without a matching variant, for example the Cortex-M55, gets clang’s
-Wmissing-multilib warning instead of a library that crashes.
compiler-rt appends the normalized target to the library directory, but
clang only searches the variant’s lib directory, so move the archive up.
The C library comes from to be added picolibc.be0, which uses the same
variants.
Assisted-by: claude-opus-5.5 (Claude Code)
clang finds the compiler-rt builtins for bare-metal Arm now, but there
is no C library for these targets:
$ echo '#include <stdio.h>' | clang --target=arm-none-eabi -mcpu=cortex-m4 -x c -c - -o /dev/null
<stdin>:1:10: fatal error: 'stdio.h' file not found
picolibc is the C library for embedded systems that Arm’s LLVM Embedded
Toolchain uses. Build it for the six Cortex-M variants of
compiler-rt.be0, into the same directories below
/usr/lib/clang-runtimes/arm-none-eabi, so that clang’s multilib.yaml
selects both together.
Each variant gets its own meson cross file, as picolibc’s own
cross-clang-*.txt files call arm-none-eabi-ar and link against libgcc
from an arm-none-eabi-gcc installation. Deviations from them:
- -nostdlibinc, so that the headers of an installed picolibc do not
shadow the ones being built.
- c_ld = 'lld', as clang’s bare-metal driver defaults to GNU ld, which
on MarIuX only supports x86:
ERROR: Unable to detect linker for compiler `clang --target=thumbv6m-none-eabi -mfloat-abi=soft -nostdlibinc -nostdlib -Wl,--version`
stderr: /usr/bin/ld: unrecognised emulation mode: armelf
- No -fshort-enums. picolibc passes it to match arm-none-eabi-gcc, but
clang defaults to 32-bit enums, and the library has to match the code
compiled with clang that links against it.
-Dspecsdir=none, as picolibc would otherwise install picolibc.specs into
clang’s install directory, /usr/bin.
Linked with the defaults (crt0-semihost.o, picolibc.ld, -lsemihost,
-fuse-ld=lld), a hello world doing 64-bit division and float/double
printf runs in QEMU 11.1.0 for every variant:
-mcpu=cortex-m0plus microbit armv6m_soft_nofp
-mcpu=cortex-m3 mps2-an385 armv7m_soft_nofp
-mcpu=cortex-m4 -mfloat-abi=soft mps2-an386 armv7m_soft_nofp
-mcpu=cortex-m4 mps2-an386 armv7em_soft_fpv4_sp_d16
-mcpu=cortex-m4 -mfloat-abi=hard mps2-an386 armv7em_hard_fpv4_sp_d16
--target=thumbv7em-none-eabihf mps2-an386 armv7em_hard_fpv4_sp_d16
-mcpu=cortex-m7 mps2-an500 armv7em_soft_fpv4_sp_d16
-mcpu=cortex-m7 -mfloat-abi=hard mps2-an500 armv7em_hard_fpv4_sp_d16
-mcpu=cortex-m33 mps2-an505 armv8m.main_soft_fp
-mcpu=cortex-m33 -mfloat-abi=hard mps2-an505 armv8m.main_hard_fp
-mcpu=cortex-m33 -mfloat-abi=soft mps2-an505 armv7m_soft_nofp
hello: 176366841446 6.250 0.333333
Assisted-by: claude-opus-5.5 (Claude Code)
compiler-rt.be0 and picolibc.be0 build the same six Cortex-M variants
into /usr/lib/clang-runtimes, and multilib.yaml lets clang pick one. A
wrong pick still links and only fails on the hardware, so check the
selection for 17 combinations of core, float ABI and target spelling,
the files of every variant, and run a program doing 64-bit division and
float and double printf for eleven of them in QEMU with semihosting.
Like llvm-smoke-test, --prefix mounts packages over /usr in a user
namespace, so they are tested before they are installed.
With the packages from this branch:
$ TMPDIR=/dev/shm scripts/smoke-tests/baremetal-arm-smoke-test \
--qemu /pkg/qemu-11.1.0-0/bin/qemu-system-arm \
--prefix compiler-rt-23.1.3-1.x86_64.bee.tar.bz2 \
--prefix picolibc-1.8.12-0.x86_64.bee.tar.bz2
[…]
OK Cortex-M33 softfp runs on mps2-an505 with arm-none-eabi/armv8m.main_soft_fp
OK Cortex-M33 hard runs on mps2-an505 with arm-none-eabi/armv8m.main_hard_fp
OK default crt0, libc and builtins from arm-none-eabi/armv7em_hard_fpv4_sp_d16
PASS (0 failed, 0 warned)
The QEMU 5.1.0 in /usr/bin has no Cortex-M4 or Cortex-M7 board, so with
it those six programs are only linked, and reported as WARN.
With the first draft of multilib.yaml, which had no softfp variants and
did not require -mfpu=none for the variants without FPU, it fails:
FAIL Cortex-M4 softfp (--target=arm-none-eabi -mcpu=cortex-m4): expected arm-none-eabi/armv7em_soft_fpv4_sp_d16, got 'arm-none-eabi/armv7m_soft_nofp'
[…]
FAIL Cortex-M4 softfp on mps2-an386: exit status 1: ARM fault: hardfault
FAIL Cortex-M7 softfp on mps2-an500: exit status 1: ARM fault: hardfault
FAIL Cortex-M33 softfp on mps2-an505: exit status 1: ARM fault: hardfault
FAIL (7 failed, 0 warned)
Every link passes -fuse-ld=lld. For bare-metal targets clang defaults
to the GNU ld in /usr/bin, which on MarIuX only knows x86:
$ clang --target=arm-none-eabi -mcpu=cortex-m3 -T picolibc.ld \
-o ld.elf hello.c -lsemihost
/usr/bin/ld: unrecognised emulation mode: armelf
That is clang’s configuration, not a fault of the runtimes, so it is
not checked. CLANG_DEFAULT_LINKER would change the host’s linker too;
clang configuration files per target triple are the better candidate.
Two things found on the way: an automount such as /pkg/qemu-11.1.0-0
cannot be triggered from within the user namespace ("Too many levels of
symbolic links"), so the script runs QEMU once before entering it; and
QEMU 11 writes the semihosting console to stderr unless that is a tty.
Assisted-by: claude-opus-5.5 (Claude Code)
For --prefix, the smoke tests re-run themselves in a mount namespace
made with `unshare --user --map-root-user --mount`, and unshare(1)
defaults to private mount propagation. An automount triggered from
inside, such as a /pkg package not yet mounted, is then mounted by the
daemon in the host’s namespace and never shows up in the private copy:
$ unshare --user --map-root-user --mount ls /pkg/boost-1.86.0-0
ls: cannot open directory '/pkg/boost-1.86.0-0': Too many levels of symbolic links
So pass `--propagation slave`: host mounts, the automounts included,
then reach the namespace, while the overlay over /usr and every other
mount made inside still do not leak out to the host.
$ unshare --user --map-root-user --mount --propagation slave \
ls /pkg/perl-5.34.0-0/profile
/pkg/perl-5.34.0-0/profile
This makes running QEMU once before re-executing, which
baremetal-arm-smoke-test did to mount /pkg/qemu-11.1.0-0, otiose, so
drop it. Now every /pkg path works, not only QEMU’s.
Host mount changes now also reach the namespace, among them the autofs
expiry after timeout=300, but the next access simply mounts it again.
With /pkg/qemu-11.1.0-0 not mounted beforehand:
$ TMPDIR=/dev/shm scripts/smoke-tests/baremetal-arm-smoke-test \
--qemu /pkg/qemu-11.1.0-0/bin/qemu-system-arm \
--prefix compiler-rt-23.1.3-1.x86_64.bee.tar.bz2 \
--prefix picolibc-1.8.12-0.x86_64.bee.tar.bz2
testing, over /usr: /dev/shm/baremetal-arm-smoke-d9n6vdgy/0 /dev/shm/baremetal-arm-smoke-d9n6vdgy/1
running with /pkg/qemu-11.1.0-0/bin/qemu-system-arm (QEMU emulator version 11.1.0)
[…]
OK Cortex-M33 hard runs on mps2-an505 with arm-none-eabi/armv8m.main_hard_fp
OK default crt0, libc and builtins from arm-none-eabi/armv7em_hard_fpv4_sp_d16
PASS (0 failed, 0 warned)
Assisted-by: claude-opus-5.5 (Claude Code)
871e3a5 to
f1a9e32
Compare
Sign in
to join this conversation on GitHub.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Update/install with
sudo bee update compiler-rt-23.1.3-1 picolibc-1.8.12-0.