1 #![feature(rustc_private)]
3 extern crate rustc_driver;
4 extern crate rustc_errors;
5 extern crate rustc_hir;
6 extern crate rustc_interface;
7 extern crate rustc_middle;
8 extern crate rustc_session;
10 use std::convert::TryFrom;
12 use std::str::FromStr;
14 use hex::FromHexError;
17 use rustc_driver::Compilation;
18 use rustc_errors::emitter::{ColorConfig, HumanReadableErrorType};
19 use rustc_middle::ty::TyCtxt;
20 use rustc_session::{config::ErrorOutputType, CtfeBacktrace};
22 struct MiriCompilerCalls {
23 miri_config: miri::MiriConfig,
26 impl rustc_driver::Callbacks for MiriCompilerCalls {
27 fn after_analysis<'tcx>(
29 compiler: &rustc_interface::interface::Compiler,
30 queries: &'tcx rustc_interface::Queries<'tcx>,
32 compiler.session().abort_if_errors();
34 queries.global_ctxt().unwrap().peek_mut().enter(|tcx| {
35 init_late_loggers(tcx);
36 let (entry_def_id, _) = if let Some((entry_def, x)) = tcx.entry_fn(()) {
39 let output_ty = ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(
42 rustc_session::early_error(
44 "miri can only run programs that have a main function",
47 let mut config = self.miri_config.clone();
49 // Add filename to `miri` arguments.
50 config.args.insert(0, compiler.input().filestem().to_string());
52 // Adjust working directory for interpretation.
53 if let Some(cwd) = env::var_os("MIRI_CWD") {
54 env::set_current_dir(cwd).unwrap();
57 if let Some(return_code) = miri::eval_main(tcx, entry_def_id, config) {
59 i32::try_from(return_code).expect("Return value was too large!"),
64 compiler.session().abort_if_errors();
70 fn init_early_loggers() {
71 // Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to
72 // initialize them both, and we always initialize `miri`'s first.
73 let env = env_logger::Env::new().filter("MIRI_LOG").write_style("MIRI_LOG_STYLE");
74 env_logger::init_from_env(env);
75 // We only initialize `rustc` if the env var is set (so the user asked for it).
76 // If it is not set, we avoid initializing now so that we can initialize
77 // later with our custom settings, and *not* log anything for what happens before
78 // `miri` gets started.
79 if env::var_os("RUSTC_LOG").is_some() {
80 rustc_driver::init_rustc_env_logger();
84 fn init_late_loggers(tcx: TyCtxt<'_>) {
85 // We initialize loggers right before we start evaluation. We overwrite the `RUSTC_LOG`
86 // env var if it is not set, control it based on `MIRI_LOG`.
87 // (FIXME: use `var_os`, but then we need to manually concatenate instead of `format!`.)
88 if let Ok(var) = env::var("MIRI_LOG") {
89 if env::var_os("RUSTC_LOG").is_none() {
90 // We try to be a bit clever here: if `MIRI_LOG` is just a single level
91 // used for everything, we only apply it to the parts of rustc that are
92 // CTFE-related. Otherwise, we use it verbatim for `RUSTC_LOG`.
93 // This way, if you set `MIRI_LOG=trace`, you get only the right parts of
94 // rustc traced, but you can also do `MIRI_LOG=miri=trace,rustc_mir::interpret=debug`.
95 if log::Level::from_str(&var).is_ok() {
98 &format!("rustc_middle::mir::interpret={0},rustc_mir::interpret={0}", var),
101 env::set_var("RUSTC_LOG", &var);
103 rustc_driver::init_rustc_env_logger();
107 // If `MIRI_BACKTRACE` is set and `RUSTC_CTFE_BACKTRACE` is not, set `RUSTC_CTFE_BACKTRACE`.
108 // Do this late, so we ideally only apply this to Miri's errors.
109 if let Some(val) = env::var_os("MIRI_BACKTRACE") {
110 let ctfe_backtrace = match &*val.to_string_lossy() {
111 "immediate" => CtfeBacktrace::Immediate,
112 "0" => CtfeBacktrace::Disabled,
113 _ => CtfeBacktrace::Capture,
115 *tcx.sess.ctfe_backtrace.borrow_mut() = ctfe_backtrace;
119 /// Returns the "default sysroot" that Miri will use if no `--sysroot` flag is set.
120 /// Should be a compile-time constant.
121 fn compile_time_sysroot() -> Option<String> {
122 if option_env!("RUSTC_STAGE").is_some() {
123 // This is being built as part of rustc, and gets shipped with rustup.
124 // We can rely on the sysroot computation in librustc_session.
127 // For builds outside rustc, we need to ensure that we got a sysroot
128 // that gets used as a default. The sysroot computation in librustc_session would
129 // end up somewhere in the build dir (see `get_or_default_sysroot`).
130 // Taken from PR <https://github.com/Manishearth/rust-clippy/pull/911>.
131 let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
132 let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
133 Some(match (home, toolchain) {
134 (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
135 _ => option_env!("RUST_SYSROOT")
136 .expect("To build Miri without rustup, set the `RUST_SYSROOT` env var at build time")
141 /// Execute a compiler with the given CLI arguments and callbacks.
143 mut args: Vec<String>,
144 callbacks: &mut (dyn rustc_driver::Callbacks + Send),
145 insert_default_args: bool,
147 // Make sure we use the right default sysroot. The default sysroot is wrong,
148 // because `get_or_default_sysroot` in `librustc_session` bases that on `current_exe`.
150 // Make sure we always call `compile_time_sysroot` as that also does some sanity-checks
151 // of the environment we were built in.
152 // FIXME: Ideally we'd turn a bad build env into a compile-time error via CTFE or so.
153 if let Some(sysroot) = compile_time_sysroot() {
154 let sysroot_flag = "--sysroot";
155 if !args.iter().any(|e| e == sysroot_flag) {
156 // We need to overwrite the default that librustc_session would compute.
157 args.push(sysroot_flag.to_owned());
162 if insert_default_args {
163 // Some options have different defaults in Miri than in plain rustc; apply those by making
164 // them the first arguments after the binary name (but later arguments can overwrite them).
165 args.splice(1..1, miri::MIRI_DEFAULT_ARGS.iter().map(ToString::to_string));
168 // Invoke compiler, and handle return code.
169 let exit_code = rustc_driver::catch_with_exit_code(move || {
170 rustc_driver::RunCompiler::new(&args, callbacks).run()
172 std::process::exit(exit_code)
176 rustc_driver::install_ice_hook();
178 // If the environment asks us to actually be rustc, then do that.
179 if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") {
180 rustc_driver::init_rustc_env_logger();
182 // Don't insert `MIRI_DEFAULT_ARGS`, in particular, `--cfg=miri`, if we are building a
183 // "host" crate. That may cause procedural macros (and probably build scripts) to depend
184 // on Miri-only symbols, such as `miri_resolve_frame`:
185 // https://github.com/rust-lang/miri/issues/1760
186 let insert_default_args = if crate_kind == "target" {
188 } else if crate_kind == "host" {
191 panic!("invalid `MIRI_BE_RUSTC` value: {:?}", crate_kind)
194 // We cannot use `rustc_driver::main` as we need to adjust the CLI arguments.
195 let mut callbacks = rustc_driver::TimePassesCallbacks::default();
196 run_compiler(env::args().collect(), &mut callbacks, insert_default_args)
199 // Init loggers the Miri way.
200 init_early_loggers();
202 // Parse our arguments and split them across `rustc` and `miri`.
203 let mut miri_config = miri::MiriConfig::default();
204 let mut rustc_args = vec![];
205 let mut after_dashdash = false;
206 for arg in env::args() {
207 if rustc_args.is_empty() {
208 // Very first arg: binary name.
209 rustc_args.push(arg);
210 } else if after_dashdash {
211 // Everything that comes after `--` is forwarded to the interpreted crate.
212 miri_config.args.push(arg);
215 "-Zmiri-disable-validation" => {
216 miri_config.validate = false;
218 "-Zmiri-disable-stacked-borrows" => {
219 miri_config.stacked_borrows = false;
221 "-Zmiri-disable-data-race-detector" => {
222 miri_config.data_race_detector = false;
224 "-Zmiri-disable-alignment-check" => {
225 miri_config.check_alignment = miri::AlignmentCheck::None;
227 "-Zmiri-symbolic-alignment-check" => {
228 miri_config.check_alignment = miri::AlignmentCheck::Symbolic;
230 "-Zmiri-disable-isolation" => {
231 miri_config.communicate = true;
233 "-Zmiri-ignore-leaks" => {
234 miri_config.ignore_leaks = true;
236 "-Zmiri-track-raw-pointers" => {
237 miri_config.track_raw = true;
240 after_dashdash = true;
242 arg if arg.starts_with("-Zmiri-seed=") => {
243 if miri_config.seed.is_some() {
244 panic!("Cannot specify -Zmiri-seed multiple times!");
246 let seed_raw = hex::decode(arg.strip_prefix("-Zmiri-seed=").unwrap())
247 .unwrap_or_else(|err| match err {
248 FromHexError::InvalidHexCharacter { .. } => panic!(
249 "-Zmiri-seed should only contain valid hex digits [0-9a-fA-F]"
251 FromHexError::OddLength =>
252 panic!("-Zmiri-seed should have an even number of digits"),
253 err => panic!("unknown error decoding -Zmiri-seed as hex: {:?}", err),
255 if seed_raw.len() > 8 {
256 panic!("-Zmiri-seed must be at most 8 bytes, was {}", seed_raw.len());
259 let mut bytes = [0; 8];
260 bytes[..seed_raw.len()].copy_from_slice(&seed_raw);
261 miri_config.seed = Some(u64::from_be_bytes(bytes));
263 arg if arg.starts_with("-Zmiri-env-exclude=") => {
266 .push(arg.strip_prefix("-Zmiri-env-exclude=").unwrap().to_owned());
268 arg if arg.starts_with("-Zmiri-track-pointer-tag=") => {
270 match arg.strip_prefix("-Zmiri-track-pointer-tag=").unwrap().parse() {
273 "-Zmiri-track-pointer-tag requires a valid `u64` argument: {}",
277 if let Some(id) = miri::PtrId::new(id) {
278 miri_config.tracked_pointer_tag = Some(id);
280 panic!("-Zmiri-track-pointer-tag requires a nonzero argument");
283 arg if arg.starts_with("-Zmiri-track-call-id=") => {
284 let id: u64 = match arg.strip_prefix("-Zmiri-track-call-id=").unwrap().parse() {
287 panic!("-Zmiri-track-call-id requires a valid `u64` argument: {}", err),
289 if let Some(id) = miri::CallId::new(id) {
290 miri_config.tracked_call_id = Some(id);
292 panic!("-Zmiri-track-call-id requires a nonzero argument");
295 arg if arg.starts_with("-Zmiri-track-alloc-id=") => {
296 let id: u64 = match arg.strip_prefix("-Zmiri-track-alloc-id=").unwrap().parse()
300 panic!("-Zmiri-track-alloc-id requires a valid `u64` argument: {}", err),
302 miri_config.tracked_alloc_id = Some(miri::AllocId(id));
304 arg if arg.starts_with("-Zmiri-compare-exchange-weak-failure-rate=") => {
306 .strip_prefix("-Zmiri-compare-exchange-weak-failure-rate=")
310 Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate,
312 "-Zmiri-compare-exchange-weak-failure-rate must be between `0.0` and `1.0`"
315 "-Zmiri-compare-exchange-weak-failure-rate requires a `f64` between `0.0` and `1.0`: {}",
319 miri_config.cmpxchg_weak_failure_rate = rate;
321 arg if arg.starts_with("-Zmiri-measureme=") => {
322 let measureme_out = arg.strip_prefix("-Zmiri-measureme=").unwrap();
323 miri_config.measureme_out = Some(measureme_out.to_string());
327 rustc_args.push(arg);
333 debug!("rustc arguments: {:?}", rustc_args);
334 debug!("crate arguments: {:?}", miri_config.args);
337 &mut MiriCompilerCalls { miri_config },
338 /* insert_default_args: */ true,