diff --git a/ci/asm/app/release.objdump b/ci/asm/app/release.objdump index a627559..c98c9ee 100644 --- a/ci/asm/app/release.objdump +++ b/ci/asm/app/release.objdump @@ -11,7 +11,9 @@ main: trap Reset: - bl #-0x6 + push {r7, lr} + mov r7, sp + bl #-0xa trap DefaultExceptionHandler: @@ -22,4 +24,4 @@ UsageFault: HardFaultTrampoline: mrs r0, msp - b #-0x14 + b #-0x18 diff --git a/ci/asm/app/release.vector_table b/ci/asm/app/release.vector_table index 09d7760..fe5a9fa 100644 --- a/ci/asm/app/release.vector_table +++ b/ci/asm/app/release.vector_table @@ -2,7 +2,7 @@ app: file format ELF32-arm-little Contents of section .vector_table: - 0000 00000120 45000000 4b000000 4d000000 ... E...K...M... - 0010 4b000000 4b000000 4b000000 00000000 K...K...K....... - 0020 00000000 00000000 00000000 4b000000 ............K... - 0030 00000000 00000000 4b000000 4b000000 ........K...K... + 0000 00000120 45000000 4f000000 51000000 ... E...O...Q... + 0010 4f000000 4f000000 4f000000 00000000 O...O...O....... + 0020 00000000 00000000 00000000 4f000000 ............O... + 0030 00000000 00000000 4f000000 4f000000 ........O...O... diff --git a/ci/main/app/app.objdump b/ci/main/app/app.objdump index 8e27a0f..f9f8df2 100644 --- a/ci/main/app/app.objdump +++ b/ci/main/app/app.objdump @@ -12,5 +12,7 @@ main: b #-4 Reset: - bl #-14 + push {r7, lr} + mov r7, sp + bl #-18 trap diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 7a25c7e..2d2fcd3 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -11,3 +11,4 @@ - [DMA](./dma.md) --- [A note on compiler support](./compiler-support.md) +[Creating a custom target](./custom-target.md) diff --git a/src/compiler-support.md b/src/compiler-support.md index 30543b3..91162e1 100644 --- a/src/compiler-support.md +++ b/src/compiler-support.md @@ -1,13 +1,14 @@ # 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`]. +team distributes a `rust-std` component, which is a pre-compiled collection of crates like [`core`] +and [`std`]. [`core`]: https://doc.rust-lang.org/core/index.html [`std`]: https://doc.rust-lang.org/std/index.html -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) +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 @@ -64,7 +65,8 @@ 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. +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 @@ -75,61 +77,90 @@ 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. +associated to it that describes, among other things, its architecture, its operating system and the +default linker. [specification]: https://github.com/rust-lang/rfcs/blob/master/text/0131-target-specification.md -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: +The Rust compiler knows about several targets. These are *built into* the compiler and can be listed +by running the following command: ``` console $ 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 +aarch64-fuchsia mipsisa32r6el-unknown-linux-gnu +aarch64-linux-android mipsisa64r6-unknown-linux-gnuabi64 +aarch64-pc-windows-msvc mipsisa64r6el-unknown-linux-gnuabi64 +aarch64-unknown-cloudabi msp430-none-elf +aarch64-unknown-freebsd nvptx64-nvidia-cuda +aarch64-unknown-hermit powerpc-unknown-linux-gnu +aarch64-unknown-linux-gnu powerpc-unknown-linux-gnuspe +aarch64-unknown-linux-musl powerpc-unknown-linux-musl +aarch64-unknown-netbsd powerpc-unknown-netbsd +aarch64-unknown-none powerpc-wrs-vxworks +aarch64-unknown-none-softfloat powerpc-wrs-vxworks-spe +aarch64-unknown-openbsd powerpc64-unknown-freebsd +aarch64-unknown-redox powerpc64-unknown-linux-gnu +aarch64-uwp-windows-msvc powerpc64-unknown-linux-musl +aarch64-wrs-vxworks powerpc64-wrs-vxworks +arm-linux-androideabi powerpc64le-unknown-linux-gnu +arm-unknown-linux-gnueabi powerpc64le-unknown-linux-musl +arm-unknown-linux-gnueabihf riscv32i-unknown-none-elf +arm-unknown-linux-musleabi riscv32imac-unknown-none-elf +arm-unknown-linux-musleabihf riscv32imc-unknown-none-elf +armebv7r-none-eabi riscv64gc-unknown-linux-gnu +armebv7r-none-eabihf riscv64gc-unknown-none-elf +armv4t-unknown-linux-gnueabi riscv64imac-unknown-none-elf +armv5te-unknown-linux-gnueabi s390x-unknown-linux-gnu +armv5te-unknown-linux-musleabi sparc-unknown-linux-gnu +armv6-unknown-freebsd sparc64-unknown-linux-gnu +armv6-unknown-netbsd-eabihf sparc64-unknown-netbsd +armv7-linux-androideabi sparc64-unknown-openbsd +armv7-unknown-cloudabi-eabihf sparcv9-sun-solaris +armv7-unknown-freebsd thumbv6m-none-eabi +armv7-unknown-linux-gnueabi thumbv7a-pc-windows-msvc +armv7-unknown-linux-gnueabihf thumbv7em-none-eabi +armv7-unknown-linux-musleabi thumbv7em-none-eabihf +armv7-unknown-linux-musleabihf thumbv7m-none-eabi +armv7-unknown-netbsd-eabihf thumbv7neon-linux-androideabi +armv7-wrs-vxworks-eabihf thumbv7neon-unknown-linux-gnueabihf +armv7a-none-eabi thumbv7neon-unknown-linux-musleabihf +armv7a-none-eabihf thumbv8m.base-none-eabi +armv7r-none-eabi thumbv8m.main-none-eabi +armv7r-none-eabihf thumbv8m.main-none-eabihf +asmjs-unknown-emscripten wasm32-unknown-emscripten +hexagon-unknown-linux-musl wasm32-unknown-unknown +i586-pc-windows-msvc wasm32-wasi +i586-unknown-linux-gnu x86_64-apple-darwin +i586-unknown-linux-musl x86_64-fortanix-unknown-sgx +i686-apple-darwin x86_64-fuchsia +i686-linux-android x86_64-linux-android +i686-pc-windows-gnu x86_64-linux-kernel +i686-pc-windows-msvc x86_64-pc-solaris +i686-unknown-cloudabi x86_64-pc-windows-gnu +i686-unknown-freebsd x86_64-pc-windows-msvc +i686-unknown-haiku x86_64-rumprun-netbsd +i686-unknown-linux-gnu x86_64-sun-solaris +i686-unknown-linux-musl x86_64-unknown-cloudabi +i686-unknown-netbsd x86_64-unknown-dragonfly +i686-unknown-openbsd x86_64-unknown-freebsd +i686-unknown-uefi x86_64-unknown-haiku +i686-uwp-windows-gnu x86_64-unknown-hermit +i686-uwp-windows-msvc x86_64-unknown-hermit-kernel +i686-wrs-vxworks x86_64-unknown-illumos +mips-unknown-linux-gnu x86_64-unknown-l4re-uclibc +mips-unknown-linux-musl x86_64-unknown-linux-gnu +mips-unknown-linux-uclibc x86_64-unknown-linux-gnux32 +mips64-unknown-linux-gnuabi64 x86_64-unknown-linux-musl +mips64-unknown-linux-muslabi64 x86_64-unknown-netbsd +mips64el-unknown-linux-gnuabi64 x86_64-unknown-openbsd +mips64el-unknown-linux-muslabi64 x86_64-unknown-redox +mipsel-unknown-linux-gnu x86_64-unknown-uefi +mipsel-unknown-linux-musl x86_64-uwp-windows-gnu +mipsel-unknown-linux-uclibc x86_64-uwp-windows-msvc +mipsisa32r6-unknown-linux-gnu x86_64-wrs-vxworks ``` -You can print the specification of any of these targets using the following command: +You can print the specification of one of these targets using the following command: ``` console $ rustc +nightly -Z unstable-options --print target-spec-json --target thumbv7m-none-eabi @@ -163,86 +194,69 @@ $ rustc +nightly -Z unstable-options --print target-spec-json --target thumbv7m- ``` 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]. +custom target by writing your own target specification file in JSON format which is described in the +[next section][custom-target]. -[the compiler source code]: https://github.com/rust-lang/rust/blob/1.27.2/src/librustc_target/spec/mod.rs#L376-L400 - -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`. - -``` console -$ 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="" -``` +[custom-target]: ./custom-target.md ## `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. +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: ``` console $ 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 +aarch64-apple-ios mipsel-unknown-linux-musl +aarch64-fuchsia nvptx64-nvidia-cuda +aarch64-linux-android powerpc-unknown-linux-gnu +aarch64-pc-windows-msvc powerpc64-unknown-linux-gnu +aarch64-unknown-linux-gnu powerpc64le-unknown-linux-gnu +aarch64-unknown-linux-musl riscv32i-unknown-none-elf +aarch64-unknown-none riscv32imac-unknown-none-elf +aarch64-unknown-none-softfloat riscv32imc-unknown-none-elf +arm-linux-androideabi riscv64gc-unknown-linux-gnu +arm-unknown-linux-gnueabi riscv64gc-unknown-none-elf +arm-unknown-linux-gnueabihf riscv64imac-unknown-none-elf +arm-unknown-linux-musleabi s390x-unknown-linux-gnu +arm-unknown-linux-musleabihf sparc64-unknown-linux-gnu +armebv7r-none-eabi sparcv9-sun-solaris +armebv7r-none-eabihf thumbv6m-none-eabi +armv5te-unknown-linux-gnueabi thumbv7em-none-eabi +armv5te-unknown-linux-musleabi 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 +armv7-unknown-linux-gnueabi thumbv7neon-linux-androideabi +armv7-unknown-linux-gnueabihf thumbv7neon-unknown-linux-gnueabihf +armv7-unknown-linux-musleabi thumbv8m.base-none-eabi +armv7-unknown-linux-musleabihf thumbv8m.main-none-eabi +armv7a-none-eabi thumbv8m.main-none-eabihf +armv7r-none-eabi wasm32-unknown-emscripten +armv7r-none-eabihf wasm32-unknown-unknown +asmjs-unknown-emscripten wasm32-wasi +i586-pc-windows-msvc x86_64-apple-darwin +i586-unknown-linux-gnu x86_64-apple-ios +i586-unknown-linux-musl x86_64-fortanix-unknown-sgx +i686-linux-android x86_64-fuchsia +i686-pc-windows-gnu x86_64-linux-android +i686-pc-windows-msvc x86_64-pc-windows-gnu +i686-unknown-freebsd x86_64-pc-windows-msvc +i686-unknown-linux-gnu x86_64-rumprun-netbsd +i686-unknown-linux-musl x86_64-sun-solaris +mips-unknown-linux-gnu x86_64-unknown-cloudabi +mips-unknown-linux-musl x86_64-unknown-freebsd +mips64-unknown-linux-gnuabi64 x86_64-unknown-linux-gnu (default) +mips64-unknown-linux-muslabi64 x86_64-unknown-linux-gnux32 +mips64el-unknown-linux-gnuabi64 x86_64-unknown-linux-musl +mips64el-unknown-linux-muslabi64 x86_64-unknown-netbsd +mipsel-unknown-linux-gnu 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. +If there's no `rust-std` component for your target, or you are using a custom target, then you'll +have to use a nightly toolchain to build the standard library. See the next page about [building for +custom targets][use-target-file]. -[Xargo]: https://github.com/japaric/xargo +[use-target-file]: ./custom-target.md#use-the-target-file +[xargo]: https://github.com/japaric/xargo diff --git a/src/custom-target.md b/src/custom-target.md new file mode 100644 index 0000000..9312e0e --- /dev/null +++ b/src/custom-target.md @@ -0,0 +1,208 @@ +# Creating a custom target + +If a custom target triple is not available for your platform, you must create a custom target file +that describes your target to rustc. + +Keep in mind that it is required to use a nightly compiler to build the core library, which must be +done for a target unknown to rustc. + +## Deciding on a target triple + +Many targets already have a known triple used to describe them, typically in the form +ARCH-VENDOR-SYS-ABI. You should aim to use the same triple that [LLVM uses][llvm-target-triple]; +however, it may differ if you need to specify additional information to Rust that LLVM does not know +about. Although the triple is technically only for human use, it's important for it to be unique and +descriptive especially if the target will be upstreamed in the future. + +The ARCH part is typically just the architecture name, except in the case of 32-bit ARM. For +example, you would probably use x86_64 for those processors, but specify the exact ARM architecture +version. Typical values might be `armv7`, `armv5te`, or `thumbv7neon`. Take a look at the names of +the [built-in targets][built-in-target] for inspiration. + +The VENDOR part is optional and describes the manufacturer. Omitting this field is the same as +using `unknown`. + +The SYS part describes the OS that is used. Typical values include `win32`, `linux`, and `darwin` +for desktop platforms. `none` is used for bare-metal usage. + +The ABI part describes how the process starts up. `eabi` is used for bare metal, while `gnu` is used +for glibc, `musl` for musl, etc. + +Now that you have a target triple, create a file with the name of the triple and a `.json` +extension. For example, a file describing `armv7a-none-eabi` would have the filename +`armv7a-none-eabi.json`. + +[llvm-target-triple]: https://clang.llvm.org/docs/CrossCompilation.html#target-triple + +## Fill the target file + +The target file must be valid JSON. There are two places where its contents are described: +[`Target`], where every field is mandatory, and [`TargetOptions`], where every field is optional. +**All underscores are replaced with hyphens**. + +The recommended way is to base your target file on the specification of a built-in target that's +similar to your target system, then tweak it to match the properties of your target system. To do +so, use the command +`rustc +nightly -Z unstable-options --print target-spec-json --target $SOME_SIMILAR_TARGET`, using +[a target that's already built into the compiler][built-in-target]. + +You can pretty much copy that output into your file. Start with a few modifications: + +- Remove `"is-builtin": true` +- Fill `llvm-target` with [the triple that LLVM expects][llvm-target-triple] +- Decide on a panicking strategy. A bare metal implementation will likely use + `"panic-strategy": "abort"`. If you decide not to `abort` on panicking, unless you [tell Cargo + to][eh_personality] per-project, you must define an [eh_personality] function. +- Configure atomics. Pick the first option that describes your target: + - I have a single-core processor, no threads, [**no interrupts**][interrupts-note], or any way for + multiple things to be happening in parallel: if you are **sure** that is the case, such as WASM + (for now), you may set `"singlethread": true`. This will configure LLVM to convert all atomic + operations to use their single threaded counterparts. Incorrectly using this option may result + in UB if using threads or interrupts. + - I have native atomic operations: set `max-atomic-width` to the biggest type in bits that your + target can operate on atomically. For example, many ARM cores have 32-bit atomic operations. You + may set `"max-atomic-width": 32` in that case. + - I have no native atomic operations, but I can emulate them myself: set `max-atomic-width` to the + highest number of bits that you can emulate up to 128, then implement all of the + [atomic][libcalls-atomic] and [sync][libcalls-atomic] functions expected by LLVM as + `#[no_mangle] unsafe extern "C"`. These functions have been standardized by gcc, so the [gcc + documentation][gcc-sync] may have more notes. Missing functions will cause a linker error, while + incorrectly implemented functions will possibly cause UB. For example, if you have a + single-core, single-thread processor with interrupts, you can implement these functions to + disable interrupts, perform the regular operation, and then re-enable them. + - I have no native atomic operations: you'll have to do some unsafe work to manually ensure + synchronization in your code. You must set `"max-atomic-width": 0`. +- Change the linker if integrating with an existing toolchain. For example, if you're using a + toolchain that uses a custom build of gcc, set `"linker-flavor": "gcc"` and `linker` to the + command name of your linker. If you require additional linker arguments, use `pre-link-args` and + `post-link-args` as so: + ``` json + "pre-link-args": { + "gcc": [ + "-Wl,--as-needed", + "-Wl,-z,noexecstack", + "-m64" + ] + }, + "post-link-args": { + "gcc": [ + "-Wl,--allow-multiple-definition", + "-Wl,--start-group,-lc,-lm,-lgcc,-lstdc++,-lsupc++,--end-group" + ] + } + ``` + Ensure that the linker type is the key within `link-args`. +- Configure LLVM features. Run `llc -march=ARCH -mattr=help` where ARCH is the base architecture + (not including the version in the case of ARM) to list the available features and their + descriptions. **If your target requires strict memory alignment access (e.g. `armv5te`), make sure + that you enable `strict-align`**. To enable a feature, place a plus before it. Likewise, to + disable a feature, place a minus before it. Features should be comma separated like so: + `"features": "+soft-float,+neon`. Note that this may not be necessary if LLVM knows enough about + your target based on the provided triple and CPU. +- Configure the CPU that LLVM uses if you know it. This will enable CPU-specific optimizations and + features. At the top of the output of the command in the last step, there is a list of known CPUs. + If you know that you will be targeting a specific CPU, you may set it in the `cpu` field in the + JSON target file. + +[`target`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/struct.Target.html +[`targetoptions`]: + https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/struct.TargetOptions.html +[aborting-on-panic]: + https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/aborting-on-panic.html +[built-in-target]: ./compiler-support.md#built-in-target +[eh_personality]: ./smallest-no-std.md#eh_personality +[interrupts-note]: https://github.com/rust-lang/rust/issues/58500#issuecomment-654341233 +[libcalls-atomic]: http://llvm.org/docs/Atomics.html#libcalls-atomic +[libcalls-sync]: http://llvm.org/docs/Atomics.html#libcalls-sync +[gcc-sync]: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html + +## Use the target file + +Once you have a target specification file, you may refer to it by its path or by its name (i.e. +excluding `.json`) if it is in the current directory or in `$RUST_TARGET_PATH`. + +Verify that it is readable by rustc: + +``` sh +❱ rustc --print cfg --target foo.json # or just foo if in the current directory +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="" +``` + +Now, you finally get to use it! Many resources have been recommending [`xargo`] or [`cargo-xbuild`]. +However, its successor, cargo's `build-std` feature, has received a lot of work recently and has +quickly reached feature parity with the other options. As such, this guide will only cover that +option. + +Start with a bare minimum [`no_std` program][no_std-program]. Now, run +`cargo build -Z build-std=core --target foo.json`, again using the above rules about referencing the +path. Hopefully, you should now have a binary in the target directory. + +You may optionally configure cargo to always use your target. See the recommendations at the end of +the page about [the smallest `no_std` program][no_std-program]. However, you'll currently have to +use the flag `-Z build-std=core` as that option is unstable. + +[`xargo`]: https://github.com/japaric/xargo +[`cargo-xbuild`]: https://github.com/rust-osdev/cargo-xbuild +[no_std-program]: ./smallest-no-std.md + +### Build additional built-in crates + +When using cargo's `build-std` feature, you can choose which crates to compile in. By default, when +only passing `-Z build-std`, `std`, `core`, and `alloc` are compiled. However, you may want to +exclude `std` when compiling for bare-metal. To do so, specify the crated you'd like after +`build-std`. For example, to include `core` and `alloc`, pass `-Z build-std=core,alloc`. + +## Troubleshooting + +### language item required, but not found: `eh_personality` + +Either add `"panic-strategy": "abort"` to your target file, or define an [eh_personality] function. +Alternatively, [tell Cargo to ignore it][eh_personality]. + +### undefined reference to `__sync_val_compare_and_swap_#` + +Rust thinks that your target has atomic instructions, but LLVM doesn't. Go back to the step about +[configuring atomics][fill-target-file]. You will need to reduce the number in `max-atomic-width`. +See [#58500] for more details. + +[fill-target-file]: #fill-the-target-file +[#58500]: https://github.com/rust-lang/rust/issues/58500 + +### could not find `sync` in `alloc` + +Similar to the above case, Rust doesn't think that you have atomics. You must implement them +yourself or [tell Rust that you have atomic instructions][fill-target-file]. + +### multiple definition of `__(something)` + +You're likely linking your Rust program with code built from another language, and the other +language includes compiler built-ins that Rust also creates. To fix this, you'll need to tell your +linker to allow multiple definitions. If using gcc, you may add: + +``` json +"post-link-args": { + "gcc": [ + "-Wl,--allow-multiple-definition" + ] +} +``` + +### error adding symbols: file format not recognized + +Switch to cargo's `build-std` feature and update your compiler. This [was a bug][#8239] introduced +for a few compiler builds that tried to pass in internal Rust object to an external linker. + +[#8239]: https://github.com/rust-lang/cargo/issues/8239 diff --git a/src/smallest-no-std.md b/src/smallest-no-std.md index 15e9530..7072c31 100644 --- a/src/smallest-no-std.md +++ b/src/smallest-no-std.md @@ -4,8 +4,8 @@ In this section we'll write the smallest `#![no_std]` program that *compiles*. ## What does `#![no_std]` mean? -`#![no_std]` is a crate level attribute that indicates that the crate will link to the [`core`] crate -instead of the [`std`] crate, but what does this mean for applications? +`#![no_std]` is a crate level attribute that indicates that the crate will link to the [`core`] +crate instead of the [`std`] crate, but what does this mean for applications? [`core`]: https://doc.rust-lang.org/core/ [`std`]: https://doc.rust-lang.org/std/ @@ -31,6 +31,7 @@ required. Because of these properties, a `#![no_std]` application can be the first and / or the only code that runs on a system. It can be many things that a standard Rust application can never be, for example: + - The kernel of an OS. - Firmware. - A bootloader. @@ -63,8 +64,8 @@ its entry point. At the time of writing, Rust's `main` interface makes some assu environment the program executes in: For example, it assumes the existence of command line arguments, so in general, it's not appropriate for `#![no_std]` programs. -The `#[panic_handler]` attribute. The function marked with this attribute defines the behavior -of panics, both library level panics (`core::panic!`) and language level panics (out of bounds +The `#[panic_handler]` attribute. The function marked with this attribute defines the behavior of +panics, both library level panics (`core::panic!`) and language level panics (out of bounds indexing). This program doesn't produce anything useful. In fact, it will produce an empty binary. @@ -104,3 +105,40 @@ $ cat .cargo/config ``` toml {{#include ../ci/smallest-no-std/.cargo/config}} ``` + +## eh_personality + +If your configuration does not unconditionally abort on panic, which most targets for full operating +systems don't (or if your [custom target][custom-target] does not contain +`"panic-strategy": "abort"`), then you must tell Cargo to do so or add an `eh_personality` function, +which requires a nightly compiler. [Here is Rust's documentation about it][more-about-lang-items], +and [here is some discussion about it][til-why-eh-personality]. + +In your Cargo.toml, add: + +``` toml +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" +``` + +Alternatively, declare the `eh_personality` function. A simple implementation that does not do +anything special when unwinding is as follows: + +``` rust +#![feature(lang_items)] + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} +``` + +You will receive the error `language item required, but not found: 'eh_personality'` if not +included. + +[custom-target]: ./custom-target.md +[more-about-lang-items]: + https://doc.rust-lang.org/unstable-book/language-features/lang-items.html#more-about-the-language-items +[til-why-eh-personality]: + https://www.reddit.com/r/rust/comments/estvau/til_why_the_eh_personality_language_item_is/