Skip to content

Commit 0c8fb9f

Browse files
committed
Add notes about creating a custom target
1 parent bc5cff2 commit 0c8fb9f

File tree

4 files changed

+237
-78
lines changed

4 files changed

+237
-78
lines changed

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
- [DMA](./dma.md)
1212
---
1313
[A note on compiler support](./compiler-support.md)
14+
[Creating a custom target](./custom-target.md)

src/compiler-support.md

Lines changed: 79 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -80,56 +80,85 @@ and the default linker.
8080

8181
[specification]: https://github.com/rust-lang/rfcs/blob/master/text/0131-target-specification.md
8282

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

8686
``` console
8787
$ rustc --print target-list | column
88-
aarch64-fuchsia mips64el-unknown-linux-gnuabi64
89-
aarch64-linux-android mipsel-unknown-linux-gnu
90-
aarch64-unknown-cloudabi mipsel-unknown-linux-musl
91-
aarch64-unknown-freebsd mipsel-unknown-linux-uclibc
92-
aarch64-unknown-linux-gnu msp430-none-elf
93-
aarch64-unknown-linux-musl powerpc-unknown-linux-gnu
94-
aarch64-unknown-openbsd powerpc-unknown-linux-gnuspe
95-
arm-linux-androideabi powerpc-unknown-netbsd
96-
arm-unknown-linux-gnueabi powerpc64-unknown-linux-gnu
97-
arm-unknown-linux-gnueabihf powerpc64le-unknown-linux-gnu
98-
arm-unknown-linux-musleabi powerpc64le-unknown-linux-musl
99-
arm-unknown-linux-musleabihf s390x-unknown-linux-gnu
100-
armebv7r-none-eabihf sparc-unknown-linux-gnu
101-
armv4t-unknown-linux-gnueabi sparc64-unknown-linux-gnu
102-
armv5te-unknown-linux-gnueabi sparc64-unknown-netbsd
103-
armv5te-unknown-linux-musleabi sparcv9-sun-solaris
104-
armv6-unknown-netbsd-eabihf thumbv6m-none-eabi
105-
armv7-linux-androideabi thumbv7em-none-eabi
106-
armv7-unknown-cloudabi-eabihf thumbv7em-none-eabihf
107-
armv7-unknown-linux-gnueabihf thumbv7m-none-eabi
108-
armv7-unknown-linux-musleabihf wasm32-experimental-emscripten
109-
armv7-unknown-netbsd-eabihf wasm32-unknown-emscripten
110-
asmjs-unknown-emscripten wasm32-unknown-unknown
111-
i586-pc-windows-msvc x86_64-apple-darwin
112-
i586-unknown-linux-gnu x86_64-fuchsia
113-
i586-unknown-linux-musl x86_64-linux-android
114-
i686-apple-darwin x86_64-pc-windows-gnu
115-
i686-linux-android x86_64-pc-windows-msvc
116-
i686-pc-windows-gnu x86_64-rumprun-netbsd
117-
i686-pc-windows-msvc x86_64-sun-solaris
118-
i686-unknown-cloudabi x86_64-unknown-bitrig
119-
i686-unknown-dragonfly x86_64-unknown-cloudabi
120-
i686-unknown-freebsd x86_64-unknown-dragonfly
121-
i686-unknown-haiku x86_64-unknown-freebsd
122-
i686-unknown-linux-gnu x86_64-unknown-haiku
123-
i686-unknown-linux-musl x86_64-unknown-l4re-uclibc
124-
i686-unknown-netbsd x86_64-unknown-linux-gnu
125-
i686-unknown-openbsd x86_64-unknown-linux-gnux32
126-
mips-unknown-linux-gnu x86_64-unknown-linux-musl
127-
mips-unknown-linux-musl x86_64-unknown-netbsd
128-
mips-unknown-linux-uclibc x86_64-unknown-openbsd
129-
mips64-unknown-linux-gnuabi64 x86_64-unknown-redox
88+
aarch64-fuchsia mipsisa32r6el-unknown-linux-gnu
89+
aarch64-linux-android mipsisa64r6-unknown-linux-gnuabi64
90+
aarch64-pc-windows-msvc mipsisa64r6el-unknown-linux-gnuabi64
91+
aarch64-unknown-cloudabi msp430-none-elf
92+
aarch64-unknown-freebsd nvptx64-nvidia-cuda
93+
aarch64-unknown-hermit powerpc-unknown-linux-gnu
94+
aarch64-unknown-linux-gnu powerpc-unknown-linux-gnuspe
95+
aarch64-unknown-linux-musl powerpc-unknown-linux-musl
96+
aarch64-unknown-netbsd powerpc-unknown-netbsd
97+
aarch64-unknown-none powerpc-wrs-vxworks
98+
aarch64-unknown-none-softfloat powerpc-wrs-vxworks-spe
99+
aarch64-unknown-openbsd powerpc64-unknown-freebsd
100+
aarch64-unknown-redox powerpc64-unknown-linux-gnu
101+
aarch64-uwp-windows-msvc powerpc64-unknown-linux-musl
102+
aarch64-wrs-vxworks powerpc64-wrs-vxworks
103+
arm-linux-androideabi powerpc64le-unknown-linux-gnu
104+
arm-unknown-linux-gnueabi powerpc64le-unknown-linux-musl
105+
arm-unknown-linux-gnueabihf riscv32i-unknown-none-elf
106+
arm-unknown-linux-musleabi riscv32imac-unknown-none-elf
107+
arm-unknown-linux-musleabihf riscv32imc-unknown-none-elf
108+
armebv7r-none-eabi riscv64gc-unknown-linux-gnu
109+
armebv7r-none-eabihf riscv64gc-unknown-none-elf
110+
armv4t-unknown-linux-gnueabi riscv64imac-unknown-none-elf
111+
armv5te-unknown-linux-gnueabi s390x-unknown-linux-gnu
112+
armv5te-unknown-linux-musleabi sparc-unknown-linux-gnu
113+
armv6-unknown-freebsd sparc64-unknown-linux-gnu
114+
armv6-unknown-netbsd-eabihf sparc64-unknown-netbsd
115+
armv7-linux-androideabi sparc64-unknown-openbsd
116+
armv7-unknown-cloudabi-eabihf sparcv9-sun-solaris
117+
armv7-unknown-freebsd thumbv6m-none-eabi
118+
armv7-unknown-linux-gnueabi thumbv7a-pc-windows-msvc
119+
armv7-unknown-linux-gnueabihf thumbv7em-none-eabi
120+
armv7-unknown-linux-musleabi thumbv7em-none-eabihf
121+
armv7-unknown-linux-musleabihf thumbv7m-none-eabi
122+
armv7-unknown-netbsd-eabihf thumbv7neon-linux-androideabi
123+
armv7-wrs-vxworks-eabihf thumbv7neon-unknown-linux-gnueabihf
124+
armv7a-none-eabi thumbv7neon-unknown-linux-musleabihf
125+
armv7a-none-eabihf thumbv8m.base-none-eabi
126+
armv7r-none-eabi thumbv8m.main-none-eabi
127+
armv7r-none-eabihf thumbv8m.main-none-eabihf
128+
asmjs-unknown-emscripten wasm32-unknown-emscripten
129+
hexagon-unknown-linux-musl wasm32-unknown-unknown
130+
i586-pc-windows-msvc wasm32-wasi
131+
i586-unknown-linux-gnu x86_64-apple-darwin
132+
i586-unknown-linux-musl x86_64-fortanix-unknown-sgx
133+
i686-apple-darwin x86_64-fuchsia
134+
i686-linux-android x86_64-linux-android
135+
i686-pc-windows-gnu x86_64-linux-kernel
136+
i686-pc-windows-msvc x86_64-pc-solaris
137+
i686-unknown-cloudabi x86_64-pc-windows-gnu
138+
i686-unknown-freebsd x86_64-pc-windows-msvc
139+
i686-unknown-haiku x86_64-rumprun-netbsd
140+
i686-unknown-linux-gnu x86_64-sun-solaris
141+
i686-unknown-linux-musl x86_64-unknown-cloudabi
142+
i686-unknown-netbsd x86_64-unknown-dragonfly
143+
i686-unknown-openbsd x86_64-unknown-freebsd
144+
i686-unknown-uefi x86_64-unknown-haiku
145+
i686-uwp-windows-gnu x86_64-unknown-hermit
146+
i686-uwp-windows-msvc x86_64-unknown-hermit-kernel
147+
i686-wrs-vxworks x86_64-unknown-illumos
148+
mips-unknown-linux-gnu x86_64-unknown-l4re-uclibc
149+
mips-unknown-linux-musl x86_64-unknown-linux-gnu
150+
mips-unknown-linux-uclibc x86_64-unknown-linux-gnux32
151+
mips64-unknown-linux-gnuabi64 x86_64-unknown-linux-musl
152+
mips64-unknown-linux-muslabi64 x86_64-unknown-netbsd
153+
mips64el-unknown-linux-gnuabi64 x86_64-unknown-openbsd
154+
mips64el-unknown-linux-muslabi64 x86_64-unknown-redox
155+
mipsel-unknown-linux-gnu x86_64-unknown-uefi
156+
mipsel-unknown-linux-musl x86_64-uwp-windows-gnu
157+
mipsel-unknown-linux-uclibc x86_64-uwp-windows-msvc
158+
mipsisa32r6-unknown-linux-gnu x86_64-wrs-vxworks
130159
```
131160

