A note on compiler support

This book makes use of a built-in compiler target, the thumbv7m-none-eabi, for which the Rust team distributes a rust-std component, which is a pre-compiled collection of crates like core and std.

If you want to attempt replicating the contents of this book for a different target architecture, you need to take into account the different levels of support that Rust provides for (compilation) targets.

LLVM support

As of Rust 1.28, the official Rust compiler, rustc, uses LLVM for (machine) code generation. The minimal level of support Rust provides for an architecture is having its LLVM backend enabled in rustc. You can see all the architectures that rustc supports, through LLVM, by running the following command:

$ # you need to have `cargo-binutils` installed to run this command
$ cargo objdump -- -version
LLVM (http://llvm.org/):
  LLVM version 7.0.0svn
  Optimized build.
  Default target: x86_64-unknown-linux-gnu
  Host CPU: skylake

  Registered Targets:
    aarch64    - AArch64 (little endian)
    aarch64_be - AArch64 (big endian)
    arm        - ARM
    arm64      - ARM64 (little endian)
    armeb      - ARM (big endian)
    hexagon    - Hexagon
    mips       - Mips
    mips64     - Mips64 [experimental]
    mips64el   - Mips64el [experimental]
    mipsel     - Mipsel
    msp430     - MSP430 [experimental]
    nvptx      - NVIDIA PTX 32-bit
    nvptx64    - NVIDIA PTX 64-bit
    ppc32      - PowerPC 32
    ppc64      - PowerPC 64
    ppc64le    - PowerPC 64 LE
    sparc      - Sparc
    sparcel    - Sparc LE
    sparcv9    - Sparc V9
    systemz    - SystemZ
    thumb      - Thumb
    thumbeb    - Thumb (big endian)
    wasm32     - WebAssembly 32-bit
    wasm64     - WebAssembly 64-bit
    x86        - 32-bit X86: Pentium-Pro and above
    x86-64     - 64-bit X86: EM64T and AMD64

If LLVM supports the architecture you are interested in, but rustc is built with the backend disabled (which is the case of AVR as of Rust 1.28), then you will need to modify the Rust source enabling it. The first two commits of PR rust-lang/rust#52787 give you an idea of the required changes.

On the other hand, if LLVM doesn't support the architecture, but a fork of LLVM does, you will have to replace the original version of LLVM with the fork before building rustc. The Rust build system allows this and in principle it should just require changing the llvm submodule to point to the fork.

If your target architecture is only supported by some vendor provided GCC, you have the option of using mrustc, an unofficial Rust compiler, to translate your Rust program into C code and then compile that using GCC.

Built-in target

A compilation target is more than just its architecture. Each target has a specification associated to it that describes, among other things, its architecture, its operating system and the default linker.

The Rust compiler knows about several targets. These are said to be built into the compiler and can be listed by running the following command:

$ rustc --print target-list | column
aarch64-fuchsia                 mips64el-unknown-linux-gnuabi64
aarch64-linux-android           mipsel-unknown-linux-gnu
aarch64-unknown-cloudabi        mipsel-unknown-linux-musl
aarch64-unknown-freebsd         mipsel-unknown-linux-uclibc
aarch64-unknown-linux-gnu       msp430-none-elf
aarch64-unknown-linux-musl      powerpc-unknown-linux-gnu
aarch64-unknown-openbsd         powerpc-unknown-linux-gnuspe
arm-linux-androideabi           powerpc-unknown-netbsd
arm-unknown-linux-gnueabi       powerpc64-unknown-linux-gnu
arm-unknown-linux-gnueabihf     powerpc64le-unknown-linux-gnu
arm-unknown-linux-musleabi      powerpc64le-unknown-linux-musl
arm-unknown-linux-musleabihf    s390x-unknown-linux-gnu
armebv7r-none-eabihf            sparc-unknown-linux-gnu
armv4t-unknown-linux-gnueabi    sparc64-unknown-linux-gnu
armv5te-unknown-linux-gnueabi   sparc64-unknown-netbsd
armv5te-unknown-linux-musleabi  sparcv9-sun-solaris
armv6-unknown-netbsd-eabihf     thumbv6m-none-eabi
armv7-linux-androideabi         thumbv7em-none-eabi
armv7-unknown-cloudabi-eabihf   thumbv7em-none-eabihf
armv7-unknown-linux-gnueabihf   thumbv7m-none-eabi
armv7-unknown-linux-musleabihf  wasm32-experimental-emscripten
armv7-unknown-netbsd-eabihf     wasm32-unknown-emscripten
asmjs-unknown-emscripten        wasm32-unknown-unknown
i586-pc-windows-msvc            x86_64-apple-darwin
i586-unknown-linux-gnu          x86_64-fuchsia
i586-unknown-linux-musl         x86_64-linux-android
i686-apple-darwin               x86_64-pc-windows-gnu
i686-linux-android              x86_64-pc-windows-msvc
i686-pc-windows-gnu             x86_64-rumprun-netbsd
i686-pc-windows-msvc            x86_64-sun-solaris
i686-unknown-cloudabi           x86_64-unknown-bitrig
i686-unknown-dragonfly          x86_64-unknown-cloudabi
i686-unknown-freebsd            x86_64-unknown-dragonfly
i686-unknown-haiku              x86_64-unknown-freebsd
i686-unknown-linux-gnu          x86_64-unknown-haiku
i686-unknown-linux-musl         x86_64-unknown-l4re-uclibc
i686-unknown-netbsd             x86_64-unknown-linux-gnu
i686-unknown-openbsd            x86_64-unknown-linux-gnux32
mips-unknown-linux-gnu          x86_64-unknown-linux-musl
mips-unknown-linux-musl         x86_64-unknown-netbsd
mips-unknown-linux-uclibc       x86_64-unknown-openbsd
mips64-unknown-linux-gnuabi64   x86_64-unknown-redox

You can print the specification of any of these targets using the following command:

$ rustc +nightly -Z unstable-options --print target-spec-json --target thumbv7m-none-eabi
{
  "abi-blacklist": [
    "stdcall",
    "fastcall",
    "vectorcall",
    "thiscall",
    "win64",
    "sysv64"
  ],
  "arch": "arm",
  "data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
  "emit-debug-gdb-scripts": false,
  "env": "",
  "executables": true,
  "is-builtin": true,
  "linker": "arm-none-eabi-gcc",
  "linker-flavor": "gcc",
  "llvm-target": "thumbv7m-none-eabi",
  "max-atomic-width": 32,
  "os": "none",
  "panic-strategy": "abort",
  "relocation-model": "static",
  "target-c-int-width": "32",
  "target-endian": "little",
  "target-pointer-width": "32",
  "vendor": ""
}

If none of these built-in targets seems appropriate for your target system, you'll have to create a custom target by writing your own target specification file in JSON format. The recommended way is to dump the specification of a built-in target that's similar to your target system into a file and then tweak it to match the properties of your target system. To do so, use the previously shown command, rustc --print target-spec-json. As of Rust 1.28, there's no up to date documentation on what each of the fields of a target specification mean, other than the compiler source code.

Once you have a target specification file you can refer to it by its path or by its name if its in the current directory or in $RUST_TARGET_PATH.

$ rustc +nightly -Z unstable-options --print target-spec-json \
      --target thumbv7m-none-eabi \
      > foo.json

$ rustc --print cfg --target foo.json # or just --target foo
debug_assertions
target_arch="arm"
target_endian="little"
target_env=""
target_feature="mclass"
target_feature="v7"
target_has_atomic="16"
target_has_atomic="32"
target_has_atomic="8"
target_has_atomic="cas"
target_has_atomic="ptr"
target_os="none"
target_pointer_width="32"
target_vendor=""

rust-std component

For some of the built-in target the Rust team distributes rust-std components via rustup. This component is a collection of pre-compiled crates like core and std, and it's required for cross compilation.

You can find the list of targets that have a rust-std component available via rustup by running the following command:

$ rustup target list | column
aarch64-apple-ios                       mips64-unknown-linux-gnuabi64
aarch64-linux-android                   mips64el-unknown-linux-gnuabi64
aarch64-unknown-fuchsia                 mipsel-unknown-linux-gnu
aarch64-unknown-linux-gnu               mipsel-unknown-linux-musl
aarch64-unknown-linux-musl              powerpc-unknown-linux-gnu
arm-linux-androideabi                   powerpc64-unknown-linux-gnu
arm-unknown-linux-gnueabi               powerpc64le-unknown-linux-gnu
arm-unknown-linux-gnueabihf             s390x-unknown-linux-gnu
arm-unknown-linux-musleabi              sparc64-unknown-linux-gnu
arm-unknown-linux-musleabihf            sparcv9-sun-solaris
armv5te-unknown-linux-gnueabi           thumbv6m-none-eabi
armv5te-unknown-linux-musleabi          thumbv7em-none-eabi
armv7-apple-ios                         thumbv7em-none-eabihf
armv7-linux-androideabi                 thumbv7m-none-eabi
armv7-unknown-linux-gnueabihf           wasm32-unknown-emscripten
armv7-unknown-linux-musleabihf          wasm32-unknown-unknown
armv7s-apple-ios                        x86_64-apple-darwin
asmjs-unknown-emscripten                x86_64-apple-ios
i386-apple-ios                          x86_64-linux-android
i586-pc-windows-msvc                    x86_64-pc-windows-gnu
i586-unknown-linux-gnu                  x86_64-pc-windows-msvc
i586-unknown-linux-musl                 x86_64-rumprun-netbsd
i686-apple-darwin                       x86_64-sun-solaris
i686-linux-android                      x86_64-unknown-cloudabi
i686-pc-windows-gnu                     x86_64-unknown-freebsd
i686-pc-windows-msvc                    x86_64-unknown-fuchsia
i686-unknown-freebsd                    x86_64-unknown-linux-gnu (default)
i686-unknown-linux-gnu                  x86_64-unknown-linux-gnux32
i686-unknown-linux-musl                 x86_64-unknown-linux-musl
mips-unknown-linux-gnu                  x86_64-unknown-netbsd
mips-unknown-linux-musl                 x86_64-unknown-redox

If there's no rust-std component for your target or you are using a custom target, then you'll have to use a tool like Xargo to have Cargo compile the core crate on the fly. Note that Xargo requires a nightly toolchain; the long term plan is to upstream Xargo's functionality into Cargo and eventually have that functionality available on stable.