]> git.lizzy.rs Git - rust.git/blob - src/bin/miri.rs
47cde5c353e133ba708cdc447b1dd0b8b8872bd4
[rust.git] / src / bin / miri.rs
1 #![feature(rustc_private)]
2
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;
9
10 use std::convert::TryFrom;
11 use std::env;
12 use std::str::FromStr;
13
14 use hex::FromHexError;
15 use log::debug;
16
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};
21
22 struct MiriCompilerCalls {
23     miri_config: miri::MiriConfig,
24 }
25
26 impl rustc_driver::Callbacks for MiriCompilerCalls {
27     fn after_analysis<'tcx>(
28         &mut self,
29         compiler: &rustc_interface::interface::Compiler,
30         queries: &'tcx rustc_interface::Queries<'tcx>,
31     ) -> Compilation {
32         compiler.session().abort_if_errors();
33
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(()) {
37                 (entry_def, x)
38             } else {
39                 let output_ty = ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(
40                     ColorConfig::Auto,
41                 ));
42                 rustc_session::early_error(
43                     output_ty,
44                     "miri can only run programs that have a main function",
45                 );
46             };
47             let mut config = self.miri_config.clone();
48
49             // Add filename to `miri` arguments.
50             config.args.insert(0, compiler.input().filestem().to_string());
51
52             // Adjust working directory for interpretation.
53             if let Some(cwd) = env::var_os("MIRI_CWD") {
54                 env::set_current_dir(cwd).unwrap();
55             }
56
57             if let Some(return_code) = miri::eval_main(tcx, entry_def_id, config) {
58                 std::process::exit(
59                     i32::try_from(return_code).expect("Return value was too large!"),
60                 );
61             }
62         });
63
64         compiler.session().abort_if_errors();
65
66         Compilation::Stop
67     }
68 }
69
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();
81     }
82 }
83
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() {
96                 env::set_var(
97                     "RUSTC_LOG",
98                     &format!("rustc_middle::mir::interpret={0},rustc_mir::interpret={0}", var),
99                 );
100             } else {
101                 env::set_var("RUSTC_LOG", &var);
102             }
103             rustc_driver::init_rustc_env_logger();
104         }
105     }
106
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,
114         };
115         *tcx.sess.ctfe_backtrace.borrow_mut() = ctfe_backtrace;
116     }
117 }
118
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.
125         return None;
126     }
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")
137             .to_owned(),
138     })
139 }
140
141 /// Execute a compiler with the given CLI arguments and callbacks.
142 fn run_compiler(
143     mut args: Vec<String>,
144     callbacks: &mut (dyn rustc_driver::Callbacks + Send),
145     insert_default_args: bool,
146 ) -> ! {
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`.
149     //
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());
158             args.push(sysroot);
159         }
160     }
161
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));
166     }
167
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()
171     });
172     std::process::exit(exit_code)
173 }
174
175 fn main() {
176     rustc_driver::install_ice_hook();
177
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();
181
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" {
187             true
188         } else if crate_kind == "host" {
189             false
190         } else {
191             panic!("invalid `MIRI_BE_RUSTC` value: {:?}", crate_kind)
192         };
193
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)
197     }
198
199     // Init loggers the Miri way.
200     init_early_loggers();
201
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);
213         } else {
214             match arg.as_str() {
215                 "-Zmiri-disable-validation" => {
216                     miri_config.validate = false;
217                 }
218                 "-Zmiri-disable-stacked-borrows" => {
219                     miri_config.stacked_borrows = false;
220                 }
221                 "-Zmiri-disable-data-race-detector" => {
222                     miri_config.data_race_detector = false;
223                 }
224                 "-Zmiri-disable-alignment-check" => {
225                     miri_config.check_alignment = miri::AlignmentCheck::None;
226                 }
227                 "-Zmiri-symbolic-alignment-check" => {
228                     miri_config.check_alignment = miri::AlignmentCheck::Symbolic;
229                 }
230                 "-Zmiri-disable-isolation" => {
231                     miri_config.communicate = true;
232                 }
233                 "-Zmiri-ignore-leaks" => {
234                     miri_config.ignore_leaks = true;
235                 }
236                 "-Zmiri-track-raw-pointers" => {
237                     miri_config.track_raw = true;
238                 }
239                 "--" => {
240                     after_dashdash = true;
241                 }
242                 arg if arg.starts_with("-Zmiri-seed=") => {
243                     if miri_config.seed.is_some() {
244                         panic!("Cannot specify -Zmiri-seed multiple times!");
245                     }
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]"
250                             ),
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),
254                         });
255                     if seed_raw.len() > 8 {
256                         panic!("-Zmiri-seed must be at most 8 bytes, was {}", seed_raw.len());
257                     }
258
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));
262                 }
263                 arg if arg.starts_with("-Zmiri-env-exclude=") => {
264                     miri_config
265                         .excluded_env_vars
266                         .push(arg.strip_prefix("-Zmiri-env-exclude=").unwrap().to_owned());
267                 }
268                 arg if arg.starts_with("-Zmiri-track-pointer-tag=") => {
269                     let id: u64 =
270                         match arg.strip_prefix("-Zmiri-track-pointer-tag=").unwrap().parse() {
271                             Ok(id) => id,
272                             Err(err) => panic!(
273                                 "-Zmiri-track-pointer-tag requires a valid `u64` argument: {}",
274                                 err
275                             ),
276                         };
277                     if let Some(id) = miri::PtrId::new(id) {
278                         miri_config.tracked_pointer_tag = Some(id);
279                     } else {
280                         panic!("-Zmiri-track-pointer-tag requires a nonzero argument");
281                     }
282                 }
283                 arg if arg.starts_with("-Zmiri-track-call-id=") => {
284                     let id: u64 = match arg.strip_prefix("-Zmiri-track-call-id=").unwrap().parse() {
285                         Ok(id) => id,
286                         Err(err) =>
287                             panic!("-Zmiri-track-call-id requires a valid `u64` argument: {}", err),
288                     };
289                     if let Some(id) = miri::CallId::new(id) {
290                         miri_config.tracked_call_id = Some(id);
291                     } else {
292                         panic!("-Zmiri-track-call-id requires a nonzero argument");
293                     }
294                 }
295                 arg if arg.starts_with("-Zmiri-track-alloc-id=") => {
296                     let id: u64 = match arg.strip_prefix("-Zmiri-track-alloc-id=").unwrap().parse()
297                     {
298                         Ok(id) => id,
299                         Err(err) =>
300                             panic!("-Zmiri-track-alloc-id requires a valid `u64` argument: {}", err),
301                     };
302                     miri_config.tracked_alloc_id = Some(miri::AllocId(id));
303                 }
304                 arg if arg.starts_with("-Zmiri-compare-exchange-weak-failure-rate=") => {
305                     let rate = match arg
306                         .strip_prefix("-Zmiri-compare-exchange-weak-failure-rate=")
307                         .unwrap()
308                         .parse::<f64>()
309                     {
310                         Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate,
311                         Ok(_) => panic!(
312                             "-Zmiri-compare-exchange-weak-failure-rate must be between `0.0` and `1.0`"
313                         ),
314                         Err(err) => panic!(
315                             "-Zmiri-compare-exchange-weak-failure-rate requires a `f64` between `0.0` and `1.0`: {}",
316                             err
317                         ),
318                     };
319                     miri_config.cmpxchg_weak_failure_rate = rate;
320                 }
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());
324                 }
325                 _ => {
326                     // Forward to rustc.
327                     rustc_args.push(arg);
328                 }
329             }
330         }
331     }
332
333     debug!("rustc arguments: {:?}", rustc_args);
334     debug!("crate arguments: {:?}", miri_config.args);
335     run_compiler(
336         rustc_args,
337         &mut MiriCompilerCalls { miri_config },
338         /* insert_default_args: */ true,
339     )
340 }