|
| 1 | +# Arm Linux support in Rust |
| 2 | + |
| 3 | +The Arm Architecture has been around since the mid-1980s, going through nine |
| 4 | +major revisions, many minor revisions, and spanning both 32-bith and 64-bit |
| 5 | +architectures. This page covers 32-bit Arm platforms that run some form of |
| 6 | +Linux (but not Android). Those targets are: |
| 7 | + |
| 8 | +* `arm-unknown-linux-gnueabi` |
| 9 | +* `arm-unknown-linux-gnueabihf` |
| 10 | +* `arm-unknown-linux-musleabi` |
| 11 | +* `arm-unknown-linux-musleabihf` |
| 12 | +* [`armeb-unknown-linux-gnueabi`](armeb-unknown-linux-gnueabi.md) |
| 13 | +* `armv4t-unknown-linux-gnueabi` |
| 14 | +* [`armv5te-unknown-linux-gnueabi`](armv5te-unknown-linux-gnueabi.md) |
| 15 | +* `armv5te-unknown-linux-musleabi` |
| 16 | +* `armv5te-unknown-linux-uclibceabi` |
| 17 | +* `armv7-unknown-linux-gnueabi` |
| 18 | +* `armv7-unknown-linux-gnueabihf` |
| 19 | +* `armv7-unknown-linux-musleabi` |
| 20 | +* `armv7-unknown-linux-musleabihf` |
| 21 | +* `armv7-unknown-linux-ohos` |
| 22 | +* [`armv7-unknown-linux-uclibceabi`](armv7-unknown-linux-uclibceabi.md) |
| 23 | +* [`armv7-unknown-linux-uclibceabihf`](armv7-unknown-linux-uclibceabihf.md) |
| 24 | +* `thumbv7neon-unknown-linux-gnueabihf` |
| 25 | +* `thumbv7neon-unknown-linux-musleabihf` |
| 26 | + |
| 27 | +Some of these targets have dedicated pages and some do not. This is largely |
| 28 | +due to historical accident, or the enthusiasm of the maintainers. This |
| 29 | +document attempts to cover all the targets, but only in broad terms. |
| 30 | + |
| 31 | +To make sense of this list, the architecture and ABI component of the |
| 32 | +`<architecture>-unknown-linux-<abi>` tuple will be discussed separately. |
| 33 | + |
| 34 | +The second part of the tuple is `unknown` because these systems don't come |
| 35 | +from any one specific vendor (like `powerpc-ibm-aix` or |
| 36 | +`aarch64-apple-darwin`). The third part is `linux`, because this page only |
| 37 | +discusses Linux targets. |
| 38 | + |
| 39 | +## Architecture Component |
| 40 | + |
| 41 | +* `arm` |
| 42 | +* `armeb` |
| 43 | +* `armv4t` |
| 44 | +* `armv5te` |
| 45 | +* `armv7` |
| 46 | +* `thumbv7neon` |
| 47 | + |
| 48 | +The architecture component simply called `arm` corresponds to the Armv6 |
| 49 | +architecture - that is, version 6 of the Arm Architecture as defined in |
| 50 | +version 6 of the Arm Architecture Reference Manual (the Arm ARM). This was the |
| 51 | +last 'legacy' release of the Arm architecture, before they split into |
| 52 | +Application, Real-Time and Microcontroller profiles (leading to Armv7-A, |
| 53 | +Armv7-R and Armv7-M). Processors that implement the Armv6 architecture include |
| 54 | +the ARM1176JZF-S, as found in BCM2835 SoC that powers the Raspberry Pi Zero. |
| 55 | +Arm processors are generally fairly backwards compatible, especially for |
| 56 | +user-mode code, so code compiled for the `arm` architecture should also work |
| 57 | +on newer ARMv7-A systems, or even 64/32-bit Armv8-A systems. |
| 58 | + |
| 59 | +The `armeb` architecture component specifies an Armv6 processor running in Big |
| 60 | +Endian mode (`eb` is for big-endian - the letters are backwards because |
| 61 | +engineers used to little-endian systems perceive big-endian numbers to be |
| 62 | +written into memory backwards, and they thought it was funnier like that). |
| 63 | +Most Arm processors can operate in either little-endian or big-endian mode and |
| 64 | +little-endian mode is by far the most common. However, if for whatever reason |
| 65 | +you wish to store your Most Significant Bytes first, these targets are |
| 66 | +available. They just aren't terribly well tested, or compatible with most |
| 67 | +existing pre-compiled Arm libraries. |
| 68 | + |
| 69 | +Targets that start with `armv4t` are for processors implementing the Armv4T |
| 70 | +architecture from 1994. These include the ARM7TDMI, as found in the Nokia 6110 |
| 71 | +brick-phone and the Game Boy Advance. The 'T' stands for *Thumb* and indicate |
| 72 | +that the processors can execute smaller 16-bit versions of some of the 32-bit |
| 73 | +Arm instructions. Because a Thumb is like a small version of an Arm. |
| 74 | + |
| 75 | +Targets that start with `armv5te` are for processors implementing the Armv5TE |
| 76 | +architecture. These are mostly from the ARM9 family, like the ARM946E-S found |
| 77 | +in the Nintendo DS. If you are programming an Arm machine from the early |
| 78 | +2000s, this might be what you need. |
| 79 | + |
| 80 | +The `armv7` is arguably a misnomer, and it should be `armv7a`. This is because |
| 81 | +it corresponds to the Application profile of Armv7 (i.e. Armv7-A), as opposed |
| 82 | +to the Real-Time or Microcontroller profile. Processors implementing this |
| 83 | +architecture include the Cortex-A7 and Cortex-A8. |
| 84 | + |
| 85 | +The `thumbv7neon` component indicates support for a processor that implements |
| 86 | +ARMv7-A (the same as `armv7`), it generates Thumb instructions (technically |
| 87 | +Thumb-2, also known as the T32 ISA) as opposed to Arm instructions (also known |
| 88 | +as the A32 ISA). These instructions are smaller, giving more code per KB of |
| 89 | +RAM, but may have a performance penalty if they take two instructions to do |
| 90 | +something Arm instructions could do in one. It's a complex trade-off and you |
| 91 | +should be doing benchmarks to work out which is better for you, if you |
| 92 | +strongly care about code size and/or performance. This component also enables |
| 93 | +support for Arm's SIMD extensions, known as Neon. These extensions will |
| 94 | +improve performance for certain kinds of repetitive operations. |
| 95 | + |
| 96 | +## ABI Component |
| 97 | + |
| 98 | +* `gnueabi` |
| 99 | +* `gnueabihf` |
| 100 | +* `musleabi` |
| 101 | +* `musleabihf` |
| 102 | +* `ohos` |
| 103 | +* `uclibceabi` |
| 104 | +* `uclibceabihf` |
| 105 | + |
| 106 | +You will need to select the appropriate ABI to match the system you want to be |
| 107 | +running this code on. For example, running `eabihf` code on an `eabi` system |
| 108 | +will not work correctly. |
| 109 | + |
| 110 | +The `gnueabi` ABI component indicates support for using the GNU C Library |
| 111 | +(glibc), and the Arm Embedded ABI (EABI). The EABI is a replacement for the |
| 112 | +original ABI (now called the Old ABI or OABI), and it is the standard ABI for |
| 113 | +32-bit Arm systems. With this ABI, function parameters that are `f32` or `f64` |
| 114 | +are passed as if they were integers, instead of being passed via in FPU |
| 115 | +registers. Generally these targets also disable the use of the FPU entirely, |
| 116 | +although that isn't always true. |
| 117 | + |
| 118 | +The `gnueabihf` ABI component is like `gnueabi`, except that it support the |
| 119 | +'hard-float' of the EABI. That is, function parameters that are `f32` or `f64` |
| 120 | +are passed in FPU registers. Naturally, this makes the FPU mandatory. |
| 121 | + |
| 122 | +Most 'desktop' Linux distributions (Debian, Ubuntu, Fedora, etc) use the GNU C |
| 123 | +Library and so you should probably select either `gnueabi` or `gnueabihf`, |
| 124 | +depending on whether your distribution is using 'soft-float' (EABI) or |
| 125 | +'hard-float' (EABIHF). Debian happens to offer |
| 126 | +[both](https://wiki.debian.org/ArmEabiPort) |
| 127 | +[kinds](https://wiki.debian.org/ArmHardFloatPort). |
| 128 | + |
| 129 | +The `musleabi` and `musleabihf` ABI components offer support for the [musl C |
| 130 | +library](https://musl.libc.org/). This C library can be used to create 'static |
| 131 | +binaries' that have no run-time library requirements (a feature that glibc |
| 132 | +does not support). There are soft-float (`eabi`) and hard-float (`eabihf`) |
| 133 | +variants, as per the `gnu*` targets above. |
| 134 | + |
| 135 | +The `uclibceabi` and `uclibceabihf` ABI components are for the [uClibc-ng C |
| 136 | +library](https://uclibc-ng.org/). This is sometimes used in light-weight |
| 137 | +embedded Linux distributions, like those created with |
| 138 | +[buildroot](https://www.buildroot.org/). |
| 139 | + |
| 140 | +## Cross Compilation |
| 141 | + |
| 142 | +Unfortunately, 32-bit Arm machines are generally not the fastest around, and |
| 143 | +they don't have much RAM. This means you are likely to be cross-compiling. |
| 144 | + |
| 145 | +To do this, you need to give Rust a suitable linker to use - one that knows |
| 146 | +the Arm architecture, and more importantly, knows where to find a suitable C |
| 147 | +Library to link against. |
| 148 | + |
| 149 | +To do that, you can add the `linker` property to your `.cargo/config.toml`. |
| 150 | +Typically you would refer to a suitable copy of GCC that has built as a |
| 151 | +cross-compiler, alongside a C library. |
| 152 | + |
| 153 | +```toml |
| 154 | +[target.arm-unknown-linux-gnueabi] |
| 155 | +linker = "arm-linux-gnueabi-gcc" |
| 156 | +``` |
| 157 | + |
| 158 | +On Debian Linux, you could install such a cross-compilation toolchain with |
| 159 | +`apt install gcc-arm-linux-gnueabi`. For more exotic combinations, you might |
| 160 | +need to build a bespoke version of GCC using [crosstool-ng]. |
| 161 | + |
| 162 | +[crosstool-ng]: https://github.com/crosstool-ng/crosstool-ng |
| 163 | + |
| 164 | +Note that for GCC, all 32-bit Arm architectures are handled in the same build |
| 165 | +- there are no separate Armv4T or Armv6 builds of GCC. The architecture is |
| 166 | +selected with flags, like `-march=armv6`, but they aren't required for the |
| 167 | +linker. |
| 168 | + |
| 169 | +Let's assume we are on some Debian machine, and we want to build a basic Arm |
| 170 | +Linux binary for a distribution using the GNU C Library, targeting Armv6 with |
| 171 | +a hard-float ABI. Such a binary should work on a Raspberry Pi, for example. |
| 172 | +The commands are: |
| 173 | + |
| 174 | +```bash |
| 175 | +sudo apt install -y gcc-arm-linux-gnueabihf |
| 176 | +rustup target add arm-unknown-linux-gnueabihf |
| 177 | +cargo new --bin armdemo |
| 178 | +cd armdemo |
| 179 | +mkdir .cargo |
| 180 | +cat > .cargo/config.toml << EOF |
| 181 | +[target.arm-unknown-linux-gnueabihf] |
| 182 | +linker = "arm-linux-gnueabihf-gcc" |
| 183 | +EOF |
| 184 | +cargo build --target=arm-unknown-linux-gnueabihf |
| 185 | +``` |
| 186 | + |
| 187 | +This will give us our ARM Linux binary for the GNU C Library with a soft-float ABI: |
| 188 | + |
| 189 | +```console |
| 190 | +$ file ./target/arm-unknown-linux-gnueabi/debug/armdemo |
| 191 | +./target/arm-unknown-linux-gnueabi/debug/armdemo: ELF 32-bit LSB pie |
| 192 | + executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter |
| 193 | + /lib/ld-linux.so.3, BuildID[sha1]=dd0b9aa5ae876330fd4e2fcf393850f083ec7fcd, |
| 194 | + for GNU/Linux 3.2.0, with debug_info, not stripped |
| 195 | +``` |
| 196 | + |
| 197 | +If you are building C code as part of your Rust project, you may want to |
| 198 | +direct `cc-rs` to use an appropriate cross-compiler with the `CROSS_COMPILE` |
| 199 | +environment variable. You may also want to set the CFLAGS environment variable |
| 200 | +for the target. For example: |
| 201 | + |
| 202 | +```bash |
| 203 | +export CROSS_COMPILE=arm-linux-gnueabi |
| 204 | +export CFLAGS_arm_unknown_linux_gnueabi="-march=armv6" |
| 205 | +``` |
| 206 | + |
| 207 | +(Note that the dashes (`-`) turn to underscores (`_`) to form the name of the |
| 208 | +CFLAGS environment variable) |
| 209 | + |
| 210 | +If you are building for a Tier 3 target using `-Zbuild-std` (on Nightly Rust), |
| 211 | +you need to set these variables as well: |
| 212 | + |
| 213 | +```bash |
| 214 | +export CXX_arm_unknown_linux_gnueabi=arm-linux-gnueabi-g++ |
| 215 | +export CC_arm_unknown_linux_gnueabi=arm-linux-gnueabi-gcc |
| 216 | +cargo +nightly build -Zbuild-std --target=arm-unknown-linux-gnueabi |
| 217 | +``` |
0 commit comments