Bare Metal Support of Eerie (Part1)
Libc Bare Metal EmbeddedNavigating the Challenges of #![no-std]
in Eerie
The quest for #![no-std]
support in Eerie has proven to be more challenging than anticipated, particularly when delving into embedded development nuances that were not initially apparent.
Unraveling Linker errors
Rust brings many advantages, one of which is freedom from dealing with traditional C/C++ linker errors. However, linking Rust and C in an embedded environment has presented a unique set of challenges. A particularly vexing issue arose when encountering a linker error that looked like this:
rust-lld: error: undefined symbol: ceilf
>>> referenced by ops.h:551 (/Users/gmmyung/Developer/eerie/iree-sys/iree/runtime/src/iree/vm/ops.h:551)
>>> dispatch.c.obj:(vm_ceil_f32) in archive /Users/gmmyung/Developer/eerie-embedded/target/thumbv7em-none-eabihf/debug/build/iree-sys-a5c1d2279731491b/out/runtime_build/build
/lib/libiree.a
>>> referenced by elementwise.c:173 (/Users/gmmyung/Developer/eerie/iree-sys/iree/runtime
/src/iree/modules/vmvx/elementwise.c:173)
>>> elementwise.c.obj:(iree_uk_generic_x32u_op) in archive /Users/gmmyung/Developer/eerie-embedded/target/thumbv7em-none-eabihf/debug/build/iree-sys-a5c1d2279731491b/out/runtime_build/build/lib/libiree.a
This pointed to the fact that libm
was not being linked with the rust-lld
linker. In non-embedded targets, the Rust linker automatically incorporates the pre-built standard library. However, in embedded targets, standard library symbols are not provided by default, leading to three potential solutions.
- Specify the exact location of the precompiled binaries of
libm
in the linker flags. - Use the
arm-none-eabi
linker to link the precompiledlibm
binaries. - Rewrite all
libm
functions in Rust.
While the first option seemed obvious, there were nuances, such as precompiled binaries for the thumbv7em
architecture, requiring manual configuration for the specific FPU type. The second option, involving the arm-none-eabi
linker, presented transitive challenges for users setting up the linker in the crate’s dependency graph, making it less suitable for minimal configuration distribution. The third option, rewriting libc in Rust, became the chosen path.
Tackling libm linker errors
Fortunately, multiple implementations of libc
, libm
, and other compiler builtins exist. The libm crate is a MUSL libm port to Rust, serves as a drop-in replacement for GNU libm
. Wrapper crates like externc-libm provide #[no_mangle]
symbols to libm functions.
Unveiling Further Challenges
As the journey progressed, another linker error surfaced:
rust-lld: error: undefined symbol: lroundf
>>> referenced by ops.h:606 (/Users/gmmyung/Developer/eerie/iree-sys/iree/runtime/src/iree/vm/ops.h:606)
>>> dispatch.c.obj:(vm_cast_f32si32) in archive /Users/gmmyung/Developer/eerie-embedded/target/thumbv7em-none-eabihf/debug/build/iree-sys-a5c1d2279731491b/out/runtime_build/build/lib/libiree.a
The lroundf
function, part of <tgmath.h>
, an extension to <math.h>
and <complex.h>
, posed a challenge. This necessitated the implementation of functions like lroundf
from scratch.
Embarking on libc Implementation
Various attempts at re-implementing libc in Rust, such as rusl, c-ward, and relibc used for the Redox OS project, did not support bare metal. The need arose to implement libc functions from the ground up. Tinyrlibc, a libc
implementation for embedded targets, served as a starting point, though incomplete. The journey began with implementing fundamental functions like memcpy
, memset
, and memcmp
, ensuring compatibility with the C standard. Additionally, malloc
and free
functions, requiring extern crate alloc
in Rust crates, were successfully implemented.
Concluding Strides
While the path to implementing libc in Rust posed numerous challenges, solutions were found and basic libc functions were successfully implemented. The current workaround involves manual linking using the GNU toolchain, but continuous efforts towards a pure Rust libc implementation promise a smoother user experience. The journey continues, with plans to implement more functions in the pursuit of progress.