|
| 1 | +> PSA: building binaries and cdylibs for the ARM Cortex-M architecture will break with today's nightly |
| 2 | +
|
| 3 | +The default linker for the 4 ARM Cortex-M targets listed below has changed from |
| 4 | +`arm-none-eabi-gcc` to `rust-lld` in the latest nightly. |
| 5 | + |
| 6 | +- `thumbv6m-none-eabi` |
| 7 | +- `thumbv7m-none-eabi` |
| 8 | +- `thumbv7em-none-eabi` |
| 9 | +- `thumbv7em-none-eabihf` |
| 10 | + |
| 11 | +This will break the builds of *binaries* and *cdylibs* that were using the |
| 12 | +old default linker (`arm-none-eabi-gcc`) *and* additionally pass extra flags to |
| 13 | +the linker using any of these rustc flags: `-C link-arg`, `-C link-args`, `-Z |
| 14 | +pre-link-arg` or `-Z pre-link-args`. Building libraries (`rlib`s and |
| 15 | +`staticlib`s) is not affected by this change. |
| 16 | + |
| 17 | +This change won't affect stable users when it reaches the 1.30 release because, |
| 18 | +as of 1.28, it's not possible to build binaries or cdylibs for those targets on |
| 19 | +the stable channel. Building libraries for those targets is possible on stable |
| 20 | +but it's not affected by this change. |
| 21 | + |
| 22 | +### Rationale |
| 23 | + |
| 24 | +This breaking change was intentional. |
| 25 | + |
| 26 | +We, the [embedded WG], wanted to reduce the number of external tools required to |
| 27 | +build embedded programs for the ARM Cortex-M architecture. By switching the |
| 28 | +default linker to the LLD linker that's shipped with the Rust toolchain the user |
| 29 | +no longer needs to install an ARM linker (like `arm-none-eabi-gcc` or |
| 30 | +`arm-none-eabi-ld`) to build Rust programs. |
| 31 | + |
| 32 | +[embedded WG]: https://github.com/rust-embedded/wg |
| 33 | + |
| 34 | +Before landing this change we first [consulted] with the community if they |
| 35 | +thought this breaking change was worth it. We received over 20 positive responses |
| 36 | +representing the Cortex-M team (part of the embedded WG), the Tock OS project, |
| 37 | +the embed-rs organization and independent developers. |
| 38 | + |
| 39 | +The consensus was that it was worth to make the default configuration more self |
| 40 | +contained and that if we were to make the change it had to be made before it |
| 41 | +became possible to build binaries on stable otherwise it wouldn't be possible |
| 42 | +to make this change without breaking stable builds. |
| 43 | + |
| 44 | +[consulted]: https://github.com/rust-embedded/wg/issues/160 |
| 45 | + |
| 46 | +### How to fix your build |
| 47 | + |
| 48 | +If you are affected by this change you'll observe a linker error with a message |
| 49 | +similar to one shown below: |
| 50 | + |
| 51 | +``` console |
| 52 | +$ # these are the custom linker flags of the project |
| 53 | +$ cat .cargo/config |
| 54 | +``` |
| 55 | + |
| 56 | +``` toml |
| 57 | +[target.thumbv7m-none-eabi] |
| 58 | +runner = 'arm-none-eabi-gdb' |
| 59 | +rustflags = [ |
| 60 | + "-C", "link-arg=-Wl,-Tlink.x", |
| 61 | + "-C", "link-arg=-nostartfiles", |
| 62 | +] |
| 63 | + |
| 64 | +[build] |
| 65 | +target = "thumbv7m-none-eabi" |
| 66 | +``` |
| 67 | + |
| 68 | +``` |
| 69 | +$ cargo build |
| 70 | +error: linking with `rust-lld` failed: exit code: 1 |
| 71 | + | |
| 72 | + = note: "rust-lld" "-flavor" "gnu" (..) |
| 73 | + = note: rust-lld: error: unknown argument: -Wl,-Tlink.x |
| 74 | + rust-lld: error: unknown argument: -nostartfiles |
| 75 | +``` |
| 76 | + |
| 77 | +There are two ways to fix the problem. |
| 78 | + |
| 79 | +#### Option A: switch back to GCC |
| 80 | + |
| 81 | +The easiest way is to switch back to using `arm-none-eabi-gcc` as the linker. To |
| 82 | +do so pass the flag `-C linker=arm-none-eabi-gcc` to rustc. In the above example |
| 83 | +you can do that in the `.cargo/config` file. |
| 84 | + |
| 85 | +``` console |
| 86 | +$ # these are the custom linker flags of the project |
| 87 | +$ cat .cargo/config |
| 88 | +``` |
| 89 | + |
| 90 | +``` toml |
| 91 | +[target.thumbv7m-none-eabi] |
| 92 | +runner = 'arm-none-eabi-gdb' |
| 93 | +rustflags = [ |
| 94 | + "-C", "linker=arm-none-eabi-gcc", # ADDED |
| 95 | + "-C", "link-arg=-Wl,-Tlink.x", |
| 96 | + "-C", "link-arg=-nostartfiles", |
| 97 | +] |
| 98 | + |
| 99 | +[build] |
| 100 | +target = "thumbv7m-none-eabi" |
| 101 | +``` |
| 102 | + |
| 103 | +``` |
| 104 | +$ cargo build && echo It works now |
| 105 | +It works now |
| 106 | +``` |
| 107 | + |
| 108 | +#### Option B: tweak the additional linker arguments |
| 109 | + |
| 110 | +The other option is to tweak the additional linker arguments so they'll be |
| 111 | +accepted by LLD. In the above example the `-nostartfiles` flag can be dropped |
| 112 | +because that's the default behavior of LLD, and the flags prefixed by `-Wl,` |
| 113 | +will have to lose their prefix. |
| 114 | + |
| 115 | +``` console |
| 116 | +$ # these are the custom linker flags of the project |
| 117 | +$ cat .cargo/config |
| 118 | +``` |
| 119 | + |
| 120 | +``` toml |
| 121 | +[target.thumbv7m-none-eabi] |
| 122 | +runner = 'arm-none-eabi-gdb' |
| 123 | +rustflags = [ |
| 124 | + "-C", "link-arg=-Tlink.x", # CHANGED |
| 125 | +# "-C", "link-arg=-nostartfiles", # REMOVED |
| 126 | +] |
| 127 | + |
| 128 | +[build] |
| 129 | +target = "thumbv7m-none-eabi" |
| 130 | +``` |
| 131 | + |
| 132 | +``` console |
| 133 | +$ cargo build && echo It works now |
| 134 | +It works now |
| 135 | +``` |
| 136 | + |
| 137 | +With this approach your build will no longer depend on an external linker. |
| 138 | + |
| 139 | +#### Should I prefer option A or B? |
| 140 | + |
| 141 | +If you are linking to a system installed C library like `newlib-arm-none-eabi` |
| 142 | +then you should continue to use GCC. The default library search path of |
| 143 | +`arm-none-eabi-gcc` includes the path to those libraries. |
| 144 | + |
| 145 | +If you are not linking to any C code then you should prefer LLD then you won't |
| 146 | +need to install the `arm-none-eabi` toolchain. |
0 commit comments