From ab4ee708f4f7f2cd89a547650e79543ed830384d Mon Sep 17 00:00:00 2001 From: yvt Date: Sun, 22 May 2022 22:14:08 +0900 Subject: [PATCH] feat(port_riscv): make `riscv-rt` optional Fixes linker errors in an application that is linked to `r3_port_riscv` and does not use the `riscv-rt`-compatible startup routine provided by `r3_port_riscv::use_rt!`. Rust implements `rlib`s as static libraries (archives). Linkers treat archives differently from object files: all object files participate in linking, while archives will only participate in linking if they can satisfy at least one undefined reference (version scripts don't count). This means that in an application that does not use `use_rt!`, `libriscv_rt*.rlib` does not participate in linking at all. This behavior also causes `#[no_mangle]` and `#[used]` items to be ignored by the linker (`KEEP` in linker scripts can't keep them either), leading to a long-standing bug in Rust ([rust-lang/rust#47384][2]). The situation changed with the merge of [rust-lang/rust#95604][1]. To fix [rust-lang/rust#47384][2], this PR introduced a synthetic object file containing references to all symbols pertaining to `#[no_mangle]` and `#[used]` items. `libriscv_rt*.rlib` now participates in linking, but the expectation is that `--gc-sections` will remove unused items in the end, unless they are explicitly retained by `KEEP` in linker scripts or other means. This change unexpectedly caused breakage in the tests for `qemu_sifive_u _s_rv(64,32)` targets, which use a custom startup routine instead of `use_rt!`. For some reason, the linker didn't respect `--gc-sections` for some items from `libriscv_rt*.rlib` and decided to include them in the output binary. These items reference symbols that are defined by the `riscv-rt` linker script, which isn't used by `qemu_sifive_u_s_ rv(64,32)` targets, hence the linker error. The thing is, `riscv-rt` outputs these items in the `.init` section[3], which is one of the section names hard-coded in LLD[4] to be excluded from `--gc-sections`. As a result, these items were considered for inclusion despite being referenced from nowhere. This commit works around this problem by making `r3_port_riscv`'s `riscv-rt` dependency optional, ensuring that `riscv-rt` participates in linking only if needed by the application. [1]: https://github.com/rust-lang/rust/pull/95604 [2]: https://github.com/rust-lang/rust/issues/47384 [3]: https://github.com/rust-embedded/riscv-rt/blob/7de3d2744a465ad723519c04f13c56664e138cb9/asm.S#L20 [4]: https://github.com/llvm/llvm-project/blob/b86440ecde5c1dec5b898a3f1bc08ab9853d5ed9/lld/ELF/MarkLive.cpp#L183 --- src/r3_port_riscv/CHANGELOG.md | 1 + src/r3_port_riscv/Cargo.toml | 2 +- src/r3_port_riscv/src/lib.md | 2 +- src/r3_port_riscv/src/lib.rs | 4 ++++ src/r3_port_riscv_test_driver/Cargo.toml | 1 + 5 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/r3_port_riscv/CHANGELOG.md b/src/r3_port_riscv/CHANGELOG.md index 30b9ca8d9d..95cf614745 100644 --- a/src/r3_port_riscv/CHANGELOG.md +++ b/src/r3_port_riscv/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - **Breaking (semver-exempt):** Change the target compiler version to `nightly-2022-03-30` +- **Breaking:** `use_rt!` is now gated behind `riscv-rt` Cargo feature. ### Fixed diff --git a/src/r3_port_riscv/Cargo.toml b/src/r3_port_riscv/Cargo.toml index 1c0604cb69..f3e1146889 100644 --- a/src/r3_port_riscv/Cargo.toml +++ b/src/r3_port_riscv/Cargo.toml @@ -27,7 +27,7 @@ unstringify = { version = "0.1.4" } seq-macro = { version = "0.3.0" } svgbobdoc = { version = "0.3.0" } macropol = { version = "0.1.3" } -riscv-rt = { version = ">= 0.6.0, < 0.9.0" } +riscv-rt = { version = ">= 0.6.0, < 0.9.0", optional = true } riscv = { version = ">= 0.5.0, < 0.8.0" } [package.metadata.docs.rs] diff --git a/src/r3_port_riscv/src/lib.md b/src/r3_port_riscv/src/lib.md index 895d8473a0..ddf4b0213c 100644 --- a/src/r3_port_riscv/src/lib.md +++ b/src/r3_port_riscv/src/lib.md @@ -2,7 +2,7 @@ The RISC-V port for [the R3 kernel](::r3_kernel). # Startup code -[`use_rt!`] hooks up the entry points ([`EntryPoint`]) using `#[`[`::riscv_rt::entry`]`]`. If this is not desirable for some reason, you can opt not to use it and call the entry points in other ways. +[`use_rt!`] hooks up the entry points ([`EntryPoint`]) using `#[`[`::riscv_rt::entry`]`]` (requires the **`riscv-rt`** Cargo feature). If this is not desirable for some reason, you can opt not to use it and call the entry points in other ways. # Interrupts diff --git a/src/r3_port_riscv/src/lib.rs b/src/r3_port_riscv/src/lib.rs index 4f07523c27..5abe508e23 100644 --- a/src/r3_port_riscv/src/lib.rs +++ b/src/r3_port_riscv/src/lib.rs @@ -9,6 +9,7 @@ #![feature(raw_ref_op)] #![feature(asm_const)] #![feature(asm_sym)] +#![feature(doc_cfg)] #![deny(unsafe_op_in_unsafe_fn)] #![cfg_attr( feature = "doc", @@ -55,6 +56,8 @@ pub mod plic { /// The binding for [`::riscv_rt`]. #[doc(hidden)] +#[cfg(feature = "riscv-rt")] +#[doc(cfg(feature = "riscv-rt"))] pub mod rt { pub mod cfg; #[cfg(target_os = "none")] @@ -85,6 +88,7 @@ pub mod sbi_timer { pub use self::mtime::cfg::*; pub use self::plic::cfg::*; +#[cfg(feature = "riscv-rt")] pub use self::rt::cfg::*; pub use self::sbi_timer::cfg::*; pub use self::threading::cfg::*; diff --git a/src/r3_port_riscv_test_driver/Cargo.toml b/src/r3_port_riscv_test_driver/Cargo.toml index e154b94d22..949b750116 100644 --- a/src/r3_port_riscv_test_driver/Cargo.toml +++ b/src/r3_port_riscv_test_driver/Cargo.toml @@ -24,6 +24,7 @@ run = [ boot-minimal-s = [] # Use `riscv-rt` for the startup code boot-rt = [ + "r3_port_riscv/riscv-rt", "riscv-rt", ]