Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 41 additions & 23 deletions compiler/rustc_metadata/src/creader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,11 @@ impl CStore {
let hash = dep.map(|d| d.hash);
let host_hash = dep.map(|d| d.host_hash).flatten();
let extra_filename = dep.map(|d| &d.extra_filename[..]);
let path_kind = if dep.is_some() { PathKind::Dependency } else { PathKind::Crate };
let path_kind = match origin {
CrateOrigin::IndirectDependency { .. } => PathKind::Dependency,
CrateOrigin::Injected => PathKind::Crate { is_injected: true },
CrateOrigin::Extern => PathKind::Crate { is_injected: false },
};
let private_dep = origin.private_dep();

let result = if let Some(cnum) = self.existing_match(name, hash) {
Expand Down Expand Up @@ -947,20 +951,27 @@ impl CStore {
}

fn inject_panic_runtime(&mut self, tcx: TyCtxt<'_>, krate: &ast::Crate) {
// If we're only compiling an rlib, then there's no need to select a
// panic runtime, so we just skip this section entirely.
let only_rlib = tcx.crate_types().iter().all(|ct| *ct == CrateType::Rlib);
if only_rlib {
info!("panic runtime injection skipped, only generating rlib");
return;
}
let desired_strategy = tcx.sess.panic_strategy();
let name = match desired_strategy {
PanicStrategy::Unwind => sym::panic_unwind,
PanicStrategy::Abort => sym::panic_abort,
PanicStrategy::ImmediateAbort => {
// Immediate-aborting panics don't use a runtime.
return;
}
};

// If we need a panic runtime, we try to find an existing one here. At
// the same time we perform some general validation of the DAG we've got
// going such as ensuring everything has a compatible panic strategy.
let mut found_panic_runtime = None;
let mut needs_panic_runtime = attr::contains_name(&krate.attrs, sym::needs_panic_runtime);
for (_cnum, data) in self.iter_crate_data() {
for (cnum, data) in self.iter_crate_data() {
needs_panic_runtime |= data.needs_panic_runtime();

if data.is_panic_runtime() && data.name() == name {
found_panic_runtime = Some(cnum)
}
}

// If we just don't need a panic runtime at all, then we're done here
Expand All @@ -969,6 +980,21 @@ impl CStore {
return;
}

// The panic runtime may already be resolved as a `std` dependency via `resolve_crate_deps`.
//
// For `build-std=always`, we avoid injecting it again as a direct dependency, because
// Cargo relies on loading panic runtimes via the `-Ldependency` search paths.
// We know that the panic runtime injected during the `std` build is the correct one
// since Cargo passes the same `-Cpanic=` option to all crates.
//
// For prebuilt `std` it doesn't matter whether the runtime is injected directly or indirectly.
if let Some(found_panic_runtime) = found_panic_runtime {
self.injected_panic_runtime = Some(found_panic_runtime);
return;
}

info!("panic runtime not found -- loading {}", name);

// By this point we know that we need a panic runtime. Here we just load
// an appropriate default runtime for our panic strategy.
//
Expand All @@ -978,17 +1004,7 @@ impl CStore {
// Also note that we have yet to perform validation of the crate graph
// in terms of everyone has a compatible panic runtime format, that's
// performed later as part of the `dependency_format` module.
let desired_strategy = tcx.sess.panic_strategy();
let name = match desired_strategy {
PanicStrategy::Unwind => sym::panic_unwind,
PanicStrategy::Abort => sym::panic_abort,
PanicStrategy::ImmediateAbort => {
// Immediate-aborting panics don't use a runtime.
return;
}
};
info!("panic runtime not found -- loading {}", name);

//
// This has to be conditional as both panic_unwind and panic_abort may be present in the
// crate graph at the same time. One of them will later be activated in dependency_formats.
let Some(cnum) = self.resolve_crate(
Expand All @@ -1002,12 +1018,14 @@ impl CStore {
};
let cdata = self.get_crate_data(cnum);

// Sanity check the loaded crate to ensure it is indeed a panic runtime
// and the panic strategy is indeed what we thought it was.
// Sanity check the loaded crate to ensure it is indeed a panic runtime.
if !cdata.is_panic_runtime() {
tcx.dcx().emit_err(diagnostics::CrateNotPanicRuntime { crate_name: name });
}
if cdata.required_panic_strategy() != Some(desired_strategy) {
// Check the `panic_abort` was compiled with `-Cpanic=abort`.
if desired_strategy == PanicStrategy::Abort
&& cdata.required_panic_strategy() != Some(PanicStrategy::Abort)
{
tcx.dcx().emit_err(diagnostics::NoPanicStrategy {
crate_name: name,
strategy: desired_strategy,
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_session/src/filesearch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ impl FileSearch {
pub fn search_paths<'b>(&'b self, kind: PathKind) -> impl Iterator<Item = &'b SearchPath> {
// If the crate is `PathKind::Crate` (a top level dependency)
// and `-Z implicit-sysroot-deps=false`, then don't include the sysroot in the search paths.
let exclude_sysroot = kind.matches(PathKind::Crate) && !self.use_implicit_sysroot_deps;
let exclude_sysroot =
kind.matches(PathKind::Crate { is_injected: false }) && !self.use_implicit_sysroot_deps;
let maybe_tlib = (!exclude_sysroot).then_some(&self.tlib_path);

self.cli_search_paths
Expand All @@ -45,7 +46,8 @@ impl FileSearch {
suffix: &'b str,
kind: PathKind,
) -> impl Iterator<Item = (&'b str, PathBuf)> {
let exclude_sysroot = kind.matches(PathKind::Crate) && !self.use_implicit_sysroot_deps;
let exclude_sysroot =
kind.matches(PathKind::Crate { is_injected: false }) && !self.use_implicit_sysroot_deps;

// The indices are clipped to have only a single iterator returned from this function, to
// avoid allocating it.
Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_session/src/search_paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ pub struct SearchPath {
#[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, Encodable, Decodable, StableHash)]
pub enum PathKind {
Native,
Crate,
Crate {
// Injected crates might be either direct or indirect dependencies, depending on the context.
// Therefore, disable the `-Lcrate=` search paths for injected crates, as they should be located
// either in the sysroot or provided via `--extern=path/to/crate`.
is_injected: bool,
},
Dependency,
Framework,
All,
Expand All @@ -43,7 +48,7 @@ impl SearchPath {
let (kind, path) = if let Some(stripped) = path.strip_prefix("native=") {
(PathKind::Native, stripped)
} else if let Some(stripped) = path.strip_prefix("crate=") {
(PathKind::Crate, stripped)
(PathKind::Crate { is_injected: false }, stripped)
} else if let Some(stripped) = path.strip_prefix("dependency=") {
(PathKind::Dependency, stripped)
} else if let Some(stripped) = path.strip_prefix("framework=") {
Expand Down
64 changes: 64 additions & 0 deletions tests/run-make-cargo/panic-strategies/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// This test ensures we are able to compile -Zbuild-std=std with multiple panic strategies.
//
//@ needs-target-std

use run_make_support::tempfile::TempDir;
use run_make_support::{cargo, rfs};

fn main() {
// This is a regression test to ensure that rustc doesn't load `panic_abort`
// from the sysroot. See rust-lang/cargo#7359
test("abort");

// The `panic_abort` crate must be compiled with the `-Cpanic=abort` option
// and the compiler has a check to enforce this. However `build-std`
// does not yet respect the `std` profile, it may lead to a mismatch:
// - Cargo profile sets `panic = "unwind"` -> `panic_abort` is not activated (linked).
// - But `panic_abort` is still compiled with `-Cpanic=unwind`.
//
// This test ensures that the check is not triggered in such a situation.
//
// FIXME(build-std): ideally, `panic_abort` should always be compiled with
// `-Cpanic=abort`, even when unused.
test("unwind");

test("immediate-abort");
}

fn test(panic: &'static str) {
let dir = TempDir::new().unwrap();

let manifest = manifest(panic);
rfs::write(dir.path().join("Cargo.toml"), &manifest);
rfs::write(dir.path().join("main.rs"), "fn main() {}");

let mut args = vec!["build", "--release", "-Zbuild-std=std"];
if panic == "immediate-abort" {
args.push("-Zpanic-immediate-abort");
}
cargo()
.current_dir(dir.path())
.args(&args)
.env("RUSTC_BOOTSTRAP", "1")
// Visual Studio 2022 requires that the LIB env var be set so it can
// find the Windows SDK.
.env("LIB", std::env::var("LIB").unwrap_or_default())
.run();
}

fn manifest(panic: &'static str) -> String {
format!(
r#"[package]
name = "foo"
version = "0.1.0"
edition = "2024"

[[bin]]
name = "foo"
path = "main.rs"

[profile.release]
panic = "{panic}"
"#
)
}
21 changes: 21 additions & 0 deletions tests/run-make/locate-panic-runtime/core.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// We are core.
#![feature(lang_items, no_core)]
#![allow(internal_features)]
#![no_std]
#![no_core]
#![crate_type = "rlib"]

#[lang = "panic_info"]
pub struct PanicInfo {}

#[lang = "copy"]
pub trait Copy: Sized {}

#[lang = "pointee_sized"]
pub trait PointeeSized {}

#[lang = "meta_sized"]
pub trait MetaSized: PointeeSized {}

#[lang = "sized"]
pub trait Sized: MetaSized {}
11 changes: 11 additions & 0 deletions tests/run-make/locate-panic-runtime/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![feature(no_core)]
#![no_std]
#![no_core]
#![crate_type = "dylib"]

extern crate std;

#[panic_handler]
fn panic(_: &std::PanicInfo) -> ! {
loop {}
}
9 changes: 9 additions & 0 deletions tests/run-make/locate-panic-runtime/panic_abort.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// We are panic runtime.
#![feature(panic_runtime, no_core)]
#![allow(internal_features)]
#![no_std]
#![no_core]
#![panic_runtime]
#![crate_type = "rlib"]

extern crate core;
66 changes: 66 additions & 0 deletions tests/run-make/locate-panic-runtime/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// This test makes sure that the injected panic runtime can be loaded from
// `-L dependency=` paths as per RFC 3874 (build-std=always).
//
// Note: We have two possible panic runtime crates: the built one and the one from
// the sysroot. Sysroot lookup is disabled via the `--sysroot=` override to verify
// that we can load the correct one.
//
// `--emit=llvm-ir` is used to avoid running the linker.

use run_make_support::{path, rfs, rust_lib_name, rustc};

fn main() {
rfs::create_dir("panic_abort");

// Compile `core`.
rustc().input("core.rs").panic("abort").sysroot("./no_exists").run();

// Compile `panic_abort` into a separate directory to prevent it from being
// found via `-L .`
rustc()
.input("panic_abort.rs")
.panic("abort")
.out_dir("panic_abort")
.sysroot("./no_exists")
.run();

// Compile `std`.
rustc()
.input("std.rs")
.extern_("panic_abort", &path("panic_abort").join(rust_lib_name("panic_abort")))
.panic("abort")
.sysroot("./no_exists")
.run();

// Compile the final artifact. The panic runtime cannot be located without the
// `-Ldependency=` option.
rustc()
.input("lib.rs")
.arg("-Cpanic=abort")
.sysroot("./no_exists")
.emit("llvm-ir")
.run_fail()
.assert_stderr_contains("can't find crate for `panic_abort`");

// Compile the final artifact. The panic runtime cannot be located via
// `-Lcrate=` paths (This means that the panic runtime is not direct
// dependency).
rustc()
.input("lib.rs")
.arg("-Cpanic=abort")
.sysroot("./no_exists")
.library_search_path(format!("crate={}", path("panic_abort").display()))
.emit("llvm-ir")
.run_fail()
.assert_stderr_contains("can't find crate for `panic_abort`");

// Compile the final artifact. The panic runtime can be located via
// `-Ldependency=` paths.
rustc()
.input("lib.rs")
.arg("-Cpanic=abort")
.sysroot("./no_exists")
.library_search_path(format!("dependency={}", path("panic_abort").display()))
.emit("llvm-ir")
.run();
}
11 changes: 11 additions & 0 deletions tests/run-make/locate-panic-runtime/std.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// We are std.
#![feature(needs_panic_runtime, no_core)]
#![allow(internal_features)]
#![no_std]
#![no_core]
// Tell rustc to inject panic runtime.
#![needs_panic_runtime]
#![crate_type = "rlib"]

extern crate core;
pub use core::*;
Loading