132-
You can print the specification of any of these targets using the following command:
161+
You can print the specification of one of these targets using the following command:
133162

134163
``` console
135164
$ rustc +nightly -Z unstable-options --print target-spec-json --target thumbv7m-none-eabi
@@ -163,38 +192,10 @@ $ rustc +nightly -Z unstable-options --print target-spec-json --target thumbv7m-
163192
```
164193

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

172-
[the compiler source code]: https://github.com/rust-lang/rust/blob/1.27.2/src/librustc_target/spec/mod.rs#L376-L400
173-
174-
Once you have a target specification file you can refer to it by its path or by its name if its in
175-
the current directory or in `$RUST_TARGET_PATH`.
176-
177-
``` console
178-
$ rustc +nightly -Z unstable-options --print target-spec-json \
179-
--target thumbv7m-none-eabi \
180-
> foo.json
181-
182-
$ rustc --print cfg --target foo.json # or just --target foo
183-
debug_assertions
184-
target_arch="arm"
185-
target_endian="little"
186-
target_env=""
187-
target_feature="mclass"
188-
target_feature="v7"
189-
target_has_atomic="16"
190-
target_has_atomic="32"
191-
target_has_atomic="8"
192-
target_has_atomic="cas"
193-
target_has_atomic="ptr"
194-
target_os="none"
195-
target_pointer_width="32"
196-
target_vendor=""
197-
```
198+
[custom-target]: ./custom-target.md
198199

