diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 481491ea72..9e95e4b0a4 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -15,6 +15,7 @@ extern crate rustc_interface; extern crate syntax; use std::str::FromStr; +use std::convert::TryFrom; use std::env; use hex::FromHexError; @@ -39,7 +40,9 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { // Add filename to `miri` arguments. config.args.insert(0, compiler.input().filestem().to_string()); - miri::eval_main(tcx, entry_def_id, config); + if let Some(return_code) = miri::eval_main(tcx, entry_def_id, config) { + std::process::exit(i32::try_from(return_code).expect("Return value was too large!")); + } }); compiler.session().abort_if_errors(); diff --git a/src/eval.rs b/src/eval.rs index 7203ba6bf1..237ef99d1f 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -11,7 +11,7 @@ use syntax::source_map::DUMMY_SP; use crate::{ EnvVars, Evaluator, FnVal, HelpersEvalContextExt, InterpCx, InterpError, InterpResult, MemoryExtra, MiriMemoryKind, Pointer, Scalar, StackPopCleanup, Tag, - TlsEvalContextExt, + TlsEvalContextExt, MPlaceTy }; /// Configuration needed to spawn a Miri instance. @@ -29,12 +29,15 @@ pub struct MiriConfig { pub seed: Option, } -// Used by priroda. +/// Returns a freshly created `InterpCx`, along with an `MPlaceTy` representing +/// the location where the return value of the `start` lang item will be +/// written to. +/// Public because this is also used by `priroda`. pub fn create_ecx<'mir, 'tcx: 'mir>( tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig, -) -> InterpResult<'tcx, InterpCx<'mir, 'tcx, Evaluator<'tcx>>> { +) -> InterpResult<'tcx, (InterpCx<'mir, 'tcx, Evaluator<'tcx>>, MPlaceTy<'tcx, Tag>)> { let mut ecx = InterpCx::new( tcx.at(syntax::source_map::DUMMY_SP), ty::ParamEnv::reveal_all(), @@ -170,12 +173,15 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( ecx.write_scalar(Scalar::from_u32(0), errno_place.into())?; ecx.machine.last_error = Some(errno_place); - Ok(ecx) + Ok((ecx, ret_ptr)) } -pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) { - let mut ecx = match create_ecx(tcx, main_id, config) { - Ok(ecx) => ecx, +/// Evaluates the main function specified by `main_id`. +/// Returns `Some(return_code)` if program executed completed. +/// Returns `None` if an evaluation error occured. +pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option { + let (mut ecx, ret_ptr) = match create_ecx(tcx, main_id, config) { + Ok(v) => v, Err(mut err) => { err.print_backtrace(); panic!("Miri initialziation error: {}", err.kind) @@ -183,14 +189,18 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) { }; // Perform the main execution. - let res: InterpResult<'_> = (|| { + let res: InterpResult<'_, i64> = (|| { ecx.run()?; - ecx.run_tls_dtors() + // Read the return code pointer *before* we run TLS destructors, to assert + // that it was written to by the time that `start` lang item returned. + let return_code = ecx.read_scalar(ret_ptr.into())?.not_undef()?.to_machine_isize(&ecx)?; + ecx.run_tls_dtors()?; + Ok(return_code) })(); // Process the result. match res { - Ok(()) => { + Ok(return_code) => { let leaks = ecx.memory.leak_report(); // Disable the leak test on some platforms where we do not // correctly implement TLS destructors. @@ -198,12 +208,16 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) { let ignore_leaks = target_os == "windows" || target_os == "macos"; if !ignore_leaks && leaks != 0 { tcx.sess.err("the evaluated program leaked memory"); + // Ignore the provided return code - let the reported error + // determine the return code. + return None; } + return Some(return_code) } Err(mut e) => { // Special treatment for some error kinds let msg = match e.kind { - InterpError::Exit(code) => std::process::exit(code), + InterpError::Exit(code) => return Some(code.into()), err_unsup!(NoMirFor(..)) => format!("{}. Did you set `MIRI_SYSROOT` to a Miri-enabled sysroot? You can prepare one with `cargo miri setup`.", e), _ => e.to_string() @@ -246,6 +260,8 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) { trace!(" local {}: {:?}", i, local.value); } } + // Let the reported error determine the return code. + return None; } } }