none-eabi - The rustc book (original) (raw)

The rustc book

{arm,thumb}*-none-eabi(hf)?

Common Target Details

This documentation covers details that apply to a range of bare-metal targets for 32-bit Arm CPUs. The arm-none-eabi flavor of the GNU compiler toolchain is often used to assist compilation to these targets.

Details that apply only to only a specific target in this group are covered in their own document.

Tier 2 Target List

Tier 3 Target List

Instruction Sets

There are two 32-bit instruction set architectures (ISAs) defined by Arm:

There is also a 64-bit ISA with fixed-width 32-bit instructions called the A64 ISA, but targets which implement that instruction set generally start withaarch64* and are discussed elsewhere.

Rust targets starting with arm* generate Arm (A32) code by default, whilst targets named thumb* generate Thumb (T32) code by default. Most Arm chips support both Thumb mode and Arm mode, with the notable exception that M-profile processors (thumbv*m*-none-eabi* targets) only support Thumb-mode.

Rust targets ending with eabi use the so-called soft-float ABI: functions which take f32 or f64 as arguments will have those values packed into integer registers. This means that an FPU is not required from an ABI perspective, but within a function floating-point instructions may still be used if the code is compiled with a target-cpu or target-feature option that enables FPU support.

Rust targets ending in eabihf use the so-called hard-float ABI: functions which take f32 or f64 as arguments will have them passed via FPU registers. These targets therefore require the availability of an FPU and will assume some baseline level of floating-point support is available (which can vary depending on the target). More advanced floating-point instructions may be generated if the code is compiled with a target-cpu or target-feature option that enables such additional FPU support. For example, if a given hard-float target has baseline single-precision (f32) support in hardware, there may betarget-cpu or target-feature options that tell LLVM to assume your processor in fact also has double-precision (f64) support.

You may of course use the f32 and f64 types in your code, regardless of the ABI being used, or the level of support your processor has for performing such operations in hardware. Any floating-point operations that LLVM assumes your processor cannot support will be lowered to library calls (like __aeabi_dadd) which perform the floating-point operation in software using integer instructions.

Target CPU and Target Feature options

It is possible to tell Rust (or LLVM) that you have a specific model of Arm processor, using the -C target-cpu option. You can also control whether Rust (or LLVM) will include instructions that target optional hardware features, e.g. hardware floating-point, or Advanced SIMD operations, using -C target-feature.

It is important to note that selecting a target-cpu will typically enable_all_ the optional features available from Arm on that model of CPU and your particular implementation of that CPU may not have those features available. In that case, you can use -C target-feature=-option to turn off the specific CPU features you do not have available, leaving you with the optimized instruction scheduling and support for the features you do have. More details are available in the detailed target-specific documentation.

Many target-features are currently unstable and subject to change, and if you use them you should disassemble the compiler output and manually inspect it to ensure only appropriate instructions for your CPU have been generated.

If you wish to use the target-cpu and target-feature options, you can add them to your .cargo/config.toml file alongside any other flags your project uses (likely linker related ones):

rustflags = [
  # Usual Arm bare-metal linker setup
  "-Clink-arg=-Tlink.x",
  "-Clink-arg=--nmagic",
  # tell Rust we have a Cortex-M55
  "-Ctarget-cpu=cortex-m55",
  # tell Rust our Cortex-M55 doesn't have Floating-Point M-Profile Vector
  # Extensions (but it does have everything else a Cortex-M55 could have).
  "-Ctarget-feature=-mve.fp"
]

[build]
target = "thumbv8m.main-none-eabihf"

Requirements

These targets are cross-compiled and use static linking.

By default, the lld linker included with Rust will be used; however, you may want to use the GNU linker instead. This can be obtained for Windows/Mac/Linux from the Arm Developer Website, or possibly from your OS's package manager. To use it, add the following to your .cargo/config.toml:

[target.<your-target>]
linker = "arm-none-eabi-ld"

The GNU linker can also be used by specifying arm-none-eabi-gcc as the linker. This is needed when using GCC's link time optimization.

These targets don't provide a linker script, so you'll need to bring your own according to the specific device you are using. Pass-Clink-arg=-Tyour_script.ld as a rustc argument to make the linker useyour_script.ld during linking.

For the arm* targets, Thumb-mode code generation can be enabled by using -C target-feature=+thumb-mode. Using the unstable#![feature(arm_target_feature)], the attribute #[target_feature(enable = "thumb-mode")] can be applied to individual unsafe functions to cause those functions to be compiled to Thumb-mode code.

Building Rust Programs

For the Tier 3 targets in this family, rust does not ship pre-compiled artifacts.

Just use the build-std nightly cargo feature to build the core library. You can pass this as a command line argument to cargo, or your .cargo/config.tomlfile might include the following lines:

[unstable]
build-std = ["core"]

Most of core should work as expected, with the following notes:

alloc is also supported, as long as you provide your own global allocator.

Rust programs are output as ELF files.

Testing

This is a cross-compiled target that you will need to emulate during testing.

The exact emulator that you'll need depends on the specific device you want to run your code on.

The target supports C code compiled with the arm-none-eabi target triple and a suitable -march or -mcpu flag.

gcc or clang can be used, but note that gcc uses -fshort-enums by default for arm-none* targets, while clang does not. rustc matches thegcc behavior, i.e., the size of a #[repr(C)] enum in Rust can be as little as 1 byte, rather than 4, as they are on arm-linux targets.