Skip to content

Commit

Permalink
fix(port_riscv)!: make riscv-rt optional
Browse files Browse the repository at this point in the history
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.

P.S. I'll try to use an exclamation mark (`!`) in the commit headline
to indicate breaking changes from now on, as per [Conventional Commits
1.0.0][5].

[1]: rust-lang/rust#95604
[2]: rust-lang/rust#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
[5]: https://www.conventionalcommits.org/en/v1.0.0/
  • Loading branch information
yvt committed May 24, 2022
1 parent e82d2de commit 7442395
Show file tree
Hide file tree
Showing 5 changed files with 8 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/r3_port_riscv/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/r3_port_riscv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion src/r3_port_riscv/src/lib.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions src/r3_port_riscv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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")]
Expand Down Expand Up @@ -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::*;
Expand Down
1 change: 1 addition & 0 deletions src/r3_port_riscv_test_driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ run = [
boot-minimal-s = []
# Use `riscv-rt` for the startup code
boot-rt = [
"r3_port_riscv/riscv-rt",
"riscv-rt",
]

Expand Down

0 comments on commit 7442395

Please sign in to comment.