|
| 1 | +- Feature Name: `target_modifiers` |
| 2 | +- Start Date: 2024-10-24 |
| 3 | +- RFC PR: [rust-lang/rfcs#3716](https://github.com/rust-lang/rfcs/pull/3716) |
| 4 | +- Rust Issue: None |
| 5 | + |
| 6 | +# Summary |
| 7 | +[summary]: #summary |
| 8 | + |
| 9 | +* We introduce the concept of "target modifier". A target modifier is a flag |
| 10 | + where it may be unsound if you link together two compilation units that |
| 11 | + disagree on the flag. |
| 12 | +* We fail the build if rustc can see two Rust compilation units that do not |
| 13 | + agree on the exact set of target modifier flags. |
| 14 | +* There are already several existing flags that could fall into this category. |
| 15 | + There are also hypothetical new flags that do. |
| 16 | +* The error can be silenced using the `-Cunsafe-allow-abi-mismatch` escape |
| 17 | + hatch. |
| 18 | +* Not having a stable way to build stdlib crates does not block stabilization |
| 19 | + of target modifiers. |
| 20 | +* As a future extension we may be able to relax the rules to allow some |
| 21 | + specific kinds of mismatches. |
| 22 | +* This RFC does not stabilize any target modifiers. That should happen in |
| 23 | + follow-up MCPs/FCPs/RFCs/etc. |
| 24 | + |
| 25 | +# Motivation |
| 26 | +[motivation]: #motivation |
| 27 | + |
| 28 | +As Rust expands into low-level domains, there will be a need for precise |
| 29 | +control over how code is compiled. This often manifests as a new compiler flag. |
| 30 | +Some of these flags trigger undefined behavior if used incorrectly, which is in |
| 31 | +tension with Rust's safety goals. This RFC proposes a new mechanism to allow |
| 32 | +use of such flags while also preventing undefined behavior. |
| 33 | + |
| 34 | +The primary goal of this RFC is to unblock *stabilization* of target modifier |
| 35 | +flags. Adding them as unstable (and unsound) flags is already happening today |
| 36 | +without this RFC. |
| 37 | + |
| 38 | +## The Linux Kernel |
| 39 | + |
| 40 | +The Linux Kernel has run into a handful of cases where it is necessary to tweak |
| 41 | +the ABI used in the kernel. Often, this is done conditionally depending on a |
| 42 | +configuration option. A few examples: |
| 43 | + |
| 44 | +* When using `CONFIG_SHADOW_CALLSTACK` the x18 register must be reserved in the |
| 45 | + ABI with `-Zfixed-x18`. |
| 46 | +* The `-Ctarget-feature=-neon` flag is used to prevent use of floating points |
| 47 | + on arm. |
| 48 | +* On 32-bit x86, `-Zreg-struct-return` and `-Zregparm=3` are used. |
| 49 | +* When using `CONFIG_CFI_CLANG` the kCFI sanitizer is enabled with |
| 50 | + `-Zsanitizer=kcfi`. Unlike most other sanitizers, this sanitizer is used in |
| 51 | + production. |
| 52 | +* In several different cases, `-Zpatchable-function-entry` is used to add nops |
| 53 | + before or after the function entrypoint. When mixed with `-Zsanitizer=kcfi` |
| 54 | + this causes special considerations as kcfi works by placing a tag before the |
| 55 | + function entrypoint. |
| 56 | +* To support `MITIGATION_RETPOLINE` and `MITIGATION_SLS`, `target.json` is used |
| 57 | + on x86. |
| 58 | + |
| 59 | +We expect there to be more examples in the future. |
| 60 | + |
| 61 | +## Sanitizers |
| 62 | + |
| 63 | +There is [an ongoing effort to stabilize some of the sanitizers][issue123615]. |
| 64 | +However, this effort explicitly aims to stabilize sanitizers that can be used |
| 65 | +without rebuilding the stdlib. With this RFC, that is no longer a blocker as |
| 66 | +the remaining sanitizers can be classified as a target modifier and then |
| 67 | +stabilized. |
| 68 | + |
| 69 | +[issue123615]: https://github.com/rust-lang/rust/issues/123615 |
| 70 | + |
| 71 | +## Embedded Targets |
| 72 | + |
| 73 | +Currently, embedded platforms such as `thumb*` or `rv*` use separate targets |
| 74 | +for configuration with significant ABI changes. For `thumb*` targets, this is |
| 75 | +currently limited to Hard- vs Soft-Float which can cause issues when linked. |
| 76 | +However for RISC-V targets, the F (32-bit hardware float), D (64-bit hardware |
| 77 | +float), and Q (128-bit hardware float) extensions all can [potentially change |
| 78 | +the ABI][riscv-float], which would increase the number of required targets. The |
| 79 | +[E extension][riscv-e] may also change the ABI by limiting the number of |
| 80 | +registers used by the I (integer operations) extension. |
| 81 | + |
| 82 | +[riscv-float]: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#named-abis |
| 83 | +[riscv-e]: https://github.com/riscv/riscv-isa-manual/blob/main/src/rv32e.adoc |
| 84 | + |
| 85 | +## Existing -C flags that are unsound |
| 86 | + |
| 87 | +It has recently been discovered that several existing `-C` flags modify the |
| 88 | +ABI, making them unsound. Examples: |
| 89 | + |
| 90 | +* [`-Csoft-float`](https://github.com/rust-lang/rust/issues/129893) |
| 91 | +* [`-Ctarget-feature=-neon`](https://github.com/rust-lang/rust/issues/131058) |
| 92 | +* [`-Clinker-plugin-lto`](https://github.com/rust-lang/rust/issues/127979) |
| 93 | +* [`-Cllvm-args`](https://github.com/rust-lang/rust/issues/131800#issuecomment-2418595757) |
| 94 | +* Possibly `-Ccode-model` and `-Crelocation-model` |
| 95 | + |
| 96 | +This problem is a new discovery and it's still not clear how to solve it. |
| 97 | +Target modifiers will not solve all of the flags; [some flags are just |
| 98 | +unfixable and need to be removed][issue130968]. But I expect that target |
| 99 | +modifiers will be the solution for some of these flags. |
| 100 | + |
| 101 | +[issue130968]: https://github.com/rust-lang/rust/issues/130968 |
| 102 | + |
| 103 | +# Guide-level explanation |
| 104 | +[guide-level-explanation]: #guide-level-explanation |
| 105 | + |
| 106 | +The Rust compiler has many flags that affect how source code is turned into |
| 107 | +machine code. Some flags can be turned on and off for each CU (compilation |
| 108 | +unit) in your project separately, and other flags must be applied to the entire |
| 109 | +application as a whole. The typical reason for flags to be in the latter |
| 110 | +category is that change some aspect of the ABI. For example, |
| 111 | +`-Zreg-struct-return` changes how to return a struct from a function call, and |
| 112 | +both the caller and callee must agree on how to do that even if they are in |
| 113 | +different CUs. |
| 114 | + |
| 115 | +The Rust compiler will detect if you incorrectly use a flag that must be |
| 116 | +applied to the application as a whole. For example, if you compile the standard |
| 117 | +library with `-Zreg-struct-return`, but don't pass the flag when compiling a |
| 118 | +dependency, then you will get the following error: |
| 119 | +``` |
| 120 | +error: mixing -Zreg-struct-return will cause an ABI mismatch |
| 121 | +
|
| 122 | +help: This error occurs because the -Zreg-struct-return flag modifies the ABI, |
| 123 | + and different crates in your project were compiled with inconsistent |
| 124 | + settings. |
| 125 | +help: To resolve this, ensure that -Zreg-struct-return is set to the same value |
| 126 | + for all crates during compilation. |
| 127 | +help: To ignore this error, recompile with the following flag: |
| 128 | + -Cunsafe-allow-abi-mismatch=reg-struct-return |
| 129 | +``` |
| 130 | +As an escape hatch, you can use `-Cunsafe-allow-abi-mismatch=reg-struct-return` |
| 131 | +to disable the error. Using this flag is unsafe as incorrect use of the ABI is |
| 132 | +undefined behavior. However, there may be some cases where the check is too |
| 133 | +strict, and you can use the flag to proceed in those cases. |
| 134 | + |
| 135 | +The requirement that all CUs agree includes stdlib crates (core, alloc, std), |
| 136 | +so using these flags usually requires that you compile your own standard |
| 137 | +library with `-Zbuild-std` or by directly invoking `rustc`. That said, some |
| 138 | +flags (e.g., `-Cpanic`) have mechanisms to avoid this requirement. |
| 139 | + |
| 140 | +# Reference-level explanation |
| 141 | +[reference-level-explanation]: #reference-level-explanation |
| 142 | + |
| 143 | +A compiler flag can be classified as a _target modifier_. When a flag is a |
| 144 | +target modifier, it can be undefined behavior to link together two CUs that |
| 145 | +disagree on the flag. |
| 146 | + |
| 147 | +To avoid unsoundness from mixing target modifiers, rustc will store the set of |
| 148 | +target modifiers in use in the crate metadata of each crate. Whenever rustc is |
| 149 | +invoked, it will inspect the crate metadata of all crates that are visible to |
| 150 | +it (usually the current crate and its direct dependencies) and emit an error if |
| 151 | +any of them have mismatched target modifiers in use. |
| 152 | + |
| 153 | +The `-Cunsafe-allow-abi-mismatch=flagname` flag can be used when compiling a |
| 154 | +crate to indicate that it should not be included in the list of crates when |
| 155 | +checking that all crates agree on `flagname`. |
| 156 | + |
| 157 | +Note that `-Cunsafe-allow-abi-mismatch=flagname` should be passed to rustc when |
| 158 | +compiling the crate that uses an incompatible value for `flagname`, which may |
| 159 | +not be the same rustc invocation as the one where the mismatch is detected. For |
| 160 | +example, if you build four CUs A,B,C,D where D depends on A,B,C and C is the |
| 161 | +only one with a different value for `flagname`, then the mismatch is detected |
| 162 | +by rustc when compiling D, but `-Cunsafe-allow-abi-mismatch` should be used |
| 163 | +when compiling C. |
| 164 | + |
| 165 | +## Stabilization |
| 166 | + |
| 167 | +It is possible to stabilize target modifiers even if they cannot be utilized |
| 168 | +without an unstable feature such as `-Zbuild-std`. |
| 169 | + |
| 170 | +# Drawbacks |
| 171 | +[drawbacks]: #drawbacks |
| 172 | + |
| 173 | +## Teaching |
| 174 | +[teaching]: #teaching |
| 175 | + |
| 176 | +We should be careful to not introduce too many concepts that end-users have to |
| 177 | +learn. |
| 178 | + |
| 179 | +It is intentional that the guide-level section of this RFC does not use the |
| 180 | +word "target modifier". The "target modifier" name is not intended to be used |
| 181 | +outside of the compiler internals and very technical documentation. Compiler |
| 182 | +errors should not say "error: trying to mix target modifiers" or something like |
| 183 | +that; rather the error should just say that mixing `-Cfoo` may cause ABI |
| 184 | +issues. |
| 185 | + |
| 186 | +For similar reasons, the flag for silencing the error is called |
| 187 | +`-Cunsafe-allow-abi-mismatch` with the word "ABI" to avoid having to teach the |
| 188 | +user about mismatched flags or target modifiers. |
| 189 | + |
| 190 | +# Rationale and alternatives |
| 191 | +[rationale-and-alternatives]: #rationale-and-alternatives |
| 192 | + |
| 193 | +## Why not just add flags like normal? |
| 194 | + |
| 195 | +Preventing undefined behaviour is an important goal of the Rust project. If we |
| 196 | +add flags that change the abi, then that is in direct opposition to that goal, |
| 197 | +as mixing them would lead to UB. |
| 198 | + |
| 199 | +## Why not just add new targets? |
| 200 | + |
| 201 | +The flag that started this entire discussion is `-Zfixed-x18`. This flag |
| 202 | +changes the ABI by changing the x18 register from a caller-saved temporary |
| 203 | +register to a reserved register. At the time, people suggested adding a new |
| 204 | +target (e.g., `aarch64-unknown-none-fixed18`), instead of adding a dedicated |
| 205 | +`-Zfixed-x18` flag. |
| 206 | + |
| 207 | +The primary benefit of adding a new target is that it's a workaround for |
| 208 | +`-Zbuild-std` being unstable. Each new target will get a prebuilt stdlib, which |
| 209 | +sidesteps the need for building your own stdlib. |
| 210 | + |
| 211 | +This RFC does not propose this solution because: |
| 212 | + |
| 213 | +1. The primary benefit is not being blocked on stabilization of `-Zbuild-std`. |
| 214 | + However, I don't think we really are blocked on stabilization of |
| 215 | + `-Zbuild-std` in the first place. See [the stabilization of target modifier |
| 216 | + flags section][stabilization] below. |
| 217 | +2. Target modifiers help with other problems such as unblocking the |
| 218 | + stabilization of sanitizers as well as existing `-C` flags that are unsound |
| 219 | + due to ABI issues. Adding new targets would leave these issues unsolved. |
| 220 | +3. Adding new targets risks an exponential number of targets. In the kernel on |
| 221 | + x86 we would need 8 different targets to support the different possible |
| 222 | + kernel configurations. It's not hard to imagine that number growing to 16 or |
| 223 | + 32 targets in the near future, especially once you consider that other |
| 224 | + embedded projects may have their own set of target modifier flags. |
| 225 | +4. Adding new prebuilt stdlibs does not actually help the projects that need |
| 226 | + these flags. Even if a prebuilt stdlib is provided for every combination of |
| 227 | + ABI-affecting flags that the kernel may need, the kernel has other reasons |
| 228 | + that require building a custom `core`. |
| 229 | + |
| 230 | +## Why not use `target.json` |
| 231 | + |
| 232 | +Because the `target.json` feature is perma-unstable, and this RFC primarily |
| 233 | +concerns itself with unblocking the _stabilization_ of these flags. Adding |
| 234 | +target modifiers as unstable flags is already happening today. (However, if |
| 235 | +this RFC gets accepted, it becomes a soundness bug to add such unstable flags |
| 236 | +without wiring them up with the target modifier machinery.) |
| 237 | + |
| 238 | +One possible alternative would be to stabilize a subset of `target.json`. |
| 239 | +However, I don't think there's much benefit to this. It just means that you now |
| 240 | +have to learn two different ways of passing flags to the compiler. See [the |
| 241 | +Teaching section][teaching] above. |
| 242 | + |
| 243 | +It would also be inconvenient to use in external build systems. Right now, the |
| 244 | +kernel passes the `-Zfixed-x18` flag like this: |
| 245 | +```make |
| 246 | +ifeq ($(CONFIG_SHADOW_CALL_STACK), y) |
| 247 | +KBUILD_CFLAGS += -ffixed-x18 |
| 248 | +KBUILD_RUSTFLAGS += -Zfixed-x18 |
| 249 | +endif |
| 250 | +``` |
| 251 | +If `-Zfixed-x18` had to be specified in a `target.json` file, it would need to |
| 252 | +happen in an entirely different part of the kernel build system. It is better |
| 253 | +to specify the rustc flag together with the clang/gcc flag. |
| 254 | + |
| 255 | +## Stabilization of target modifiers |
| 256 | +[stabilization]: #stabilization-of-target-modifiers |
| 257 | + |
| 258 | +Using a target modifier without rebuilding the Rust stdlib is often not |
| 259 | +possible. This means that some target modifiers can only be used in tandem with |
| 260 | +`-Zbuild-std`, which is currently unstable. |
| 261 | + |
| 262 | +However, there's no reason we _have_ to block the stabilization of target |
| 263 | +modifiers on the stabilization of `-Zbuild-std`. If a target modifier `-Cfoo` |
| 264 | +is stabilized, then you can break users of `-Cfoo` with the reason "we changed |
| 265 | +the way you pass flags to `core`", but you can't break users with the reason |
| 266 | +"we renamed `-Cfoo` to `-Cbar`; this is okay because you're also using |
| 267 | +`-Zbuild-std` even though the rename is unrelated to `-Zbuild-std`". |
| 268 | + |
| 269 | +## Not all mismatches are unsound |
| 270 | +[not-all-unsound]: #not-all-mismatches-are-unsound |
| 271 | + |
| 272 | +This RFC says that mismatching target modifiers in any way results in a build |
| 273 | +error. However, there are a lot of cases where the real rules are more |
| 274 | +complicated than that. For example, with the following three CUs: |
| 275 | + |
| 276 | +* CU A compiled with `-Zfixed-x18 -Zsanitizer=shadow-call-stack`. |
| 277 | +* CU B compiled with `-Zfixed-x18`. |
| 278 | +* CU C compiled with neither flag. |
| 279 | + |
| 280 | +It is unsound to link together CUs A and C, but linking A with B or B with C is |
| 281 | +sound. |
| 282 | + |
| 283 | +However, real-world scenarios where mismatching a target modifier is necessary |
| 284 | +are quite uncommon. The only case I'm aware of is the runtime for a sanitizer. |
| 285 | +For example, when ASAN (address sanitizer) detects a bug, it calls into a |
| 286 | +special ASAN-failure-handler function. The function for handling ASAN-failures |
| 287 | +should not be sanitized. |
| 288 | + |
| 289 | +Making the compiler accept specific mismatches that are sound is out of scope |
| 290 | +for this RFC. Such decisions will be made on a flag-by-flag basis in follow-up |
| 291 | +decisions (most likely an MCP). Until then, end-users can use |
| 292 | +`-Cunsafe-allow-abi-mismatch` to proceed in such cases. |
| 293 | + |
| 294 | +## Cases that are not caught |
| 295 | +[not-caught]: #cases-that-are-not-caught |
| 296 | + |
| 297 | +This RFC proposes to store information in the crate metadata to detect ABI |
| 298 | +mismatches. However, this means that there are two cases that could result in |
| 299 | +mismatches being missed: |
| 300 | + |
| 301 | +* When rustc is not doing the final link, different incompatible leaf modules |
| 302 | + might not get detected. For instance, using the CUs A,B,C from [the previous |
| 303 | + section][not-all-unsound], then if stdlib is CU B and there are two leaf CUs |
| 304 | + A and C, then the incompatibility between A and C would not get detected |
| 305 | + unless rustc performs the final link. |
| 306 | +* With dynamic linking, you may have two shared objects compiled completely |
| 307 | + separately with incompatible ABIs. |
| 308 | + |
| 309 | +Note that the first situation can never happen with the base proposal: if we |
| 310 | +require exact matches, then all CUs must agree because all CUs depend on |
| 311 | +`core`. The missed detection requires that we don't consider AB or BC |
| 312 | +incompatible. This could be an argument in favor of not allowing any mismatches |
| 313 | +with the shadow call stack sanitizer (which you never want to mix in practice |
| 314 | +anyway). |
| 315 | + |
| 316 | +The dynamic linking case is considered acceptable. Detecting it is out of scope |
| 317 | +of this RFC. |
| 318 | + |
| 319 | +## Name mangling |
| 320 | + |
| 321 | +It has been proposed that the modified target could be encoded in the name |
| 322 | +mangling scheme to help catch the two cases from [the previous |
| 323 | +section][not-caught]. However, this raises a bunch of open questions: |
| 324 | + |
| 325 | +1. It probably does not help catch the first case. Dynamic function calls |
| 326 | + between the leaf modules wouldn't get caught, so that would require that one |
| 327 | + of the leaf modules references a symbol defined by the other leaf module. |
| 328 | + However, I find it hard to imagine this happening in the real world unless |
| 329 | + the symbol is marked `#[no_mangle]`. |
| 330 | +2. Similarly, if two dynamic objects are compiled completely separately, they |
| 331 | + probably do not reference each other through anything other than symbols |
| 332 | + marked `#[no_mangle]`. While it could potentially identify a mismatch where |
| 333 | + component A depends on component B, and component B is recompiled with a |
| 334 | + different ABI while using the old version of A, this scenario is not |
| 335 | + well-supported to start with because of Rust's unstable ABI. |
| 336 | +3. Some ABI-affecting flags only change the C ABI, but those symbols are |
| 337 | + usually using `#[no_mangle]`. |
| 338 | +4. Do we really want to make our symbol names even longer? |
| 339 | + |
| 340 | +For the above reasons, name mangling is not proposed as a mechanism for |
| 341 | +detection for now. However, it could be a potential future addition. |
| 342 | + |
| 343 | +## Policy around flags that might not be ABI affecting |
| 344 | + |
| 345 | +Some flags have an unclear status where it is unclear whether it affects the |
| 346 | +ABI. For example, `-Zpatchable-function-entry` (which adds nop instructions |
| 347 | +before/after the function entrypoint) generally isn't considered to affect the |
| 348 | +ABI, but if combined with `-Zsanitizer=kcfi` then it does affect it since kcfi |
| 349 | +works by placing a hash of the function signature before the function |
| 350 | +entrypoint. Since `-Zsanitizer=kcfi` already needs to be a target modifier |
| 351 | +_anyway_, you could argue that `-Zpatchable-function-entry` doesn't need to be |
| 352 | +one. |
| 353 | + |
| 354 | +However, for these cases where we are uncertain, we take a conservative |
| 355 | +approach and mark them as a target modifier. It is not breaking to relax the |
| 356 | +rules in future releases. |
| 357 | + |
| 358 | +As for flags such as `-Cllvm-args` that can do basically anything, it may make |
| 359 | +more sense to just rename it to `-Cunsafe-llvm-args` rather than use the target |
| 360 | +modifier functionality. |
| 361 | + |
| 362 | +## Problems with mixing non-target-modifiers |
| 363 | + |
| 364 | +I discussed this proposal with people from other communities (mainly kernel and |
| 365 | +C folks), and they shared several other cases where mixing flags are a problem. |
| 366 | +They pointed out that there are some flags where mixing them is really bad and |
| 367 | +should be detected, but which are not ABI issues or unsound per se. The most |
| 368 | +common example of this is exploit mitigations, where mixing the flags will |
| 369 | +silently lead to a vulnerable binary. On the other hand, ABI mismatches usually |
| 370 | +fail in a loud way, so they were not as concerned about those. |
| 371 | + |
| 372 | +The sections below describe several such cases. They are intended to provide |
| 373 | +additional context for the reader to better understand the problem space. We |
| 374 | +will likely want to use the same infrastructure for detecting some of the |
| 375 | +mismatches mentioned below, but the precise list is out of scope of this RFC. |
| 376 | + |
| 377 | +Since the cases below are not unsound, the flag for overriding them should not |
| 378 | +include the word "unsafe". |
| 379 | + |
| 380 | +### Exploit mitigations |
| 381 | + |
| 382 | +There are some mitigations that are used to mitigate CPU speculation |
| 383 | +vulnerabilities (e.g., SPECTRE) or used to make exploitation of vulnerabilities |
| 384 | +harder (e.g., control flow protection), which work by either telling the |
| 385 | +compiler to generate code that include instructions to prevent CPU speculation |
| 386 | +in some specific locations, or telling the compiler to generate code that |
| 387 | +checks that destinations of indirect branches are one of their valid |
| 388 | +destinations in the control flow graph. These mitigations usually don't change |
| 389 | +the ABI, as they just change how code is generated within functions. |
| 390 | + |
| 391 | +The problem is that the attacks you are trying to mitigate involve either |
| 392 | +forcing CPU speculation in some specific locations or changing the control flow |
| 393 | +to an arbitrary attacker-controlled address. If you have an unprotected |
| 394 | +specific location or unprotected indirect branch anywhere in your program, an |
| 395 | +attacker may still be able to use it either forcing CPU speculation in these |
| 396 | +unprotected specific locations, or by changing addresses in memory used by |
| 397 | +these unprotected indirect branches. This means that if you only apply the |
| 398 | +mitigation to some CUs, then the CUs that lack the mitigation will be |
| 399 | +completely unprotected, and the mitigation might be bypassable. |
| 400 | + |
| 401 | +### Sanitizers |
| 402 | + |
| 403 | +This case is rather similar to exploit mitigations. |
| 404 | + |
| 405 | +Some sanitizers can be mixed and matched between CUs without breaking the ABI. |
| 406 | +For example, on the Android aarch64 target, the shadow call stack sanitizer |
| 407 | +does not change the ABI, and can be freely mixed between CUs. However, the |
| 408 | +sanitizer does not catch violations in CUs that don't enable the sanitizer. |
| 409 | + |
| 410 | +For sanitizers used in production (such as shadow call stack or kcfi) this is |
| 411 | +particularly problematic, as a vulnerability in sanitized code may allow you to |
| 412 | +jump into unsanitized code. |
| 413 | + |
| 414 | +### .note.gnu.property |
| 415 | + |
| 416 | +In the case of BTI (`-Zbranch-protection=bti`), the mitigation relies on the |
| 417 | +kernel's ELF loader setting a special bit in the page table. However, setting |
| 418 | +this bit is only valid if BTI is enabled everywhere. The compiler will use a |
| 419 | +section called `.note.gnu.property` to tell the linker whether BTI is enabled, |
| 420 | +and the linker only propagates `.note.gnu.property` if all CUs agree on it. |
| 421 | +This means that if one CU is missing BTI, the linker will disable it for the |
| 422 | +entire executable, and the kernel's ELF loader will not set the bit in the page |
| 423 | +tables when loading the machine code, rendering BTI ineffective. |
| 424 | + |
| 425 | +### Performance |
| 426 | + |
| 427 | +Another reason is performance. One some targets, the precompiled stdlib always |
| 428 | +comes with panic landing pads, even if you're using `-Cpanic=abort`. It's also |
| 429 | +usually compiled with a very minimal set of target features for greater |
| 430 | +compatibility. These discrepancies can have an unacceptable impact on |
| 431 | +performance. |
| 432 | + |
| 433 | +### Code patching |
| 434 | + |
| 435 | +You might use `-Zbranch-protection=pac-ret` or `-Zpatchable-function-entry` to |
| 436 | +insert special instructions at the beginning/end of all functions so you can |
| 437 | +use runtime code-patching to replace them later. It is only because of the |
| 438 | +runtime code-patching logic that these flags need to be used everywhere. |
| 439 | + |
| 440 | +### Debugging information |
| 441 | + |
| 442 | +Mixing CUs with different options for `-Cforce-unwind-tables`, |
| 443 | +`-Zdwarf-version`, or `-Zdebuginfo-compression` may result in a binary that you |
| 444 | +consider to be invalid as you may be unable to read the debugging information. |
| 445 | +But it would not be an ABI issue. |
| 446 | + |
| 447 | +# Prior art |
| 448 | +[prior-art]: #prior-art |
| 449 | + |
| 450 | +## The panic strategy |
| 451 | + |
| 452 | +The Rust compiler already *has* infrastructure to detect flag mismatches: the |
| 453 | +flags `-Cpanic` and `-Zpanic-in-drop`. The prebuilt stdlib comes with different |
| 454 | +pieces depending on which strategy is used, although panic landing flags are |
| 455 | +not entirely removed when using `-Cpanic=abort`, as only part of the prebuilt |
| 456 | +stdlib is switched out. |
| 457 | + |
| 458 | +## Global target modifiers |
| 459 | + |
| 460 | +A suggestion that has come up several times |
| 461 | +([1](https://github.com/rust-lang/rust/issues/116972), |
| 462 | +[2](https://github.com/rust-lang/rust/issues/116973), |
| 463 | +[3](https://github.com/rust-lang/rust/issues/121970#issuecomment-1978605782)) |
| 464 | +is to have a variation of `-Ctarget-feature=` that must be applied globally, |
| 465 | +which could be called `-Cglobal-target-features=`. This is very similar to this |
| 466 | +RFC, though it is broader as the "target modifier" concept can apply to any |
| 467 | +compiler flag and not just to a single `-Cglobal-target-features=` flag. |
| 468 | + |
| 469 | +## Stabilization of things that require nightly features |
| 470 | + |
| 471 | +This RFC proposes that we shouldn't block stabilization of target modifiers on |
| 472 | +a stable way to build libcore. There is precedent in the rust project for |
| 473 | +unblocking stabilizations in this manner: When `#![no_std]` was stabilized, |
| 474 | +[the RFC][rfc1184] said the following: |
| 475 | + |
| 476 | +> As mentioned above, there are three separate lang items which are required by |
| 477 | +> the libcore library to link correctly. These items are: |
| 478 | +> |
| 479 | +> * `panic_fmt` |
| 480 | +> * `stack_exhausted` |
| 481 | +> * `eh_personality` |
| 482 | +> |
| 483 | +> This RFC does not attempt to stabilize these lang items for a number of |
| 484 | +> reasons: |
| 485 | +> |
| 486 | +> * The exact set of these lang items is somewhat nebulous and may change over |
| 487 | +> time. |
| 488 | +> * The signatures of each of these lang items can either be platform-specific |
| 489 | +> or it’s just “too weird” to stabilize. |
| 490 | +> * These items are pretty obscure and it’s not very widely known what they do |
| 491 | +> or how they should be implemented. |
| 492 | +> |
| 493 | +> Stabilization of these lang items (in any form) will be considered in a |
| 494 | +> future RFC. |
| 495 | +
|
| 496 | +This means that no-std can't actually be used without providing these symbols |
| 497 | +in some other way. Doing so is unstable. |
| 498 | + |
| 499 | +[rfc1184]: https://rust-lang.github.io/rfcs/1184-stabilize-no_std.html |
| 500 | + |
| 501 | +## .note.gnu.property |
| 502 | + |
| 503 | +The `.note.gnu.property` section discussed previously is an example of C code |
| 504 | +detecting mismatches of a flag at link time. |
| 505 | + |
| 506 | +# Unresolved questions |
| 507 | +[unresolved-questions]: #unresolved-questions |
| 508 | + |
| 509 | +This RFC does not stabilize any target modifiers. Such decisions should be made |
| 510 | +as a follow-up to this RFC on a flag-by-flag basis using the usual process for |
| 511 | +stabilizing a compiler flag. |
| 512 | + |
| 513 | +The `-Cunsafe-allow-abi-mismatch` flag will be stabilized when the first target |
| 514 | +modifier is stabilized. |
| 515 | + |
| 516 | +# Future possibilities |
| 517 | +[future-possibilities]: #future-possibilities |
| 518 | + |
| 519 | +A possible future extension could be to detect inconsistencies between the ABI |
| 520 | +of C code and Rust code. This would be an interesting extension, but it is not |
| 521 | +critical for target modifiers as calling into C is inherently unsafe to start |
| 522 | +with. Similarly, another possible future extension could be to catch ABI |
| 523 | +mismatches when using dynamic linking. |
0 commit comments