199200
## `rust-std` component
200201

@@ -241,8 +242,8 @@ mips-unknown-linux-musl x86_64-unknown-redox
241242
```
242243

243244
If there's no `rust-std` component for your target or you are using a custom target, then you'll have
244-
to use a tool like [Xargo] to have Cargo compile the `core` crate on the fly. Note that Xargo
245-
requires a nightly toolchain; the long term plan is to upstream Xargo's functionality into Cargo
246-
and eventually have that functionality available on stable.
245+
to use a nightly toolchain to build the standard library. See the next page about [building for custom targets][use-target-file].
246+
247+
[use-target-file]: ./custom-target.md#use-the-target-file
247248

248249
[Xargo]: https://github.com/japaric/xargo

src/custom-target.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Creating a custom target
2+
3+
If a custom target triple is not available for your platform, you must create a custom target.json file that describes your target to rustc.
4+
5+
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.
6+
7+
## Decide on a target triple
8+
9+
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.
10+
11+
12+
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.
13+
14+
The VENDOR part is optional, and describes the manufacturer. Omitting this field is the same as using `unknown`.
15+
16+
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.
17+
18+
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.
19+
20+
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`.
21+
22+
[llvm-target-triple]: https://clang.llvm.org/docs/CrossCompilation.html#target-triple
23+
24+
## Fill the target file
25+
26+
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**.
27+
28+
The recommended way is to
29+
base your target file on the specification of a built-in target that's similar to your target system, then
30+
tweak it to match the properties of your target system. To do so, use the command
31+
`rustc +nightly -Z unstable-options --print target-spec-json --target $SOME_SIMILAR_TARGET`, using [a target that's
32+
already built into the compiler][built-in-target].
33+
34+
You can pretty much copy that output into your file. Start with a few modifications:
35+
* Remove `"is-builtin": true`
36+
* Fill `llvm-target` with [the triple that LLVM expects][llvm-target-triple]
37+
* Decide on a panicking strategy. A bare metal implementation will likely use `"panic-strategy": "abort"`. If you decide not to `abort` on panicking, even if you [tell Cargo to][aborting-on-panic], you must define an [eh_personality] function.
38+
* Configure atomics. Pick the first option that describes your target:
39+
* I have a single-core processor, no threads, no interrupts, 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.
40+
* 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.
41+
* 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 64, 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.
42+
* 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`.
43+
* 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:
44+
``` json
45+
"pre-link-args": {
46+
"gcc": [
47+
"-Wl,--as-needed",
48+
"-Wl,-z,noexecstack",
49+
"-m64"
50+
]
51+
},
52+
"post-link-args": {
53+
"gcc": [
54+
"-Wl,--allow-multiple-definition",
55+
"-Wl,--start-group,-lc,-lm,-lgcc,-lstdc++,-lsupc++,--end-group"
56+
]
57+
}
58+
```
59+
Ensure that the linker type is the key within `link-args`.
60+
* 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.
61+
* 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.
62+
63+
[`Target`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/struct.Target.html
64+
[`TargetOptions`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/struct.TargetOptions.html
65+
[aborting-on-panic]: https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/aborting-on-panic.html
66+
[built-in-target]: ./compiler-support.md#built-in-target
67+
[eh_personality]: ./smallest-no-std.md#eh_personality
68+
[libcalls-atomic]: http://llvm.org/docs/Atomics.html#libcalls-atomic
69+
[libcalls-sync]: http://llvm.org/docs/Atomics.html#libcalls-sync
70+
[gcc-sync]: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
71+
72+
## Use the target file
73+
74+
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`.
75+
76+
Verify that it is readable by rustc:
77+
``` sh
78+
❱ rustc --print cfg --target foo.json # or just foo if in the current directory
79+
debug_assertions
80+
target_arch="arm"
81+
target_endian="little"
82+
target_env=""
83+
target_feature="mclass"
84+
target_feature="v7"
85+
target_has_atomic="16"
86+
target_has_atomic="32"
87+
target_has_atomic="8"
88+
target_has_atomic="cas"
89+
target_has_atomic="ptr"
90+
target_os="none"
91+
target_pointer_width="32"
92+
target_vendor=""
93+
```
94+
95+
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.
96+
97+
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.
98+
99+
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.
100+
101+
[`xargo`]: https://github.com/japaric/xargo
102+
[`cargo-xbuild`]: https://github.com/rust-osdev/cargo-xbuild
103+
[no_std-program]: ./smallest-no-std.md
104+
105+
### Build additional built-in crates
106+
107+
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`.
108+
109+
## Troubleshooting
110+
111+
### language item required, but not found: `eh_personality`
112+
113+
Either add `"panic-strategy": "abort"` to your target file, or define an [eh_personality] function.
114+
115+
### undefined reference to `__sync_val_compare_and_swap_#`
116+
117+
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.
118+
119+
[fill-target-file]: #fill-the-target-file
120+
[#58500]: https://github.com/rust-lang/rust/issues/58500
121+
122+
### could not find `sync` in `alloc`
123+
124+
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].
125+
126+
### multiple definition of `__(something)`
127+
128+
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:
129+
130+
``` json
131+
"post-link-args": {
132+
"gcc": [
133+
"-Wl,--allow-multiple-definition"
134+
]
135+
}
136+
```
137+
138+
### error adding symbols: file format not recognized
139+
140+
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.
141+
142+
[#8239]: https://github.com/rust-lang/cargo/issues/8239

0 commit comments

Comments
 (0)