Skip to content

Commit 84a4514

Browse files
committed
Auto merge of #1549 - RalfJung:panic-abort, r=oli-obk
support panic=abort This adds support for abort-on-panic (#1058). To achieve this, we insert `cargo-miri` as `RUSTC` when building the standard library, and patch the rustc flags in a way similar to what bootstrap does. However, this is currently not supported on Windows as the Windows code uses inline assembly to cause an abort (?!?). I'll submit a rustc PR with some `cffg(miri)` to make that work. (EDIT: that would be rust-lang/rust#76871) Cc `@Aaron1011` r? `@oli-obk`
2 parents ce3d1a6 + 97a71c0 commit 84a4514

File tree

8 files changed

+60
-29
lines changed

8 files changed

+60
-29
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ different Miri binaries, and as such worth documenting:
244244
* `MIRI_BE_RUSTC` when set to any value tells the Miri driver to actually not
245245
interpret the code but compile it like rustc would. This is useful to be sure
246246
that the compiled `rlib`s are compatible with Miri.
247+
When set while running `cargo-miri`, it indicates that we are part of a sysroot
248+
build (for which some crates need special treatment).
247249
* `MIRI_CWD` when set to any value tells the Miri driver to change to the given
248250
directory after loading all the source files, but before commencing
249251
interpretation. This is useful if the interpreted program wants a different

cargo-miri/bin.rs

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,7 @@ fn setup(subcommand: MiriCommand) {
295295
br#"
296296
[dependencies.std]
297297
default_features = false
298-
# We need the `panic_unwind` feature because we use the `unwind` panic strategy.
299-
# Using `abort` works for libstd, but then libtest will not compile.
298+
# We support unwinding, so enable that panic runtime.
300299
features = ["panic_unwind"]
301300
302301
[dependencies.test]
@@ -338,10 +337,14 @@ path = "lib.rs"
338337
// because we still need bootstrap to distinguish between host and target crates.
339338
// In that case we overwrite `RUSTC_REAL` instead which determines the rustc used
340339
// for target crates.
340+
// We set ourselves (`cargo-miri`) instead of Miri directly to be able to patch the flags
341+
// for `libpanic_abort` (usually this is done by bootstrap but we have to do it ourselves).
342+
// The `MIRI_BE_RUSTC` will mean we dispatch to `phase_setup_rustc`.
343+
let cargo_miri_path = std::env::current_exe().expect("current executable path invalid");
341344
if env::var_os("RUSTC_STAGE").is_some() {
342-
command.env("RUSTC_REAL", find_miri());
345+
command.env("RUSTC_REAL", &cargo_miri_path);
343346
} else {
344-
command.env("RUSTC", find_miri());
347+
command.env("RUSTC", &cargo_miri_path);
345348
}
346349
command.env("MIRI_BE_RUSTC", "1");
347350
// Make sure there are no other wrappers or flags getting in our way
@@ -370,6 +373,21 @@ path = "lib.rs"
370373
}
371374
}
372375

