]> git.lizzy.rs Git - rust.git/blobdiff - src/bin/miri.rs
avoid using unchecked casts or arithmetic
[rust.git] / src / bin / miri.rs
index 7f15d00e2c8abd5ab71b2080c5432d6e7665dc41..4a54867c99640c4c5f75f690965405a19c63ae4a 100644 (file)
@@ -7,53 +7,53 @@
 extern crate log_settings;
 extern crate miri;
 extern crate rustc;
-extern crate rustc_metadata;
 extern crate rustc_driver;
-extern crate rustc_errors;
-extern crate rustc_codegen_utils;
+extern crate rustc_hir;
 extern crate rustc_interface;
-extern crate syntax;
+extern crate rustc_session;
 
-use std::str::FromStr;
+use std::convert::TryFrom;
 use std::env;
+use std::str::FromStr;
 
-use rustc_interface::interface;
-use rustc::hir::def_id::LOCAL_CRATE;
+use hex::FromHexError;
+
+use rustc_session::CtfeBacktrace;
+use rustc_driver::Compilation;
+use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_interface::{interface, Queries};
+use rustc::ty::TyCtxt;
 
 struct MiriCompilerCalls {
     miri_config: miri::MiriConfig,
 }
 
 impl rustc_driver::Callbacks for MiriCompilerCalls {
-    fn after_parsing(&mut self, compiler: &interface::Compiler) -> bool {
-        let attr = (
-            String::from("miri"),
-            syntax::feature_gate::AttributeType::Whitelisted,
-        );
-        compiler.session().plugin_attributes.borrow_mut().push(attr);
-
-        // Continue execution
-        true
-    }
-
-    fn after_analysis(&mut self, compiler: &interface::Compiler) -> bool {
-        init_late_loggers();
+    fn after_analysis<'tcx>(
+        &mut self,
+        compiler: &interface::Compiler,
+        queries: &'tcx Queries<'tcx>,
+    ) -> Compilation {
         compiler.session().abort_if_errors();
 
-        compiler.global_ctxt().unwrap().peek_mut().enter(|tcx| {
+        queries.global_ctxt().unwrap().peek_mut().enter(|tcx| {
+            init_late_loggers(tcx);
             let (entry_def_id, _) = tcx.entry_fn(LOCAL_CRATE).expect("no main function found!");
             let mut config = self.miri_config.clone();
 
             // 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();
 
-        // Don't continue execution
-        false
+        Compilation::Stop
     }
 }
 
@@ -66,59 +66,65 @@ fn init_early_loggers() {
     // If it is not set, we avoid initializing now so that we can initialize
     // later with our custom settings, and *not* log anything for what happens before
     // `miri` gets started.
-    if env::var("RUST_LOG").is_ok() {
+    if env::var("RUSTC_LOG").is_ok() {
         rustc_driver::init_rustc_env_logger();
     }
 }
 
-fn init_late_loggers() {
-    // We initialize loggers right before we start evaluation. We overwrite the `RUST_LOG`
+fn init_late_loggers(tcx: TyCtxt<'_>) {
+    // We initialize loggers right before we start evaluation. We overwrite the `RUSTC_LOG`
     // env var if it is not set, control it based on `MIRI_LOG`.
     if let Ok(var) = env::var("MIRI_LOG") {
-        if env::var("RUST_LOG").is_err() {
+        if env::var("RUSTC_LOG").is_err() {
             // We try to be a bit clever here: if `MIRI_LOG` is just a single level
             // used for everything, we only apply it to the parts of rustc that are
-            // CTFE-related. Otherwise, we use it verbatim for `RUST_LOG`.
+            // CTFE-related. Otherwise, we use it verbatim for `RUSTC_LOG`.
             // This way, if you set `MIRI_LOG=trace`, you get only the right parts of
             // rustc traced, but you can also do `MIRI_LOG=miri=trace,rustc_mir::interpret=debug`.
             if log::Level::from_str(&var).is_ok() {
-                env::set_var("RUST_LOG",
-                    &format!("rustc::mir::interpret={0},rustc_mir::interpret={0}", var));
+                env::set_var(
+                    "RUSTC_LOG",
+                    &format!("rustc::mir::interpret={0},rustc_mir::interpret={0}", var),
+                );
             } else {
-                env::set_var("RUST_LOG", &var);
+                env::set_var("RUSTC_LOG", &var);
             }
             rustc_driver::init_rustc_env_logger();
         }
     }
 
-    // If `MIRI_BACKTRACE` is set and `RUST_CTFE_BACKTRACE` is not, set `RUST_CTFE_BACKTRACE`.
-    // Do this late, so we really only apply this to miri's errors.
-    if let Ok(var) = env::var("MIRI_BACKTRACE") {
-        if env::var("RUST_CTFE_BACKTRACE") == Err(env::VarError::NotPresent) {
-            env::set_var("RUST_CTFE_BACKTRACE", &var);
-        }
+    // If `MIRI_BACKTRACE` is set and `RUSTC_CTFE_BACKTRACE` is not, set `RUSTC_CTFE_BACKTRACE`.
+    // Do this late, so we ideally only apply this to Miri's errors.
+    if let Ok(val) = env::var("MIRI_BACKTRACE") {
+        let ctfe_backtrace = match &*val {
+            "immediate" => CtfeBacktrace::Immediate,
+            "0" => CtfeBacktrace::Disabled,
+            _ => CtfeBacktrace::Capture,
+        };
+        *tcx.sess.ctfe_backtrace.borrow_mut() = ctfe_backtrace;
     }
 }
 
-fn find_sysroot() -> String {
-    if let Ok(sysroot) = std::env::var("MIRI_SYSROOT") {
-        return sysroot;
+/// Returns the "default sysroot" that Miri will use if no `--sysroot` flag is set.
+/// Should be a compile-time constant.
+fn compile_time_sysroot() -> Option<String> {
+    if option_env!("RUSTC_STAGE").is_some() {
+        // This is being built as part of rustc, and gets shipped with rustup.
+        // We can rely on the sysroot computation in librustc.
+        return None;
     }
-
+    // For builds outside rustc, we need to ensure that we got a sysroot
+    // that gets used as a default.  The sysroot computation in librustc would
+    // end up somewhere in the build dir.
     // Taken from PR <https://github.com/Manishearth/rust-clippy/pull/911>.
     let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
     let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
-    match (home, toolchain) {
+    Some(match (home, toolchain) {
         (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
-        _ => {
-            option_env!("RUST_SYSROOT")
-                .expect(
-                    "could not find sysroot. Either set `MIRI_SYSROOT` at run-time, or at \
-                     build-time specify `RUST_SYSROOT` env var or use rustup or multirust",
-                )
-                .to_owned()
-        }
-    }
+        _ => option_env!("RUST_SYSROOT")
+            .expect("To build Miri without rustup, set the `RUST_SYSROOT` env var at build time")
+            .to_owned(),
+    })
 }
 
 fn main() {
@@ -126,62 +132,133 @@ fn main() {
 
     // Parse our arguments and split them across `rustc` and `miri`.
     let mut validate = true;
+    let mut stacked_borrows = true;
+    let mut communicate = false;
+    let mut ignore_leaks = false;
     let mut seed: Option<u64> = None;
+    let mut tracked_pointer_tag: Option<miri::PtrId> = None;
+    let mut tracked_alloc_id: Option<miri::AllocId> = None;
     let mut rustc_args = vec![];
     let mut miri_args = vec![];
     let mut after_dashdash = false;
+    let mut excluded_env_vars = vec![];
     for arg in std::env::args() {
         if rustc_args.is_empty() {
             // Very first arg: for `rustc`.
             rustc_args.push(arg);
-        }
-        else if after_dashdash {
+        } else if after_dashdash {
             // Everything that comes after are `miri` args.
             miri_args.push(arg);
         } else {
             match arg.as_str() {
                 "-Zmiri-disable-validation" => {
                     validate = false;
-                },
+                }
+                "-Zmiri-disable-stacked-borrows" => {
+                    stacked_borrows = false;
+                }
+                "-Zmiri-disable-isolation" => {
+                    communicate = true;
+                }
+                "-Zmiri-ignore-leaks" => {
+                    ignore_leaks = true;
+                }
                 "--" => {
                     after_dashdash = true;
                 }
-                _ => {
-                    let split: Vec<String> = arg.split("-Zmiri-seed=").map(|s| s.to_owned()).collect();
-                    if split.len() == 2 {
-                        if seed.is_some() {
-                            panic!("Cannot specify -Zmiri-seed multiple times!");
-                        }
-                        let seed_raw = hex::decode(&split[1]).unwrap();
-                        if seed_raw.len() > 8 {
-                            panic!(format!("-Zmiri-seed must be at most 8 bytes, was {}", seed_raw.len()));
-                        }
-
-                        let mut bytes = [0; 8];
-                        bytes[..seed_raw.len()].copy_from_slice(&hex::decode(&split[1]).unwrap());
-                        seed = Some(u64::from_be_bytes(bytes));
+                arg if arg.starts_with("-Zmiri-seed=") => {
+                    if seed.is_some() {
+                        panic!("Cannot specify -Zmiri-seed multiple times!");
+                    }
+                    let seed_raw = hex::decode(arg.trim_start_matches("-Zmiri-seed="))
+                        .unwrap_or_else(|err| match err {
+                            FromHexError::InvalidHexCharacter { .. } => panic!(
+                                "-Zmiri-seed should only contain valid hex digits [0-9a-fA-F]"
+                            ),
+                            FromHexError::OddLength =>
+                                panic!("-Zmiri-seed should have an even number of digits"),
+                            err => panic!("Unknown error decoding -Zmiri-seed as hex: {:?}", err),
+                        });
+                    if seed_raw.len() > 8 {
+                        panic!(format!(
+                            "-Zmiri-seed must be at most 8 bytes, was {}",
+                            seed_raw.len()
+                        ));
+                    }
+
+                    let mut bytes = [0; 8];
+                    bytes[..seed_raw.len()].copy_from_slice(&seed_raw);
+                    seed = Some(u64::from_be_bytes(bytes));
+                }
+                arg if arg.starts_with("-Zmiri-env-exclude=") => {
+                    excluded_env_vars
+                        .push(arg.trim_start_matches("-Zmiri-env-exclude=").to_owned());
+                }
+                arg if arg.starts_with("-Zmiri-track-pointer-tag=") => {
+                    let id: u64 = match arg.trim_start_matches("-Zmiri-track-pointer-tag=").parse()
+                    {
+                        Ok(id) => id,
+                        Err(err) => panic!(
+                            "-Zmiri-track-pointer-tag requires a valid `u64` as the argument: {}",
+                            err
+                        ),
+                    };
+                    if let Some(id) = miri::PtrId::new(id) {
+                        tracked_pointer_tag = Some(id);
                     } else {
-                        rustc_args.push(arg);
+                        panic!("-Zmiri-track-pointer-tag must be a nonzero id");
                     }
                 }
+                arg if arg.starts_with("-Zmiri-track-alloc-id=") => {
+                    let id: u64 = match arg.trim_start_matches("-Zmiri-track-alloc-id=").parse()
+                    {
+                        Ok(id) => id,
+                        Err(err) => panic!(
+                            "-Zmiri-track-alloc-id requires a valid `u64` as the argument: {}",
+                            err
+                        ),
+                    };
+                    tracked_alloc_id = Some(miri::AllocId(id));
+                }
+                _ => {
+                    rustc_args.push(arg);
+                }
             }
         }
     }
 
-    // Determine sysroot and let rustc know about it.
-    let sysroot_flag = String::from("--sysroot");
-    if !rustc_args.contains(&sysroot_flag) {
-        rustc_args.push(sysroot_flag);
-        rustc_args.push(find_sysroot());
+    // Determine sysroot if needed.  Make sure we always call `compile_time_sysroot`
+    // as that also does some sanity-checks of the environment we were built in.
+    // FIXME: Ideally we'd turn a bad build env into a compile-time error, but
+    // CTFE does not seem powerful enough for that yet.
+    if let Some(sysroot) = compile_time_sysroot() {
+        let sysroot_flag = "--sysroot";
+        if !rustc_args.iter().any(|e| e == sysroot_flag) {
+            // We need to overwrite the default that librustc would compute.
+            rustc_args.push(sysroot_flag.to_owned());
+            rustc_args.push(sysroot);
+        }
     }
+
     // Finally, add the default flags all the way in the beginning, but after the binary name.
     rustc_args.splice(1..1, miri::miri_default_args().iter().map(ToString::to_string));
 
     debug!("rustc arguments: {:?}", rustc_args);
     debug!("miri arguments: {:?}", miri_args);
-    let miri_config = miri::MiriConfig { validate, args: miri_args, seed };
-    let result = rustc_driver::report_ices_to_stderr_if_any(move || {
+    let miri_config = miri::MiriConfig {
+        validate,
+        stacked_borrows,
+        communicate,
+        ignore_leaks,
+        excluded_env_vars,
+        seed,
+        args: miri_args,
+        tracked_pointer_tag,
+        tracked_alloc_id,
+    };
+    rustc_driver::install_ice_hook();
+    let result = rustc_driver::catch_fatal_errors(move || {
         rustc_driver::run_compiler(&rustc_args, &mut MiriCompilerCalls { miri_config }, None, None)
-    }).and_then(|result| result);
+    });
     std::process::exit(result.is_err() as i32);
 }