Skip to main content

miri/
eval.rs

1//! Main evaluator loop and setting up the initial stack frame.
2
3use std::ffi::{OsStr, OsString};
4use std::num::NonZeroI32;
5use std::panic::{self, AssertUnwindSafe};
6use std::path::PathBuf;
7use std::rc::Rc;
8use std::task::Poll;
9use std::{iter, mem, thread};
10
11use rustc_abi::ExternAbi;
12use rustc_data_structures::fx::{FxHashMap, FxHashSet};
13use rustc_errors::FatalErrorMarker;
14use rustc_hir::def::Namespace;
15use rustc_hir::def_id::{DefId, LOCAL_CRATE};
16use rustc_hir_analysis::check::check_function_signature;
17use rustc_middle::middle::exported_symbols::ExportedSymbol;
18use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
19use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutCx};
20use rustc_middle::ty::{self, Ty, TyCtxt};
21use rustc_session::config::EntryFnType;
22use rustc_target::spec::Os;
23
24use crate::concurrency::GenmcCtx;
25use crate::diagnostics::report_leaks;
26use crate::helpers::is_no_core;
27use crate::shims::{global_ctor, tls};
28use crate::*;
29
30#[derive(Copy, Clone, Debug)]
31pub enum MiriEntryFnType {
32    MiriStart,
33    Rustc(EntryFnType),
34}
35
36/// Finds the entry point Miri should execute.
37///
38/// Public because this is used by Priroda.
39pub fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, MiriEntryFnType) {
40    if let Some((def_id, entry_type)) = tcx.entry_fn(()) {
41        return (def_id, MiriEntryFnType::Rustc(entry_type));
42    }
43    // Look for a symbol in the local crate named `miri_start`, and treat that as the entry point.
44    let sym = tcx.exported_non_generic_symbols(LOCAL_CRATE).iter().find_map(|(sym, _)| {
45        if sym.symbol_name_for_local_instance(tcx).name == "miri_start" { Some(sym) } else { None }
46    });
47    if let Some(ExportedSymbol::NonGeneric(id)) = sym {
48        let start_def_id = id.expect_local();
49        let start_span = tcx.def_span(start_def_id);
50
51        let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig_safe_rust_abi(
52            [tcx.types.isize, Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8))],
53            tcx.types.isize,
54        ));
55
56        let correct_func_sig = check_function_signature(
57            tcx,
58            ObligationCause::new(start_span, start_def_id, ObligationCauseCode::Misc),
59            *id,
60            expected_sig,
61        )
62        .is_ok();
63
64        if correct_func_sig {
65            (*id, MiriEntryFnType::MiriStart)
66        } else {
67            tcx.dcx().fatal(
68                "`miri_start` must have the following signature:\n\
69                fn miri_start(argc: isize, argv: *const *const u8) -> isize",
70            );
71        }
72    } else {
73        tcx.dcx().fatal(
74            "Miri can only run programs that have a main function.\n\
75            Alternatively, you can export a `miri_start` function:\n\
76            \n\
77            #[cfg(miri)]\n\
78            #[unsafe(no_mangle)]\n\
79            fn miri_start(argc: isize, argv: *const *const u8) -> isize {\
80            \n    // Call the actual start function that your project implements, based on your target's conventions.\n\
81            }"
82        );
83    }
84}
85
86/// When the program exits, we will yield to any other thread that is ready to execute.
87/// But we must only do that a finite number of times, or a background thread running `loop {}`
88/// will hang the program.
89const THREAD_YIELDS_AT_EXIT: u32 = 256;
90
91/// Configuration needed to spawn a Miri instance.
92#[derive(Clone)]
93pub struct MiriConfig {
94    /// The host environment snapshot to use as basis for what is provided to the interpreted program.
95    /// (This is still subject to isolation as well as `forwarded_env_vars`.)
96    pub env: Vec<(OsString, OsString)>,
97    /// Determine if validity checking is enabled.
98    pub validation: ValidationMode,
99    /// Determines if Stacked Borrows or Tree Borrows is enabled.
100    pub borrow_tracker: Option<BorrowTrackerMethod>,
101    /// Controls alignment checking.
102    pub check_alignment: AlignmentCheck,
103    /// Action for an op requiring communication with the host.
104    pub isolated_op: IsolatedOp,
105    /// Determines if memory leaks should be ignored.
106    pub ignore_leaks: bool,
107    /// Environment variables that should always be forwarded from the host.
108    pub forwarded_env_vars: Vec<String>,
109    /// Additional environment variables that should be set in the interpreted program.
110    pub set_env_vars: FxHashMap<String, String>,
111    /// Command-line arguments passed to the interpreted program.
112    pub args: Vec<String>,
113    /// The binary we pretend to be.
114    pub current_exe: Option<PathBuf>,
115    /// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`).
116    pub seed: Option<u64>,
117    /// The stacked borrows pointer ids to report about.
118    pub tracked_pointer_tags: FxHashSet<BorTag>,
119    /// The allocation ids to report about.
120    pub tracked_alloc_ids: FxHashSet<AllocId>,
121    /// For the tracked alloc ids, also report read/write accesses.
122    pub track_alloc_accesses: bool,
123    /// Determine if data race detection should be enabled.
124    pub data_race_detector: bool,
125    /// Determine if weak memory emulation should be enabled. Requires data race detection to be enabled.
126    pub weak_memory_emulation: bool,
127    /// Determine if we are running in GenMC mode and with which settings. In GenMC mode, Miri will explore multiple concurrent executions of the given program.
128    pub genmc_config: Option<GenmcConfig>,
129    /// Track when an outdated (weak memory) load happens.
130    pub track_outdated_loads: bool,
131    /// Rate of spurious failures for compare_exchange_weak atomic operations,
132    /// between 0.0 and 1.0, defaulting to 0.8 (80% chance of failure).
133    pub cmpxchg_weak_failure_rate: f64,
134    /// If `Some`, enable the `measureme` profiler, writing results to a file
135    /// with the specified prefix.
136    pub measureme_out: Option<String>,
137    /// Which style to use for printing backtraces.
138    pub backtrace_style: BacktraceStyle,
139    /// Which provenance to use for int2ptr casts.
140    pub provenance_mode: ProvenanceMode,
141    /// Whether to ignore any output by the program. This is helpful when debugging miri
142    /// as its messages don't get intermingled with the program messages.
143    pub mute_stdout_stderr: bool,
144    /// The probability of the active thread being preempted at the end of each basic block.
145    pub preemption_rate: f64,
146    /// Report the current instruction being executed every N basic blocks.
147    pub report_progress: Option<u32>,
148    /// The location of the shared object files to load when calling external functions
149    pub native_lib: Vec<PathBuf>,
150    /// Whether to enable the new native lib tracing system.
151    pub native_lib_enable_tracing: bool,
152    /// Run a garbage collector for BorTags every N basic blocks.
153    pub gc_interval: u32,
154    /// The number of CPUs to be reported by miri.
155    pub num_cpus: u32,
156    /// Requires Miri to emulate pages of a certain size.
157    pub page_size: Option<u64>,
158    /// Whether to collect a backtrace when each allocation is created, just in case it leaks.
159    pub collect_leak_backtraces: bool,
160    /// Probability for address reuse.
161    pub address_reuse_rate: f64,
162    /// Probability for address reuse across threads.
163    pub address_reuse_cross_thread_rate: f64,
164    /// Round Robin scheduling with no preemption.
165    pub fixed_scheduling: bool,
166    /// Whether floating-point operations can behave non-deterministically.
167    pub float_nondet: bool,
168    /// Whether floating-point operations can have a non-deterministic rounding error.
169    pub float_rounding_error: FloatRoundingErrorMode,
170    /// Whether Miri artificially introduces short reads/writes on file descriptors.
171    pub short_fd_operations: bool,
172    /// A list of crates that are considered user-relevant.
173    pub user_relevant_crates: Vec<String>,
174}
175
176impl Default for MiriConfig {
177    fn default() -> MiriConfig {
178        MiriConfig {
179            env: vec![],
180            validation: ValidationMode::Shallow,
181            borrow_tracker: Some(BorrowTrackerMethod::StackedBorrows),
182            check_alignment: AlignmentCheck::Int,
183            isolated_op: IsolatedOp::Reject(RejectOpWith::Abort),
184            ignore_leaks: false,
185            forwarded_env_vars: vec![],
186            set_env_vars: FxHashMap::default(),
187            args: vec![],
188            current_exe: None,
189            seed: None,
190            tracked_pointer_tags: FxHashSet::default(),
191            tracked_alloc_ids: FxHashSet::default(),
192            track_alloc_accesses: false,
193            data_race_detector: true,
194            weak_memory_emulation: true,
195            genmc_config: None,
196            track_outdated_loads: false,
197            cmpxchg_weak_failure_rate: 0.8, // 80%
198            measureme_out: None,
199            backtrace_style: BacktraceStyle::Short,
200            provenance_mode: ProvenanceMode::Default,
201            mute_stdout_stderr: false,
202            preemption_rate: 0.01, // 1%
203            report_progress: None,
204            native_lib: vec![],
205            native_lib_enable_tracing: false,
206            gc_interval: 10_000,
207            num_cpus: 1,
208            page_size: None,
209            collect_leak_backtraces: true,
210            address_reuse_rate: 0.5,
211            address_reuse_cross_thread_rate: 0.1,
212            fixed_scheduling: false,
213            float_nondet: true,
214            float_rounding_error: FloatRoundingErrorMode::Random,
215            short_fd_operations: true,
216            user_relevant_crates: vec![],
217        }
218    }
219}
220
221#[derive(Clone, Copy, Debug)]
222pub enum ExitType {
223    MainThreadFinish,
224    ExitCalled,
225}
226
227/// A state machine that's run when the program is exiting.
228#[derive(Debug)]
229pub struct ProgramExitHandler<'tcx> {
230    state: ProgramExitState<'tcx>,
231    exit_code: i32,
232    exit_type: ExitType,
233}
234#[derive(Debug)]
235enum ProgramExitState<'tcx> {
236    TlsDtors(tls::TlsDtorsState<'tcx>),
237    Yield { remaining: u32 },
238    Done,
239    Unreachable,
240}
241
242impl<'tcx> ProgramExitHandler<'tcx> {
243    pub fn new(exit_code: i32, exit_type: ExitType) -> Self {
244        Self {
245            state: ProgramExitState::TlsDtors(tls::TlsDtorsState::new_for_exit()),
246            exit_code,
247            exit_type,
248        }
249    }
250    pub fn on_stack_empty(&mut self, ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, Poll<()>> {
251        use ProgramExitState::*;
252        match &mut self.state {
253            TlsDtors(state) =>
254                match state.on_stack_empty(ecx)? {
255                    Poll::Pending => {} // just keep going
256                    Poll::Ready(()) => {
257                        if ecx.machine.data_race.as_genmc_ref().is_some() {
258                            // In GenMC mode, we don't yield at the end of the main thread. Instead,
259                            // the `GenmcCtx` will ensure that unfinished threads get a chance to
260                            // run at this point.
261                            self.state = Done;
262                        } else {
263                            // Give background threads a chance to finish by yielding the main
264                            // thread a couple of times.
265                            self.state = Yield { remaining: THREAD_YIELDS_AT_EXIT };
266                        }
267                    }
268                },
269            Yield { remaining } =>
270                match remaining.checked_sub(1) {
271                    None => self.state = Done,
272                    Some(new_remaining) => {
273                        *remaining = new_remaining;
274                        ecx.yield_active_thread();
275                    }
276                },
277            Done => {
278                // Terminate this thread in the scheduler.
279                ecx.terminate_active_thread(/* for_exit */ true)?;
280
281                // We cannot come back.
282                self.state = Unreachable;
283
284                // In GenMC mode, we do not immediately stop execution on exit.
285                if let Some(genmc_ctx) = ecx.machine.data_race.as_genmc_ref() {
286                    // If there's no error, execution will continue (on another thread).
287                    genmc_ctx.handle_exit(
288                        ecx.machine.threads.active_thread(),
289                        self.exit_code,
290                        self.exit_type,
291                    )?;
292                } else {
293                    // Stop interpreter loop.
294                    throw_machine_stop!(TerminationInfo::Exit {
295                        exit_code: self.exit_code,
296                        leak_check: matches!(self.exit_type, ExitType::MainThreadFinish),
297                    });
298                }
299            }
300            Unreachable => unreachable!(),
301        }
302        interp_ok(Poll::Pending)
303    }
304}
305
306/// The state of the main thread. Implementation detail of `on_main_stack_empty`.
307#[derive(Debug)]
308enum MainThreadState<'tcx> {
309    GlobalCtors {
310        ctor_state: global_ctor::GlobalCtorState<'tcx>,
311        /// The main function to call.
312        entry_id: DefId,
313        entry_type: MiriEntryFnType,
314        /// Arguments passed to `main`.
315        argc: ImmTy<'tcx>,
316        argv: ImmTy<'tcx>,
317    },
318    Running,
319    Exiting(ProgramExitHandler<'tcx>),
320}
321
322impl<'tcx> MainThreadState<'tcx> {
323    fn on_main_stack_empty(
324        &mut self,
325        ecx: &mut MiriInterpCx<'tcx>,
326    ) -> InterpResult<'tcx, Poll<()>> {
327        use MainThreadState::*;
328        match self {
329            GlobalCtors { ctor_state, entry_id, entry_type, argc, argv } => {
330                match ctor_state.on_stack_empty(ecx)? {
331                    Poll::Pending => {} // just keep going
332                    Poll::Ready(()) => {
333                        call_main(ecx, *entry_id, *entry_type, argc.clone(), argv.clone())?;
334                        *self = Running;
335                    }
336                }
337            }
338            Running => {
339                // Main code is done, the program is exiting.
340
341                // This is like a call to `exit`. Ensure `exit` gets called at most once.
342                let old_exit = mem::replace(&mut ecx.machine.exit_called, true);
343                if old_exit {
344                    throw_ub_format!("`exit` called more than once");
345                }
346
347                // Figure out exit code.
348                let ret_place = ecx.machine.main_fn_ret_place.clone().unwrap();
349                let exit_code = ecx.read_target_isize(&ret_place)?;
350                // Rust uses `isize` but the underlying type of an exit code is `i32`.
351                let Ok(exit_code) = i32::try_from(exit_code) else {
352                    throw_ub_format!(
353                        "lang_start item return an exit code that does not fit into `i32`"
354                    );
355                };
356                *self = Exiting(ProgramExitHandler::new(exit_code, ExitType::MainThreadFinish));
357            }
358            Exiting(state) => {
359                // This is a sink state, we stay here forever.
360                return state.on_stack_empty(ecx);
361            }
362        }
363        interp_ok(Poll::Pending)
364    }
365}
366
367/// Returns a freshly created `InterpCx`.
368/// Public because this is also used by `priroda`.
369pub fn create_ecx<'tcx>(
370    tcx: TyCtxt<'tcx>,
371    entry_id: DefId,
372    entry_type: MiriEntryFnType,
373    config: &MiriConfig,
374    genmc_ctx: Option<Rc<GenmcCtx>>,
375) -> InterpResult<'tcx, InterpCx<'tcx, MiriMachine<'tcx>>> {
376    let typing_env = ty::TypingEnv::fully_monomorphized();
377    let layout_cx = LayoutCx::new(tcx, typing_env);
378    let mut ecx = InterpCx::new(
379        tcx,
380        rustc_span::DUMMY_SP,
381        typing_env,
382        MiriMachine::new(config, layout_cx, genmc_ctx),
383    );
384
385    // Make sure we have MIR. We check MIR for some stable monomorphic function in libcore. However,
386    // if the current crate is #![no_core] it's fine to be missing the usual items from libcore.
387    if !is_no_core(tcx) {
388        let sentinel = helpers::try_resolve_path(
389            tcx,
390            &["core", "ascii", "escape_default"],
391            Namespace::ValueNS,
392        );
393        if !matches!(sentinel, Some(s) if tcx.is_mir_available(s.def.def_id())) {
394            tcx.dcx().fatal(
395                "the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing.\n\
396                Note that directly invoking the `miri` binary is not supported; please use `cargo miri` instead."
397            );
398        }
399    }
400
401    // Compute argc and argv from `config.args`.
402    let argc =
403        ImmTy::from_int(i64::try_from(config.args.len()).unwrap(), ecx.machine.layouts.isize);
404    let argv = {
405        // Put each argument in memory, collect pointers.
406        let mut argvs = Vec::<Immediate<Provenance>>::with_capacity(config.args.len());
407        for arg in config.args.iter() {
408            // Make space for `0` terminator.
409            let size = u64::try_from(arg.len()).unwrap().strict_add(1);
410            let arg_type = Ty::new_array(tcx, tcx.types.u8, size);
411            let arg_place =
412                ecx.allocate(ecx.layout_of(arg_type)?, MiriMemoryKind::Machine.into())?;
413            ecx.write_os_str_to_c_str(OsStr::new(arg), arg_place.ptr(), size)?;
414            ecx.mark_immutable(&arg_place);
415            argvs.push(arg_place.to_ref(&ecx));
416        }
417        // Make an array with all these pointers, in the Miri memory.
418        let u8_ptr_type = Ty::new_imm_ptr(tcx, tcx.types.u8);
419        let u8_ptr_ptr_type = Ty::new_imm_ptr(tcx, u8_ptr_type);
420        let argvs_layout =
421            ecx.layout_of(Ty::new_array(tcx, u8_ptr_type, u64::try_from(argvs.len()).unwrap()))?;
422        let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into())?;
423        for (arg, idx) in argvs.into_iter().zip(0..) {
424            let place = ecx.project_index(&argvs_place, idx)?;
425            ecx.write_immediate(arg, &place)?;
426        }
427        ecx.mark_immutable(&argvs_place);
428        // Store `argc` and `argv` for macOS `_NSGetArg{c,v}`, and for the GC to see them.
429        {
430            let argc_place =
431                ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?;
432            ecx.write_immediate(*argc, &argc_place)?;
433            ecx.mark_immutable(&argc_place);
434            ecx.machine.argc = Some(argc_place.ptr());
435
436            let argv_place =
437                ecx.allocate(ecx.layout_of(u8_ptr_ptr_type)?, MiriMemoryKind::Machine.into())?;
438            ecx.write_pointer(argvs_place.ptr(), &argv_place)?;
439            ecx.mark_immutable(&argv_place);
440            ecx.machine.argv = Some(argv_place.ptr());
441        }
442        // Store command line as UTF-16 for Windows `GetCommandLineW`.
443        if tcx.sess.target.os == Os::Windows {
444            // Construct a command string with all the arguments.
445            let cmd_utf16: Vec<u16> = args_to_utf16_command_string(config.args.iter());
446
447            let cmd_type =
448                Ty::new_array(tcx, tcx.types.u16, u64::try_from(cmd_utf16.len()).unwrap());
449            let cmd_place =
450                ecx.allocate(ecx.layout_of(cmd_type)?, MiriMemoryKind::Machine.into())?;
451            ecx.machine.cmd_line = Some(cmd_place.ptr());
452            // Store the UTF-16 string. We just allocated so we know the bounds are fine.
453            for (&c, idx) in cmd_utf16.iter().zip(0..) {
454                let place = ecx.project_index(&cmd_place, idx)?;
455                ecx.write_scalar(Scalar::from_u16(c), &place)?;
456            }
457            ecx.mark_immutable(&cmd_place);
458        }
459        let imm = argvs_place.to_ref(&ecx);
460        let layout = ecx.layout_of(u8_ptr_ptr_type)?;
461        ImmTy::from_immediate(imm, layout)
462    };
463
464    // Some parts of initialization require a full `InterpCx`.
465    MiriMachine::late_init(&mut ecx, config, {
466        let mut main_thread_state = MainThreadState::GlobalCtors {
467            entry_id,
468            entry_type,
469            argc,
470            argv,
471            ctor_state: global_ctor::GlobalCtorState::default(),
472        };
473
474        // Cannot capture anything GC-relevant here.
475        // `argc` and `argv` *are* GC_relevant, but they also get stored in `machine.argc` and
476        // `machine.argv` so we are good.
477        Box::new(move |m| main_thread_state.on_main_stack_empty(m))
478    })?;
479
480    interp_ok(ecx)
481}
482
483// Call the entry function.
484fn call_main<'tcx>(
485    ecx: &mut MiriInterpCx<'tcx>,
486    entry_id: DefId,
487    entry_type: MiriEntryFnType,
488    argc: ImmTy<'tcx>,
489    argv: ImmTy<'tcx>,
490) -> InterpResult<'tcx, ()> {
491    let tcx = ecx.tcx();
492
493    // Setup first stack frame.
494    let entry_instance = ty::Instance::mono(tcx, entry_id);
495
496    // Return place (in static memory so that it does not count as leak).
497    let ret_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?;
498    ecx.machine.main_fn_ret_place = Some(ret_place.clone());
499
500    // Call start function.
501    match entry_type {
502        MiriEntryFnType::Rustc(EntryFnType::Main { .. }) => {
503            let entry_sig = tcx.fn_sig(entry_id).no_bound_vars().unwrap();
504            let main_ret_ty = entry_sig.output();
505            let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
506
507            let start_id = tcx.lang_items().start_fn().unwrap_or_else(|| {
508                tcx.dcx().fatal("could not find start lang item");
509            });
510            let start_instance = ty::Instance::try_resolve(
511                tcx,
512                ecx.typing_env(),
513                start_id,
514                tcx.mk_args(&[ty::GenericArg::from(main_ret_ty)]),
515            )
516            .unwrap()
517            .unwrap();
518
519            let main_ptr = ecx.fn_ptr(FnVal::Instance(entry_instance));
520
521            // Always using DEFAULT is okay since we don't support signals in Miri anyway.
522            // (This means we are effectively ignoring `-Zon-broken-pipe`.)
523            let sigpipe = rustc_session::config::sigpipe::DEFAULT;
524
525            ecx.call_function(
526                start_instance,
527                ExternAbi::Rust,
528                &[
529                    ImmTy::from_scalar(
530                        Scalar::from_pointer(main_ptr, ecx),
531                        ecx.layout_of(Ty::new_fn_ptr(tcx, entry_sig)).unwrap(),
532                    ),
533                    argc,
534                    argv,
535                    ImmTy::from_uint(sigpipe, ecx.machine.layouts.u8),
536                ],
537                Some(&ret_place),
538                ReturnContinuation::Stop { cleanup: true },
539            )?;
540        }
541        MiriEntryFnType::MiriStart => {
542            ecx.call_function(
543                entry_instance,
544                ExternAbi::Rust,
545                &[argc, argv],
546                Some(&ret_place),
547                ReturnContinuation::Stop { cleanup: true },
548            )?;
549        }
550    }
551
552    interp_ok(())
553}
554
555/// Evaluates the entry function specified by `entry_id`.
556/// Returns `Ok(())` if program execution completed with exit code 0.
557/// Returns `Err(code)` if an evaluation error occurred or the program returned a non-0 exit code.
558pub fn eval_entry<'tcx>(
559    tcx: TyCtxt<'tcx>,
560    entry_id: DefId,
561    entry_type: MiriEntryFnType,
562    config: &MiriConfig,
563    genmc_ctx: Option<Rc<GenmcCtx>>,
564) -> Result<(), NonZeroI32> {
565    // Copy setting before we move `config`.
566    let ignore_leaks = config.ignore_leaks;
567
568    let mut ecx = match create_ecx(tcx, entry_id, entry_type, config, genmc_ctx).report_err() {
569        Ok(v) => v,
570        Err(err) => {
571            let (kind, backtrace) = err.into_parts();
572            backtrace.print_backtrace();
573            panic!("Miri initialization error: {kind:?}")
574        }
575    };
576
577    // Perform the main execution.
578    let res: thread::Result<InterpResult<'_, !>> =
579        panic::catch_unwind(AssertUnwindSafe(|| ecx.run_threads()));
580    let res = res.unwrap_or_else(|panic_payload| {
581        // rustc "handles" some errors by unwinding with FatalErrorMarker
582        // (after emitting suitable diagnostics), so do not treat those as ICEs.
583        if !panic_payload.is::<FatalErrorMarker>() {
584            ecx.handle_ice();
585        }
586        panic::resume_unwind(panic_payload)
587    });
588    // Obtain the result of the execution. This is always an `Err`, but that doesn't necessarily
589    // indicate an error.
590    let Err(res) = res.report_err();
591
592    // Error reporting: if we survive all checks, we return the exit code the program gave us.
593    'miri_error: {
594        // Show diagnostic, if any.
595        let Some((return_code, leak_check)) = report_result(&ecx, res) else {
596            break 'miri_error;
597        };
598
599        // If we get here there was no fatal error -- yet.
600        // Possibly check for memory leaks.
601        if leak_check && !ignore_leaks {
602            // Check for thread leaks.
603            if !ecx.have_all_terminated() {
604                tcx.dcx()
605                    .err("the main thread terminated without waiting for all remaining threads");
606                tcx.dcx().note("set `MIRIFLAGS=-Zmiri-ignore-leaks` to disable this check");
607                break 'miri_error;
608            }
609            // Check for memory leaks.
610            info!("Additional static roots: {:?}", ecx.machine.static_roots);
611            let leaks = ecx.take_leaked_allocations(|ecx| &ecx.machine.static_roots);
612            if !leaks.is_empty() {
613                report_leaks(&ecx, leaks);
614                tcx.dcx().note("set `MIRIFLAGS=-Zmiri-ignore-leaks` to disable this check");
615                // Ignore the provided return code - let the reported error
616                // determine the return code.
617                break 'miri_error;
618            }
619        }
620
621        // The interpreter has not reported an error.
622        // (There could still be errors in the session if there are other interpreters.)
623        return match NonZeroI32::new(return_code) {
624            None => Ok(()),
625            Some(return_code) => Err(return_code),
626        };
627    }
628
629    // The interpreter reported an error.
630    assert!(tcx.dcx().has_errors().is_some());
631    Err(NonZeroI32::new(rustc_driver::EXIT_FAILURE).unwrap())
632}
633
634/// Turns an array of arguments into a Windows command line string.
635///
636/// The string will be UTF-16 encoded and NUL terminated.
637///
638/// Panics if the zeroth argument contains the `"` character because doublequotes
639/// in `argv[0]` cannot be encoded using the standard command line parsing rules.
640///
641/// Further reading:
642/// * [Parsing C++ command-line arguments](https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-160#parsing-c-command-line-arguments)
643/// * [The C/C++ Parameter Parsing Rules](https://daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULES)
644fn args_to_utf16_command_string<I, T>(mut args: I) -> Vec<u16>
645where
646    I: Iterator<Item = T>,
647    T: AsRef<str>,
648{
649    // Parse argv[0]. Slashes aren't escaped. Literal double quotes are not allowed.
650    let mut cmd = {
651        let Some(arg0) = args.next() else {
652            return vec![0];
653        };
654        let arg0 = arg0.as_ref();
655        if arg0.contains('"') {
656            panic!("argv[0] cannot contain a doublequote (\") character");
657        } else {
658            // Always surround argv[0] with quotes.
659            let mut s = String::new();
660            s.push('"');
661            s.push_str(arg0);
662            s.push('"');
663            s
664        }
665    };
666
667    // Build the other arguments.
668    for arg in args {
669        let arg = arg.as_ref();
670        cmd.push(' ');
671        if arg.is_empty() {
672            cmd.push_str("\"\"");
673        } else if !arg.bytes().any(|c| matches!(c, b'"' | b'\t' | b' ')) {
674            // No quote, tab, or space -- no escaping required.
675            cmd.push_str(arg);
676        } else {
677            // Spaces and tabs are escaped by surrounding them in quotes.
678            // Quotes are themselves escaped by using backslashes when in a
679            // quoted block.
680            // Backslashes only need to be escaped when one or more are directly
681            // followed by a quote. Otherwise they are taken literally.
682
683            cmd.push('"');
684            let mut chars = arg.chars().peekable();
685            loop {
686                let mut nslashes = 0;
687                while let Some(&'\\') = chars.peek() {
688                    chars.next();
689                    nslashes += 1;
690                }
691
692                match chars.next() {
693                    Some('"') => {
694                        cmd.extend(iter::repeat_n('\\', nslashes * 2 + 1));
695                        cmd.push('"');
696                    }
697                    Some(c) => {
698                        cmd.extend(iter::repeat_n('\\', nslashes));
699                        cmd.push(c);
700                    }
701                    None => {
702                        cmd.extend(iter::repeat_n('\\', nslashes * 2));
703                        break;
704                    }
705                }
706            }
707            cmd.push('"');
708        }
709    }
710
711    if cmd.contains('\0') {
712        panic!("interior null in command line arguments");
713    }
714    cmd.encode_utf16().chain(iter::once(0)).collect()
715}
716
717#[cfg(test)]
718mod tests {
719    use super::*;
720    #[test]
721    #[should_panic(expected = "argv[0] cannot contain a doublequote (\") character")]
722    fn windows_argv0_panic_on_quote() {
723        args_to_utf16_command_string(["\""].iter());
724    }
725    #[test]
726    fn windows_argv0_no_escape() {
727        // Ensure that a trailing backslash in argv[0] is not escaped.
728        let cmd = String::from_utf16_lossy(&args_to_utf16_command_string(
729            [r"C:\Program Files\", "arg1", "arg 2", "arg \" 3"].iter(),
730        ));
731        assert_eq!(cmd.trim_end_matches('\0'), r#""C:\Program Files\" arg1 "arg 2" "arg \" 3""#);
732    }
733}