376+
fn phase_setup_rustc(args: env::Args) {
377+
// Mostly we just forward everything.
378+
// `MIRI_BE_RUST` is already set.
379+
let mut cmd = miri();
380+
cmd.args(args);
381+
382+
// Patch the panic runtime for `libpanic_abort` (mirroring what bootstrap usually does).
383+
if get_arg_flag_value("--crate-name").as_deref() == Some("panic_abort") {
384+
cmd.arg("-C").arg("panic=abort");
385+
}
386+
387+
// Run it!
388+
exec(cmd);
389+
}
390+
373391
fn phase_cargo_miri(mut args: env::Args) {
374392
// Check for version and help flags even when invoked as `cargo-miri`.
375393
if has_arg_flag("--help") || has_arg_flag("-h") {
@@ -402,7 +420,7 @@ fn phase_cargo_miri(mut args: env::Args) {
402420
// <https://github.com/rust-lang/miri/pull/1540#issuecomment-693553191> describes an alternative
403421
// approach that uses `cargo check`, making that part easier but target and binary handling
404422
// harder.
405-
let miri_path = std::env::current_exe().expect("current executable path invalid");
423+
let cargo_miri_path = std::env::current_exe().expect("current executable path invalid");
406424
let cargo_cmd = match subcommand {
407425
MiriCommand::Test => "test",
408426
MiriCommand::Run => "run",
@@ -470,22 +488,22 @@ fn phase_cargo_miri(mut args: env::Args) {
470488
if env::var_os("RUSTC_WRAPPER").is_some() {
471489
println!("WARNING: Ignoring `RUSTC_WRAPPER` environment variable, Miri does not support wrapping.");
472490
}
473-
cmd.env("RUSTC_WRAPPER", &miri_path);
474-
if verbose {
475-
eprintln!("+ RUSTC_WRAPPER={:?}", miri_path);
476-
}
491+
cmd.env("RUSTC_WRAPPER", &cargo_miri_path);
477492

478493
// Set the runner for the current target to us as well, so we can interpret the binaries.
479494
let runner_env_name = format!("CARGO_TARGET_{}_RUNNER", target.to_uppercase().replace('-', "_"));
480-
cmd.env(runner_env_name, &miri_path);
495+
cmd.env(&runner_env_name, &cargo_miri_path);
481496

482497
// Set rustdoc to us as well, so we can make it do nothing (see issue #584).
483-
cmd.env("RUSTDOC", &miri_path);
498+
cmd.env("RUSTDOC", &cargo_miri_path);
484499

485500
// Run cargo.
486501
if verbose {
487-
cmd.env("MIRI_VERBOSE", ""); // This makes the other phases verbose.
502+
eprintln!("[cargo-miri miri] RUSTC_WRAPPER={:?}", cargo_miri_path);
503+
eprintln!("[cargo-miri miri] {}={:?}", runner_env_name, cargo_miri_path);
504+
eprintln!("[cargo-miri miri] RUSTDOC={:?}", cargo_miri_path);
488505
eprintln!("[cargo-miri miri] {:?}", cmd);
506+
cmd.env("MIRI_VERBOSE", ""); // This makes the other phases verbose.
489507
}
490508
exec(cmd)
491509
}
@@ -699,6 +717,12 @@ fn main() {
699717
// Skip binary name.
700718
args.next().unwrap();
701719

720+
// Dispatch running as part of sysroot compilation.
721+
if env::var_os("MIRI_BE_RUSTC").is_some() {
722+
phase_setup_rustc(args);
723+
return;
724+
}
725+
702726
// Dispatch to `cargo-miri` phase. There are three phases:
703727
// - When we are called via `cargo miri`, we run as the frontend and invoke the underlying
704728
// cargo. We set RUSTC_WRAPPER and CARGO_TARGET_RUNNER to ourselves.

src/shims/foreign_items.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use log::trace;
44

55
use rustc_hir::def_id::DefId;
66
use rustc_middle::{mir, ty};
7-
use rustc_target::abi::{Align, Size};
7+
use rustc_target::{abi::{Align, Size}, spec::PanicStrategy};
88
use rustc_apfloat::Float;
99
use rustc_span::symbol::sym;
1010

@@ -146,6 +146,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
146146
let code = this.read_scalar(code)?.to_i32()?;
147147
throw_machine_stop!(TerminationInfo::Exit(code.into()));
148148
}
149+
"abort" => {
150+
throw_machine_stop!(TerminationInfo::Abort(None))
151+
}
149152
_ => throw_unsup_format!("can't call (diverging) foreign function: {}", link_name),
150153
},
151154
Some(p) => p,
@@ -159,14 +162,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
159162
// We forward this to the underlying *implementation* in the panic runtime crate.
160163
// Normally, this will be either `libpanic_unwind` or `libpanic_abort`, but it could
161164
// also be a custom user-provided implementation via `#![feature(panic_runtime)]`
162-
"__rust_start_panic" | "__rust_panic_cleanup"=> {
163-
// FIXME we might want to cache this... but it's not really performance-critical.
164-
let panic_runtime = tcx
165-
.crates()
166-
.iter()
167-
.find(|cnum| tcx.is_panic_runtime(**cnum))
168-
.expect("No panic runtime found!");
169-
let panic_runtime = tcx.crate_name(*panic_runtime);
165+
"__rust_start_panic" | "__rust_panic_cleanup" => {
166+
// This replicates some of the logic in `inject_panic_runtime`.
167+
// FIXME: is there a way to reuse that logic?
168+
let panic_runtime = match this.tcx.sess.panic_strategy() {
169+
PanicStrategy::Unwind => sym::panic_unwind,
170+
PanicStrategy::Abort => sym::panic_abort,
171+
};
170172
let start_panic_instance =
171173
this.resolve_path(&[&*panic_runtime.as_str(), link_name]);
172174
return Ok(Some(&*this.load_mir(start_panic_instance.def, None)?));

src/shims/panic.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4444
let this = self.eval_context_mut();
4545

4646
trace!("miri_start_panic: {:?}", this.frame().instance);
47+
// Make sure we only start unwinding when this matches our panic strategy.
48+
assert_eq!(this.tcx.sess.panic_strategy(), PanicStrategy::Unwind);
4749

4850
// Get the raw pointer stored in arg[0] (the panic payload).
4951
let &[payload] = check_arg_count(args)?;
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
// ignore-test: Abort panics are not yet supported
2-
// error-pattern: the evaluated program panicked
1+
// error-pattern: the evaluated program aborted execution
32
// compile-flags: -C panic=abort
3+
// ignore-windows: windows panics via inline assembly (FIXME)
4+
45
fn main() {
56
std::panic!("panicking from libstd");
67
}

tests/compile-fail/panic/panic_abort2.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// ignore-test: Abort panics are not yet supported
2-
// error-pattern: the evaluated program panicked
1+
// error-pattern: the evaluated program aborted execution
32
// compile-flags: -C panic=abort
3+
// ignore-windows: windows panics via inline assembly (FIXME)
44

55
fn main() {
66
std::panic!("{}-panicking from libstd", 42);

tests/compile-fail/panic/panic_abort3.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// ignore-test: Abort panics are not yet supported
2-
//error-pattern: the evaluated program panicked
1+
// error-pattern: the evaluated program aborted execution
32
// compile-flags: -C panic=abort
3+
// ignore-windows: windows panics via inline assembly (FIXME)
44

55
fn main() {
66
core::panic!("panicking from libcore");

tests/compile-fail/panic/panic_abort4.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// ignore-test: Abort panics are not yet supported
2-
//error-pattern: the evaluated program panicked
1+
// error-pattern: the evaluated program aborted execution
32
// compile-flags: -C panic=abort
3+
// ignore-windows: windows panics via inline assembly (FIXME)
44

55
fn main() {
66
core::panic!("{}-panicking from libcore", 42);

0 commit comments

Comments
 (0)