]> git.lizzy.rs Git - rust.git/blob - src/bin/miri.rs
Auto merge of #1670 - unseddd:abi, r=RalfJung
[rust.git] / src / bin / miri.rs
1 #![feature(rustc_private)]
2
3 extern crate rustc_middle;
4 extern crate rustc_driver;
5 extern crate rustc_hir;
6 extern crate rustc_interface;
7 extern crate rustc_session;
8 extern crate rustc_errors;
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_session::{CtfeBacktrace, config::ErrorOutputType};
18 use rustc_errors::emitter::{HumanReadableErrorType, ColorConfig};
19 use rustc_driver::Compilation;
20 use rustc_hir::def_id::LOCAL_CRATE;
21 use rustc_middle::ty::TyCtxt;
22
23 struct MiriCompilerCalls {
24     miri_config: miri::MiriConfig,
25 }
26
27 impl rustc_driver::Callbacks for MiriCompilerCalls {
28     fn after_analysis<'tcx>(
29         &mut self,
30         compiler: &rustc_interface::interface::Compiler,
31         queries: &'tcx rustc_interface::Queries<'tcx>,
32     ) -> Compilation {
33         compiler.session().abort_if_errors();
34
35         queries.global_ctxt().unwrap().peek_mut().enter(|tcx| {
36             init_late_loggers(tcx);
37             let (entry_def_id, _) = if let Some((entry_def, x)) = tcx.entry_fn(LOCAL_CRATE) {
38                 (entry_def, x)
39             } else {
40                 let output_ty = ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto));
41                 rustc_session::early_error(output_ty, "miri can only run programs that have a main function");
42             };
43             let mut config = self.miri_config.clone();
44
45             // Add filename to `miri` arguments.
46             config.args.insert(0, compiler.input().filestem().to_string());
47
48             // Adjust working directory for interpretation.
49             if let Some(cwd) = env::var_os("MIRI_CWD") {
50                 env::set_current_dir(cwd).unwrap();
51             }
52
53             if let Some(return_code) = miri::eval_main(tcx, entry_def_id.to_def_id(), config) {
54                 std::process::exit(
55                     i32::try_from(return_code).expect("Return value was too large!"),
56                 );
57             }
58         });
59
60         compiler.session().abort_if_errors();
61
62         Compilation::Stop
63     }
64 }
65
66 fn init_early_loggers() {
67     // Note that our `extern crate log` is *not* the same as rustc's; as a result, we have to
68     // initialize them both, and we always initialize `miri`'s first.
69     let env = env_logger::Env::new().filter("MIRI_LOG").write_style("MIRI_LOG_STYLE");
70     env_logger::init_from_env(env);
71     // We only initialize `rustc` if the env var is set (so the user asked for it).
72     // If it is not set, we avoid initializing now so that we can initialize
73     // later with our custom settings, and *not* log anything for what happens before
74     // `miri` gets started.
75     if env::var_os("RUSTC_LOG").is_some() {
76         rustc_driver::init_rustc_env_logger();
77     }
78 }
79
80 fn init_late_loggers(tcx: TyCtxt<'_>) {
81     // We initialize loggers right before we start evaluation. We overwrite the `RUSTC_LOG`
82     // env var if it is not set, control it based on `MIRI_LOG`.
83     // (FIXME: use `var_os`, but then we need to manually concatenate instead of `format!`.)
84     if let Ok(var) = env::var("MIRI_LOG") {
85         if env::var_os("RUSTC_LOG").is_none() {
86             // We try to be a bit clever here: if `MIRI_LOG` is just a single level
87             // used for everything, we only apply it to the parts of rustc that are
88             // CTFE-related. Otherwise, we use it verbatim for `RUSTC_LOG`.
89             // This way, if you set `MIRI_LOG=trace`, you get only the right parts of
90             // rustc traced, but you can also do `MIRI_LOG=miri=trace,rustc_mir::interpret=debug`.
91             if log::Level::from_str(&var).is_ok() {
92                 env::set_var(
93                     "RUSTC_LOG",
94                     &format!("rustc_middle::mir::interpret={0},rustc_mir::interpret={0}", var),
95                 );
96             } else {
97                 env::set_var("RUSTC_LOG", &var);
98             }
99             rustc_driver::init_rustc_env_logger();
100         }
101     }
102
103     // If `MIRI_BACKTRACE` is set and `RUSTC_CTFE_BACKTRACE` is not, set `RUSTC_CTFE_BACKTRACE`.
104     // Do this late, so we ideally only apply this to Miri's errors.
105     if let Some(val) = env::var_os("MIRI_BACKTRACE") {
106         let ctfe_backtrace = match &*val.to_string_lossy() {
107             "immediate" => CtfeBacktrace::Immediate,
108             "0" => CtfeBacktrace::Disabled,
109             _ => CtfeBacktrace::Capture,
110         };
111         *tcx.sess.ctfe_backtrace.borrow_mut() = ctfe_backtrace;
112     }
113 }
114
115 /// Returns the "default sysroot" that Miri will use if no `--sysroot` flag is set.
116 /// Should be a compile-time constant.
117 fn compile_time_sysroot() -> Option<String> {
118     if option_env!("RUSTC_STAGE").is_some() {
119         // This is being built as part of rustc, and gets shipped with rustup.
120         // We can rely on the sysroot computation in librustc_session.
121         return None;
122     }
123     // For builds outside rustc, we need to ensure that we got a sysroot
124     // that gets used as a default.  The sysroot computation in librustc_session would
125     // end up somewhere in the build dir (see `get_or_default_sysroot`).
126     // Taken from PR <https://github.com/Manishearth/rust-clippy/pull/911>.
127     let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
128     let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
129     Some(match (home, toolchain) {
130         (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
131         _ => option_env!("RUST_SYSROOT")
132             .expect("To build Miri without rustup, set the `RUST_SYSROOT` env var at build time")
133             .to_owned(),
134     })
135 }
136
137 /// Execute a compiler with the given CLI arguments and callbacks.
138 fn run_compiler(mut args: Vec<String>, callbacks: &mut (dyn rustc_driver::Callbacks + Send)) -> ! {
139     // Make sure we use the right default sysroot. The default sysroot is wrong,
140     // because `get_or_default_sysroot` in `librustc_session` bases that on `current_exe`.
141     //
142     // Make sure we always call `compile_time_sysroot` as that also does some sanity-checks
143     // of the environment we were built in.
144     // FIXME: Ideally we'd turn a bad build env into a compile-time error via CTFE or so.
145     if let Some(sysroot) = compile_time_sysroot() {
146         let sysroot_flag = "--sysroot";
147         if !args.iter().any(|e| e == sysroot_flag) {
148             // We need to overwrite the default that librustc_session would compute.
149             args.push(sysroot_flag.to_owned());
150             args.push(sysroot);
151         }
152     }
153
154     // Some options have different defaults in Miri than in plain rustc; apply those by making
155     // them the first arguments after the binary name (but later arguments can overwrite them).
156     args.splice(1..1, miri::MIRI_DEFAULT_ARGS.iter().map(ToString::to_string));
157
158     // Invoke compiler, and handle return code.
159     let exit_code = rustc_driver::catch_with_exit_code(move || {
160         rustc_driver::RunCompiler::new(&args, callbacks).run()
161     });
162     std::process::exit(exit_code)
163 }
164
165 fn main() {
166     rustc_driver::install_ice_hook();
167
168     // If the environment asks us to actually be rustc, then do that.
169     if env::var_os("MIRI_BE_RUSTC").is_some() {
170         rustc_driver::init_rustc_env_logger();
171         // We cannot use `rustc_driver::main` as we need to adjust the CLI arguments.
172         let mut callbacks = rustc_driver::TimePassesCallbacks::default();
173         run_compiler(env::args().collect(), &mut callbacks)
174     }
175
176     // Init loggers the Miri way.
177     init_early_loggers();
178
179     // Parse our arguments and split them across `rustc` and `miri`.
180     let mut miri_config = miri::MiriConfig::default();
181     let mut rustc_args = vec![];
182     let mut after_dashdash = false;
183     for arg in env::args() {
184         if rustc_args.is_empty() {
185             // Very first arg: binary name.
186             rustc_args.push(arg);
187         } else if after_dashdash {
188             // Everything that comes after `--` is forwarded to the interpreted crate.
189             miri_config.args.push(arg);
190         } else {
191             match arg.as_str() {
192                 "-Zmiri-disable-validation" => {
193                     miri_config.validate = false;
194                 }
195                 "-Zmiri-disable-stacked-borrows" => {
196                     miri_config.stacked_borrows = false;
197                 }
198                 "-Zmiri-disable-data-race-detector" => {
199                     miri_config.data_race_detector = false;
200                 }
201                 "-Zmiri-disable-alignment-check" => {
202                     miri_config.check_alignment = miri::AlignmentCheck::None;
203                 }
204                 "-Zmiri-symbolic-alignment-check" => {
205                     miri_config.check_alignment = miri::AlignmentCheck::Symbolic;
206                 }
207                 "-Zmiri-disable-isolation" => {
208                     miri_config.communicate = true;
209                 }
210                 "-Zmiri-ignore-leaks" => {
211                     miri_config.ignore_leaks = true;
212                 }
213                 "-Zmiri-track-raw-pointers" => {
214                     miri_config.track_raw = true;
215                 }
216                 "--" => {
217                     after_dashdash = true;
218                 }
219                 arg if arg.starts_with("-Zmiri-seed=") => {
220                     if miri_config.seed.is_some() {
221                         panic!("Cannot specify -Zmiri-seed multiple times!");
222                     }
223                     let seed_raw = hex::decode(arg.strip_prefix("-Zmiri-seed=").unwrap())
224                         .unwrap_or_else(|err| match err {
225                             FromHexError::InvalidHexCharacter { .. } => panic!(
226                                 "-Zmiri-seed should only contain valid hex digits [0-9a-fA-F]"
227                             ),
228                             FromHexError::OddLength =>
229                                 panic!("-Zmiri-seed should have an even number of digits"),
230                             err => panic!("unknown error decoding -Zmiri-seed as hex: {:?}", err),
231                         });
232                     if seed_raw.len() > 8 {
233                         panic!(format!(
234                             "-Zmiri-seed must be at most 8 bytes, was {}",
235                             seed_raw.len()
236                         ));
237                     }
238
239                     let mut bytes = [0; 8];
240                     bytes[..seed_raw.len()].copy_from_slice(&seed_raw);
241                     miri_config.seed = Some(u64::from_be_bytes(bytes));
242                 }
243                 arg if arg.starts_with("-Zmiri-env-exclude=") => {
244                     miri_config.excluded_env_vars
245                         .push(arg.strip_prefix("-Zmiri-env-exclude=").unwrap().to_owned());
246                 }
247                 arg if arg.starts_with("-Zmiri-track-pointer-tag=") => {
248                     let id: u64 = match arg.strip_prefix("-Zmiri-track-pointer-tag=").unwrap().parse() {
249                         Ok(id) => id,
250                         Err(err) => panic!(
251                             "-Zmiri-track-pointer-tag requires a valid `u64` argument: {}",
252                             err
253                         ),
254                     };
255                     if let Some(id) = miri::PtrId::new(id) {
256                         miri_config.tracked_pointer_tag = Some(id);
257                     } else {
258                         panic!("-Zmiri-track-pointer-tag requires a nonzero argument");
259                     }
260                 }
261                 arg if arg.starts_with("-Zmiri-track-call-id=") => {
262                     let id: u64 = match arg.strip_prefix("-Zmiri-track-call-id=").unwrap().parse() {
263                         Ok(id) => id,
264                         Err(err) => panic!(
265                             "-Zmiri-track-call-id requires a valid `u64` argument: {}",
266                             err
267                         ),
268                     };
269                     if let Some(id) = miri::CallId::new(id) {
270                         miri_config.tracked_call_id = Some(id);
271                     } else {
272                         panic!("-Zmiri-track-call-id requires a nonzero argument");
273                     }
274                 }
275                 arg if arg.starts_with("-Zmiri-track-alloc-id=") => {
276                     let id: u64 = match arg.strip_prefix("-Zmiri-track-alloc-id=").unwrap().parse() {
277                         Ok(id) => id,
278                         Err(err) => panic!(
279                             "-Zmiri-track-alloc-id requires a valid `u64` argument: {}",
280                             err
281                         ),
282                     };
283                     miri_config.tracked_alloc_id = Some(miri::AllocId(id));
284                 }
285                 arg if arg.starts_with("-Zmiri-compare-exchange-weak-failure-rate=") => {
286                     let rate = match arg.strip_prefix("-Zmiri-compare-exchange-weak-failure-rate=").unwrap().parse::<f64>() {
287                         Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate,
288                         Ok(_) => panic!("-Zmiri-compare-exchange-weak-failure-rate must be between `0.0` and `1.0`"),
289                         Err(err) => panic!("-Zmiri-compare-exchange-weak-failure-rate requires a `f64` between `0.0` and `1.0`: {}", err),
290                     };
291                     miri_config.cmpxchg_weak_failure_rate = rate;
292                 }
293                 _ => {
294                     // Forward to rustc.
295                     rustc_args.push(arg);
296                 }
297             }
298         }
299     }
300
301     debug!("rustc arguments: {:?}", rustc_args);
302     debug!("crate arguments: {:?}", miri_config.args);
303     run_compiler(rustc_args, &mut MiriCompilerCalls { miri_config })
304 }