]> git.lizzy.rs Git - rust.git/blobdiff - src/bin/miri.rs
avoid using unchecked casts or arithmetic
[rust.git] / src / bin / miri.rs
index acfc429ed805ce36e37ba2017c08e3c44f3c019f..4a54867c99640c4c5f75f690965405a19c63ae4a 100644 (file)
 #![feature(rustc_private)]
 
+extern crate env_logger;
 extern crate getopts;
+#[macro_use]
+extern crate log;
+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 env_logger;
-extern crate log_settings;
-extern crate syntax;
+extern crate rustc_hir;
+extern crate rustc_interface;
+extern crate rustc_session;
 
-#[macro_use]
-extern crate log;
-
-use std::path::PathBuf;
-use std::str::FromStr;
+use std::convert::TryFrom;
 use std::env;
+use std::str::FromStr;
 
-use rustc::session::Session;
-use rustc_metadata::cstore::CStore;
-use rustc_driver::{Compilation, CompilerCalls, RustcDefaultCalls};
-use rustc_driver::driver::{CompileState, CompileController};
-use rustc::session::config::{self, Input, ErrorOutputType};
-use rustc_codegen_utils::codegen_backend::CodegenBackend;
-use rustc::hir::def_id::LOCAL_CRATE;
-use syntax::ast;
+use hex::FromHexError;
 
-struct MiriCompilerCalls {
-    default: Box<RustcDefaultCalls>,
+use rustc_session::CtfeBacktrace;
+use rustc_driver::Compilation;
+use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_interface::{interface, Queries};
+use rustc::ty::TyCtxt;
 
-    /// Whether to enforce the validity invariant.
-    validate: bool,
+struct MiriCompilerCalls {
+    miri_config: miri::MiriConfig,
 }
 
-impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
-    fn early_callback(
+impl rustc_driver::Callbacks for MiriCompilerCalls {
+    fn after_analysis<'tcx>(
         &mut self,
-        matches: &getopts::Matches,
-        sopts: &config::Options,
-        cfg: &ast::CrateConfig,
-        descriptions: &rustc_errors::registry::Registry,
-        output: ErrorOutputType,
+        compiler: &interface::Compiler,
+        queries: &'tcx Queries<'tcx>,
     ) -> Compilation {
-        self.default.early_callback(
-            matches,
-            sopts,
-            cfg,
-            descriptions,
-            output,
-        )
-    }
-    fn no_input(
-        &mut self,
-        matches: &getopts::Matches,
-        sopts: &config::Options,
-        cfg: &ast::CrateConfig,
-        odir: &Option<PathBuf>,
-        ofile: &Option<PathBuf>,
-        descriptions: &rustc_errors::registry::Registry,
-    ) -> Option<(Input, Option<PathBuf>)> {
-        self.default.no_input(
-            matches,
-            sopts,
-            cfg,
-            odir,
-            ofile,
-            descriptions,
-        )
-    }
-    fn late_callback(
-        &mut self,
-        codegen_backend: &CodegenBackend,
-        matches: &getopts::Matches,
-        sess: &Session,
-        cstore: &CStore,
-        input: &Input,
-        odir: &Option<PathBuf>,
-        ofile: &Option<PathBuf>,
-    ) -> Compilation {
-        self.default.late_callback(codegen_backend, matches, sess, cstore, input, odir, ofile)
-    }
-    fn build_controller(
-        self: Box<Self>,
-        sess: &Session,
-        matches: &getopts::Matches,
-    ) -> CompileController<'a> {
-        let this = *self;
-        let mut control = this.default.build_controller(sess, matches);
-        control.after_hir_lowering.callback = Box::new(after_hir_lowering);
-        let validate = this.validate;
-        control.after_analysis.callback =
-            Box::new(move |state| after_analysis(state, validate));
-        control.after_analysis.stop = Compilation::Stop;
-        control
-    }
-}
-
-fn after_hir_lowering(state: &mut CompileState) {
-    let attr = (
-        String::from("miri"),
-        syntax::feature_gate::AttributeType::Whitelisted,
-    );
-    state.session.plugin_attributes.borrow_mut().push(attr);
-}
-
-fn after_analysis<'a, 'tcx>(
-    state: &mut CompileState<'a, 'tcx>,
-    validate: bool,
-) {
-    init_late_loggers();
-    state.session.abort_if_errors();
+        compiler.session().abort_if_errors();
 
-    let tcx = state.tcx.unwrap();
+        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());
 
-    let (entry_def_id, _) = tcx.entry_fn(LOCAL_CRATE).expect("no main function found!");
+            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!"),
+                );
+            }
+        });
 
-    miri::eval_main(tcx, entry_def_id, validate);
+        compiler.session().abort_if_errors();
 
-    state.session.abort_if_errors();
+        Compilation::Stop
+    }
 }
 
 fn init_early_loggers() {
-    // Notice that our `extern crate log` is NOT the same as rustc's!  So we have to initialize
-    // them both.  We always initialize miri early.
+    // Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to
+    // initialize them both, and we always initialize `miri`'s first.
     let env = env_logger::Env::new().filter("MIRI_LOG").write_style("MIRI_LOG_STYLE");
     env_logger::init_from_env(env);
-    // We only initialize rustc if the env var is set (so the user asked for it).
+    // We only initialize `rustc` if the env var is set (so the user asked for it).
     // 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() {
+    // later with our custom settings, and *not* log anything for what happens before
+    // `miri` gets started.
+    if env::var("RUSTC_LOG").is_ok() {
         rustc_driver::init_rustc_env_logger();
     }
 }
 
-fn init_late_loggers() {
-    // Initializing loggers right before we start evaluation.  We overwrite the RUST_LOG
-    // env var if it is not set, control it based on MIRI_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() {
-            // We try to be a bit clever here: If MIRI_LOG is just a single level
+        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;
     }
-
-    // Taken from https://github.com/Manishearth/rust-clippy/pull/911.
+    // 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() {
     init_early_loggers();
-    let mut args: Vec<String> = std::env::args().collect();
 
-    // Parse our own -Z flags and remove them before rustc gets their hand on them.
+    // Parse our arguments and split them across `rustc` and `miri`.
     let mut validate = true;
-    args.retain(|arg| {
-        match arg.as_str() {
-            "-Zmiri-disable-validation" => {
-                validate = false;
-                false
-            },
-            _ => 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 {
+            // 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;
+                }
+                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 {
+                        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 !args.contains(&sysroot_flag) {
-        args.push(sysroot_flag);
-        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.
-    args.splice(1..1, miri::miri_default_args().iter().map(ToString::to_string));
-
-    trace!("rustc arguments: {:?}", args);
-    let result = rustc_driver::run(move || {
-        rustc_driver::run_compiler(&args, Box::new(MiriCompilerCalls {
-            default: Box::new(RustcDefaultCalls),
-            validate,
-        }), None, None)
+    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,
+        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)
     });
-    std::process::exit(result as i32);
+    std::process::exit(result.is_err() as i32);
 }