]> git.lizzy.rs Git - rust.git/blob - src/tools/miri/src/machine.rs
Merge branch 'master' into patch-2
[rust.git] / src / tools / miri / src / machine.rs
1 //! Global machine state as well as implementation of the interpreter engine
2 //! `Machine` trait.
3
4 use std::borrow::Cow;
5 use std::cell::RefCell;
6 use std::fmt;
7
8 use rand::rngs::StdRng;
9 use rand::SeedableRng;
10
11 use rustc_ast::ast::Mutability;
12 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
13 #[allow(unused)]
14 use rustc_data_structures::static_assert_size;
15 use rustc_middle::{
16     mir,
17     ty::{
18         self,
19         layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout},
20         Instance, Ty, TyCtxt, TypeAndMut,
21     },
22 };
23 use rustc_span::def_id::{CrateNum, DefId};
24 use rustc_span::Symbol;
25 use rustc_target::abi::Size;
26 use rustc_target::spec::abi::Abi;
27
28 use crate::{
29     concurrency::{data_race, weak_memory},
30     shims::unix::FileHandler,
31     *,
32 };
33
34 // Some global facts about the emulated machine.
35 pub const PAGE_SIZE: u64 = 4 * 1024; // FIXME: adjust to target architecture
36 pub const STACK_ADDR: u64 = 32 * PAGE_SIZE; // not really about the "stack", but where we start assigning integer addresses to allocations
37 pub const STACK_SIZE: u64 = 16 * PAGE_SIZE; // whatever
38
39 /// Extra data stored with each stack frame
40 pub struct FrameData<'tcx> {
41     /// Extra data for Stacked Borrows.
42     pub stacked_borrows: Option<stacked_borrows::FrameExtra>,
43
44     /// If this is Some(), then this is a special "catch unwind" frame (the frame of `try_fn`
45     /// called by `try`). When this frame is popped during unwinding a panic,
46     /// we stop unwinding, use the `CatchUnwindData` to handle catching.
47     pub catch_unwind: Option<CatchUnwindData<'tcx>>,
48
49     /// If `measureme` profiling is enabled, holds timing information
50     /// for the start of this frame. When we finish executing this frame,
51     /// we use this to register a completed event with `measureme`.
52     pub timing: Option<measureme::DetachedTiming>,
53 }
54
55 impl<'tcx> std::fmt::Debug for FrameData<'tcx> {
56     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57         // Omitting `timing`, it does not support `Debug`.
58         let FrameData { stacked_borrows, catch_unwind, timing: _ } = self;
59         f.debug_struct("FrameData")
60             .field("stacked_borrows", stacked_borrows)
61             .field("catch_unwind", catch_unwind)
62             .finish()
63     }
64 }
65
66 impl VisitTags for FrameData<'_> {
67     fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
68         let FrameData { catch_unwind, stacked_borrows, timing: _ } = self;
69
70         catch_unwind.visit_tags(visit);
71         stacked_borrows.visit_tags(visit);
72     }
73 }
74
75 /// Extra memory kinds
76 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
77 pub enum MiriMemoryKind {
78     /// `__rust_alloc` memory.
79     Rust,
80     /// `malloc` memory.
81     C,
82     /// Windows `HeapAlloc` memory.
83     WinHeap,
84     /// Memory for args, errno, and other parts of the machine-managed environment.
85     /// This memory may leak.
86     Machine,
87     /// Memory allocated by the runtime (e.g. env vars). Separate from `Machine`
88     /// because we clean it up and leak-check it.
89     Runtime,
90     /// Globals copied from `tcx`.
91     /// This memory may leak.
92     Global,
93     /// Memory for extern statics.
94     /// This memory may leak.
95     ExternStatic,
96     /// Memory for thread-local statics.
97     /// This memory may leak.
98     Tls,
99 }
100
101 impl From<MiriMemoryKind> for MemoryKind<MiriMemoryKind> {
102     #[inline(always)]
103     fn from(kind: MiriMemoryKind) -> MemoryKind<MiriMemoryKind> {
104         MemoryKind::Machine(kind)
105     }
106 }
107
108 impl MayLeak for MiriMemoryKind {
109     #[inline(always)]
110     fn may_leak(self) -> bool {
111         use self::MiriMemoryKind::*;
112         match self {
113             Rust | C | WinHeap | Runtime => false,
114             Machine | Global | ExternStatic | Tls => true,
115         }
116     }
117 }
118
119 impl fmt::Display for MiriMemoryKind {
120     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121         use self::MiriMemoryKind::*;
122         match self {
123             Rust => write!(f, "Rust heap"),
124             C => write!(f, "C heap"),
125             WinHeap => write!(f, "Windows heap"),
126             Machine => write!(f, "machine-managed memory"),
127             Runtime => write!(f, "language runtime memory"),
128             Global => write!(f, "global (static or const)"),
129             ExternStatic => write!(f, "extern static"),
130             Tls => write!(f, "thread-local static"),
131         }
132     }
133 }
134
135 /// Pointer provenance.
136 #[derive(Debug, Clone, Copy)]
137 pub enum Provenance {
138     Concrete {
139         alloc_id: AllocId,
140         /// Stacked Borrows tag.
141         sb: SbTag,
142     },
143     Wildcard,
144 }
145
146 // This needs to be `Eq`+`Hash` because the `Machine` trait needs that because validity checking
147 // *might* be recursive and then it has to track which places have already been visited.
148 // However, comparing provenance is meaningless, since `Wildcard` might be any provenance -- and of
149 // course we don't actually do recursive checking.
150 // We could change `RefTracking` to strip provenance for its `seen` set but that type is generic so that is quite annoying.
151 // Instead owe add the required instances but make them panic.
152 impl PartialEq for Provenance {
153     fn eq(&self, _other: &Self) -> bool {
154         panic!("Provenance must not be compared")
155     }
156 }
157 impl Eq for Provenance {}
158 impl std::hash::Hash for Provenance {
159     fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {
160         panic!("Provenance must not be hashed")
161     }
162 }
163
164 /// The "extra" information a pointer has over a regular AllocId.
165 #[derive(Copy, Clone, PartialEq)]
166 pub enum ProvenanceExtra {
167     Concrete(SbTag),
168     Wildcard,
169 }
170
171 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
172 static_assert_size!(Pointer<Provenance>, 24);
173 // FIXME: this would with in 24bytes but layout optimizations are not smart enough
174 // #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
175 //static_assert_size!(Pointer<Option<Provenance>>, 24);
176 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
177 static_assert_size!(Scalar<Provenance>, 32);
178
179 impl interpret::Provenance for Provenance {
180     /// We use absolute addresses in the `offset` of a `Pointer<Provenance>`.
181     const OFFSET_IS_ADDR: bool = true;
182
183     /// We cannot err on partial overwrites, it happens too often in practice (due to unions).
184     const ERR_ON_PARTIAL_PTR_OVERWRITE: bool = false;
185
186     fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187         let (prov, addr) = ptr.into_parts(); // address is absolute
188         write!(f, "{:#x}", addr.bytes())?;
189
190         match prov {
191             Provenance::Concrete { alloc_id, sb } => {
192                 // Forward `alternate` flag to `alloc_id` printing.
193                 if f.alternate() {
194                     write!(f, "[{alloc_id:#?}]")?;
195                 } else {
196                     write!(f, "[{alloc_id:?}]")?;
197                 }
198                 // Print Stacked Borrows tag.
199                 write!(f, "{sb:?}")?;
200             }
201             Provenance::Wildcard => {
202                 write!(f, "[wildcard]")?;
203             }
204         }
205
206         Ok(())
207     }
208
209     fn get_alloc_id(self) -> Option<AllocId> {
210         match self {
211             Provenance::Concrete { alloc_id, .. } => Some(alloc_id),
212             Provenance::Wildcard => None,
213         }
214     }
215
216     fn join(left: Option<Self>, right: Option<Self>) -> Option<Self> {
217         match (left, right) {
218             // If both are the *same* concrete tag, that is the result.
219             (
220                 Some(Provenance::Concrete { alloc_id: left_alloc, sb: left_sb }),
221                 Some(Provenance::Concrete { alloc_id: right_alloc, sb: right_sb }),
222             ) if left_alloc == right_alloc && left_sb == right_sb => left,
223             // If one side is a wildcard, the best possible outcome is that it is equal to the other
224             // one, and we use that.
225             (Some(Provenance::Wildcard), o) | (o, Some(Provenance::Wildcard)) => o,
226             // Otherwise, fall back to `None`.
227             _ => None,
228         }
229     }
230 }
231
232 impl fmt::Debug for ProvenanceExtra {
233     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234         match self {
235             ProvenanceExtra::Concrete(pid) => write!(f, "{pid:?}"),
236             ProvenanceExtra::Wildcard => write!(f, "<wildcard>"),
237         }
238     }
239 }
240
241 impl ProvenanceExtra {
242     pub fn and_then<T>(self, f: impl FnOnce(SbTag) -> Option<T>) -> Option<T> {
243         match self {
244             ProvenanceExtra::Concrete(pid) => f(pid),
245             ProvenanceExtra::Wildcard => None,
246         }
247     }
248 }
249
250 /// Extra per-allocation data
251 #[derive(Debug, Clone)]
252 pub struct AllocExtra {
253     /// Stacked Borrows state is only added if it is enabled.
254     pub stacked_borrows: Option<stacked_borrows::AllocExtra>,
255     /// Data race detection via the use of a vector-clock,
256     ///  this is only added if it is enabled.
257     pub data_race: Option<data_race::AllocExtra>,
258     /// Weak memory emulation via the use of store buffers,
259     ///  this is only added if it is enabled.
260     pub weak_memory: Option<weak_memory::AllocExtra>,
261 }
262
263 impl VisitTags for AllocExtra {
264     fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
265         let AllocExtra { stacked_borrows, data_race, weak_memory } = self;
266
267         stacked_borrows.visit_tags(visit);
268         data_race.visit_tags(visit);
269         weak_memory.visit_tags(visit);
270     }
271 }
272
273 /// Precomputed layouts of primitive types
274 pub struct PrimitiveLayouts<'tcx> {
275     pub unit: TyAndLayout<'tcx>,
276     pub i8: TyAndLayout<'tcx>,
277     pub i16: TyAndLayout<'tcx>,
278     pub i32: TyAndLayout<'tcx>,
279     pub i64: TyAndLayout<'tcx>,
280     pub i128: TyAndLayout<'tcx>,
281     pub isize: TyAndLayout<'tcx>,
282     pub u8: TyAndLayout<'tcx>,
283     pub u16: TyAndLayout<'tcx>,
284     pub u32: TyAndLayout<'tcx>,
285     pub u64: TyAndLayout<'tcx>,
286     pub u128: TyAndLayout<'tcx>,
287     pub usize: TyAndLayout<'tcx>,
288     pub bool: TyAndLayout<'tcx>,
289     pub mut_raw_ptr: TyAndLayout<'tcx>,   // *mut ()
290     pub const_raw_ptr: TyAndLayout<'tcx>, // *const ()
291 }
292
293 impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
294     fn new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result<Self, LayoutError<'tcx>> {
295         let tcx = layout_cx.tcx;
296         let mut_raw_ptr = tcx.mk_ptr(TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Mut });
297         let const_raw_ptr = tcx.mk_ptr(TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not });
298         Ok(Self {
299             unit: layout_cx.layout_of(tcx.mk_unit())?,
300             i8: layout_cx.layout_of(tcx.types.i8)?,
301             i16: layout_cx.layout_of(tcx.types.i16)?,
302             i32: layout_cx.layout_of(tcx.types.i32)?,
303             i64: layout_cx.layout_of(tcx.types.i64)?,
304             i128: layout_cx.layout_of(tcx.types.i128)?,
305             isize: layout_cx.layout_of(tcx.types.isize)?,
306             u8: layout_cx.layout_of(tcx.types.u8)?,
307             u16: layout_cx.layout_of(tcx.types.u16)?,
308             u32: layout_cx.layout_of(tcx.types.u32)?,
309             u64: layout_cx.layout_of(tcx.types.u64)?,
310             u128: layout_cx.layout_of(tcx.types.u128)?,
311             usize: layout_cx.layout_of(tcx.types.usize)?,
312             bool: layout_cx.layout_of(tcx.types.bool)?,
313             mut_raw_ptr: layout_cx.layout_of(mut_raw_ptr)?,
314             const_raw_ptr: layout_cx.layout_of(const_raw_ptr)?,
315         })
316     }
317
318     pub fn uint(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
319         match size.bits() {
320             8 => Some(self.u8),
321             16 => Some(self.u16),
322             32 => Some(self.u32),
323             64 => Some(self.u64),
324             128 => Some(self.u128),
325             _ => None,
326         }
327     }
328
329     pub fn int(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
330         match size.bits() {
331             8 => Some(self.i8),
332             16 => Some(self.i16),
333             32 => Some(self.i32),
334             64 => Some(self.i64),
335             128 => Some(self.i128),
336             _ => None,
337         }
338     }
339 }
340
341 /// The machine itself.
342 ///
343 /// If you add anything here that stores machine values, remember to update
344 /// `visit_all_machine_values`!
345 pub struct MiriMachine<'mir, 'tcx> {
346     // We carry a copy of the global `TyCtxt` for convenience, so methods taking just `&Evaluator` have `tcx` access.
347     pub tcx: TyCtxt<'tcx>,
348
349     /// Stacked Borrows global data.
350     pub stacked_borrows: Option<stacked_borrows::GlobalState>,
351
352     /// Data race detector global data.
353     pub data_race: Option<data_race::GlobalState>,
354
355     /// Ptr-int-cast module global data.
356     pub intptrcast: intptrcast::GlobalState,
357
358     /// Environment variables set by `setenv`.
359     /// Miri does not expose env vars from the host to the emulated program.
360     pub(crate) env_vars: EnvVars<'tcx>,
361
362     /// Program arguments (`Option` because we can only initialize them after creating the ecx).
363     /// These are *pointers* to argc/argv because macOS.
364     /// We also need the full command line as one string because of Windows.
365     pub(crate) argc: Option<MemPlace<Provenance>>,
366     pub(crate) argv: Option<MemPlace<Provenance>>,
367     pub(crate) cmd_line: Option<MemPlace<Provenance>>,
368
369     /// TLS state.
370     pub(crate) tls: TlsData<'tcx>,
371
372     /// What should Miri do when an op requires communicating with the host,
373     /// such as accessing host env vars, random number generation, and
374     /// file system access.
375     pub(crate) isolated_op: IsolatedOp,
376
377     /// Whether to enforce the validity invariant.
378     pub(crate) validate: bool,
379
380     /// Whether to enforce [ABI](Abi) of function calls.
381     pub(crate) enforce_abi: bool,
382
383     /// The table of file descriptors.
384     pub(crate) file_handler: shims::unix::FileHandler,
385     /// The table of directory descriptors.
386     pub(crate) dir_handler: shims::unix::DirHandler,
387
388     /// This machine's monotone clock.
389     pub(crate) clock: Clock,
390
391     /// The set of threads.
392     pub(crate) threads: ThreadManager<'mir, 'tcx>,
393
394     /// Precomputed `TyLayout`s for primitive data types that are commonly used inside Miri.
395     pub(crate) layouts: PrimitiveLayouts<'tcx>,
396
397     /// Allocations that are considered roots of static memory (that may leak).
398     pub(crate) static_roots: Vec<AllocId>,
399
400     /// The `measureme` profiler used to record timing information about
401     /// the emulated program.
402     profiler: Option<measureme::Profiler>,
403     /// Used with `profiler` to cache the `StringId`s for event names
404     /// uesd with `measureme`.
405     string_cache: FxHashMap<String, measureme::StringId>,
406
407     /// Cache of `Instance` exported under the given `Symbol` name.
408     /// `None` means no `Instance` exported under the given name is found.
409     pub(crate) exported_symbols_cache: FxHashMap<Symbol, Option<Instance<'tcx>>>,
410
411     /// Whether to raise a panic in the context of the evaluated process when unsupported
412     /// functionality is encountered. If `false`, an error is propagated in the Miri application context
413     /// instead (default behavior)
414     pub(crate) panic_on_unsupported: bool,
415
416     /// Equivalent setting as RUST_BACKTRACE on encountering an error.
417     pub(crate) backtrace_style: BacktraceStyle,
418
419     /// Crates which are considered local for the purposes of error reporting.
420     pub(crate) local_crates: Vec<CrateNum>,
421
422     /// Mapping extern static names to their base pointer.
423     extern_statics: FxHashMap<Symbol, Pointer<Provenance>>,
424
425     /// The random number generator used for resolving non-determinism.
426     /// Needs to be queried by ptr_to_int, hence needs interior mutability.
427     pub(crate) rng: RefCell<StdRng>,
428
429     /// The allocation IDs to report when they are being allocated
430     /// (helps for debugging memory leaks and use after free bugs).
431     tracked_alloc_ids: FxHashSet<AllocId>,
432
433     /// Controls whether alignment of memory accesses is being checked.
434     pub(crate) check_alignment: AlignmentCheck,
435
436     /// Failure rate of compare_exchange_weak, between 0.0 and 1.0
437     pub(crate) cmpxchg_weak_failure_rate: f64,
438
439     /// Corresponds to -Zmiri-mute-stdout-stderr and doesn't write the output but acts as if it succeeded.
440     pub(crate) mute_stdout_stderr: bool,
441
442     /// Whether weak memory emulation is enabled
443     pub(crate) weak_memory: bool,
444
445     /// The probability of the active thread being preempted at the end of each basic block.
446     pub(crate) preemption_rate: f64,
447
448     /// If `Some`, we will report the current stack every N basic blocks.
449     pub(crate) report_progress: Option<u32>,
450     // The total number of blocks that have been executed.
451     pub(crate) basic_block_count: u64,
452
453     /// Handle of the optional shared object file for external functions.
454     #[cfg(target_os = "linux")]
455     pub external_so_lib: Option<(libloading::Library, std::path::PathBuf)>,
456     #[cfg(not(target_os = "linux"))]
457     pub external_so_lib: Option<!>,
458
459     /// Run a garbage collector for SbTags every N basic blocks.
460     pub(crate) gc_interval: u32,
461     /// The number of blocks that passed since the last SbTag GC pass.
462     pub(crate) since_gc: u32,
463     /// The number of CPUs to be reported by miri.
464     pub(crate) num_cpus: u32,
465 }
466
467 impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
468     pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Self {
469         let local_crates = helpers::get_local_crates(layout_cx.tcx);
470         let layouts =
471             PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
472         let profiler = config.measureme_out.as_ref().map(|out| {
473             measureme::Profiler::new(out).expect("Couldn't create `measureme` profiler")
474         });
475         let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
476         let stacked_borrows = config.stacked_borrows.then(|| {
477             RefCell::new(stacked_borrows::GlobalStateInner::new(
478                 config.tracked_pointer_tags.clone(),
479                 config.tracked_call_ids.clone(),
480                 config.retag_fields,
481             ))
482         });
483         let data_race = config.data_race_detector.then(|| data_race::GlobalState::new(config));
484         MiriMachine {
485             tcx: layout_cx.tcx,
486             stacked_borrows,
487             data_race,
488             intptrcast: RefCell::new(intptrcast::GlobalStateInner::new(config)),
489             // `env_vars` depends on a full interpreter so we cannot properly initialize it yet.
490             env_vars: EnvVars::default(),
491             argc: None,
492             argv: None,
493             cmd_line: None,
494             tls: TlsData::default(),
495             isolated_op: config.isolated_op,
496             validate: config.validate,
497             enforce_abi: config.check_abi,
498             file_handler: FileHandler::new(config.mute_stdout_stderr),
499             dir_handler: Default::default(),
500             layouts,
501             threads: ThreadManager::default(),
502             static_roots: Vec::new(),
503             profiler,
504             string_cache: Default::default(),
505             exported_symbols_cache: FxHashMap::default(),
506             panic_on_unsupported: config.panic_on_unsupported,
507             backtrace_style: config.backtrace_style,
508             local_crates,
509             extern_statics: FxHashMap::default(),
510             rng: RefCell::new(rng),
511             tracked_alloc_ids: config.tracked_alloc_ids.clone(),
512             check_alignment: config.check_alignment,
513             cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
514             mute_stdout_stderr: config.mute_stdout_stderr,
515             weak_memory: config.weak_memory_emulation,
516             preemption_rate: config.preemption_rate,
517             report_progress: config.report_progress,
518             basic_block_count: 0,
519             clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
520             #[cfg(target_os = "linux")]
521             external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| {
522                 let target_triple = layout_cx.tcx.sess.opts.target_triple.triple();
523                 // Check if host target == the session target.
524                 if env!("TARGET") != target_triple {
525                     panic!(
526                         "calling external C functions in linked .so file requires host and target to be the same: host={}, target={}",
527                         env!("TARGET"),
528                         target_triple,
529                     );
530                 }
531                 // Note: it is the user's responsibility to provide a correct SO file.
532                 // WATCH OUT: If an invalid/incorrect SO file is specified, this can cause
533                 // undefined behaviour in Miri itself!
534                 (
535                     unsafe {
536                         libloading::Library::new(lib_file_path)
537                             .expect("failed to read specified extern shared object file")
538                     },
539                     lib_file_path.clone(),
540                 )
541             }),
542             #[cfg(not(target_os = "linux"))]
543             external_so_lib: config.external_so_file.as_ref().map(|_| {
544                 panic!("loading external .so files is only supported on Linux")
545             }),
546             gc_interval: config.gc_interval,
547             since_gc: 0,
548             num_cpus: config.num_cpus,
549         }
550     }
551
552     pub(crate) fn late_init(
553         this: &mut MiriInterpCx<'mir, 'tcx>,
554         config: &MiriConfig,
555     ) -> InterpResult<'tcx> {
556         EnvVars::init(this, config)?;
557         MiriMachine::init_extern_statics(this)?;
558         ThreadManager::init(this);
559         Ok(())
560     }
561
562     fn add_extern_static(
563         this: &mut MiriInterpCx<'mir, 'tcx>,
564         name: &str,
565         ptr: Pointer<Option<Provenance>>,
566     ) {
567         // This got just allocated, so there definitely is a pointer here.
568         let ptr = ptr.into_pointer_or_addr().unwrap();
569         this.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
570     }
571
572     fn alloc_extern_static(
573         this: &mut MiriInterpCx<'mir, 'tcx>,
574         name: &str,
575         val: ImmTy<'tcx, Provenance>,
576     ) -> InterpResult<'tcx> {
577         let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?;
578         this.write_immediate(*val, &place.into())?;
579         Self::add_extern_static(this, name, place.ptr);
580         Ok(())
581     }
582
583     /// Sets up the "extern statics" for this machine.
584     fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
585         match this.tcx.sess.target.os.as_ref() {
586             "linux" => {
587                 // "environ"
588                 Self::add_extern_static(
589                     this,
590                     "environ",
591                     this.machine.env_vars.environ.unwrap().ptr,
592                 );
593                 // A couple zero-initialized pointer-sized extern statics.
594                 // Most of them are for weak symbols, which we all set to null (indicating that the
595                 // symbol is not supported, and triggering fallback code which ends up calling a
596                 // syscall that we do support).
597                 for name in &["__cxa_thread_atexit_impl", "getrandom", "statx", "__clock_gettime64"]
598                 {
599                     let val = ImmTy::from_int(0, this.machine.layouts.usize);
600                     Self::alloc_extern_static(this, name, val)?;
601                 }
602             }
603             "freebsd" => {
604                 // "environ"
605                 Self::add_extern_static(
606                     this,
607                     "environ",
608                     this.machine.env_vars.environ.unwrap().ptr,
609                 );
610             }
611             "android" => {
612                 // "signal"
613                 let layout = this.machine.layouts.const_raw_ptr;
614                 let dlsym = Dlsym::from_str("signal".as_bytes(), &this.tcx.sess.target.os)?
615                     .expect("`signal` must be an actual dlsym on android");
616                 let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
617                 let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout);
618                 Self::alloc_extern_static(this, "signal", val)?;
619                 // A couple zero-initialized pointer-sized extern statics.
620                 // Most of them are for weak symbols, which we all set to null (indicating that the
621                 // symbol is not supported, and triggering fallback code.)
622                 for name in &["bsd_signal"] {
623                     let val = ImmTy::from_int(0, this.machine.layouts.usize);
624                     Self::alloc_extern_static(this, name, val)?;
625                 }
626             }
627             "windows" => {
628                 // "_tls_used"
629                 // This is some obscure hack that is part of the Windows TLS story. It's a `u8`.
630                 let val = ImmTy::from_int(0, this.machine.layouts.u8);
631                 Self::alloc_extern_static(this, "_tls_used", val)?;
632             }
633             _ => {} // No "extern statics" supported on this target
634         }
635         Ok(())
636     }
637
638     pub(crate) fn communicate(&self) -> bool {
639         self.isolated_op == IsolatedOp::Allow
640     }
641
642     /// Check whether the stack frame that this `FrameInfo` refers to is part of a local crate.
643     pub(crate) fn is_local(&self, frame: &FrameInfo<'_>) -> bool {
644         let def_id = frame.instance.def_id();
645         def_id.is_local() || self.local_crates.contains(&def_id.krate)
646     }
647 }
648
649 impl VisitTags for MiriMachine<'_, '_> {
650     fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
651         #[rustfmt::skip]
652         let MiriMachine {
653             threads,
654             tls,
655             env_vars,
656             argc,
657             argv,
658             cmd_line,
659             extern_statics,
660             dir_handler,
661             stacked_borrows,
662             data_race,
663             intptrcast,
664             file_handler,
665             tcx: _,
666             isolated_op: _,
667             validate: _,
668             enforce_abi: _,
669             clock: _,
670             layouts: _,
671             static_roots: _,
672             profiler: _,
673             string_cache: _,
674             exported_symbols_cache: _,
675             panic_on_unsupported: _,
676             backtrace_style: _,
677             local_crates: _,
678             rng: _,
679             tracked_alloc_ids: _,
680             check_alignment: _,
681             cmpxchg_weak_failure_rate: _,
682             mute_stdout_stderr: _,
683             weak_memory: _,
684             preemption_rate: _,
685             report_progress: _,
686             basic_block_count: _,
687             external_so_lib: _,
688             gc_interval: _,
689             since_gc: _,
690             num_cpus: _,
691         } = self;
692
693         threads.visit_tags(visit);
694         tls.visit_tags(visit);
695         env_vars.visit_tags(visit);
696         dir_handler.visit_tags(visit);
697         file_handler.visit_tags(visit);
698         data_race.visit_tags(visit);
699         stacked_borrows.visit_tags(visit);
700         intptrcast.visit_tags(visit);
701         argc.visit_tags(visit);
702         argv.visit_tags(visit);
703         cmd_line.visit_tags(visit);
704         for ptr in extern_statics.values() {
705             ptr.visit_tags(visit);
706         }
707     }
708 }
709
710 /// A rustc InterpCx for Miri.
711 pub type MiriInterpCx<'mir, 'tcx> = InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>;
712
713 /// A little trait that's useful to be inherited by extension traits.
714 pub trait MiriInterpCxExt<'mir, 'tcx> {
715     fn eval_context_ref<'a>(&'a self) -> &'a MiriInterpCx<'mir, 'tcx>;
716     fn eval_context_mut<'a>(&'a mut self) -> &'a mut MiriInterpCx<'mir, 'tcx>;
717 }
718 impl<'mir, 'tcx> MiriInterpCxExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {
719     #[inline(always)]
720     fn eval_context_ref(&self) -> &MiriInterpCx<'mir, 'tcx> {
721         self
722     }
723     #[inline(always)]
724     fn eval_context_mut(&mut self) -> &mut MiriInterpCx<'mir, 'tcx> {
725         self
726     }
727 }
728
729 /// Machine hook implementations.
730 impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
731     type MemoryKind = MiriMemoryKind;
732     type ExtraFnVal = Dlsym;
733
734     type FrameExtra = FrameData<'tcx>;
735     type AllocExtra = AllocExtra;
736
737     type Provenance = Provenance;
738     type ProvenanceExtra = ProvenanceExtra;
739
740     type MemoryMap = MonoHashMap<
741         AllocId,
742         (MemoryKind<MiriMemoryKind>, Allocation<Provenance, Self::AllocExtra>),
743     >;
744
745     const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global);
746
747     const PANIC_ON_ALLOC_FAIL: bool = false;
748
749     #[inline(always)]
750     fn enforce_alignment(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
751         ecx.machine.check_alignment != AlignmentCheck::None
752     }
753
754     #[inline(always)]
755     fn use_addr_for_alignment_check(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
756         ecx.machine.check_alignment == AlignmentCheck::Int
757     }
758
759     #[inline(always)]
760     fn enforce_validity(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
761         ecx.machine.validate
762     }
763
764     #[inline(always)]
765     fn enforce_abi(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
766         ecx.machine.enforce_abi
767     }
768
769     #[inline(always)]
770     fn checked_binop_checks_overflow(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
771         ecx.tcx.sess.overflow_checks()
772     }
773
774     #[inline(always)]
775     fn find_mir_or_eval_fn(
776         ecx: &mut MiriInterpCx<'mir, 'tcx>,
777         instance: ty::Instance<'tcx>,
778         abi: Abi,
779         args: &[OpTy<'tcx, Provenance>],
780         dest: &PlaceTy<'tcx, Provenance>,
781         ret: Option<mir::BasicBlock>,
782         unwind: StackPopUnwind,
783     ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
784         ecx.find_mir_or_eval_fn(instance, abi, args, dest, ret, unwind)
785     }
786
787     #[inline(always)]
788     fn call_extra_fn(
789         ecx: &mut MiriInterpCx<'mir, 'tcx>,
790         fn_val: Dlsym,
791         abi: Abi,
792         args: &[OpTy<'tcx, Provenance>],
793         dest: &PlaceTy<'tcx, Provenance>,
794         ret: Option<mir::BasicBlock>,
795         _unwind: StackPopUnwind,
796     ) -> InterpResult<'tcx> {
797         ecx.call_dlsym(fn_val, abi, args, dest, ret)
798     }
799
800     #[inline(always)]
801     fn call_intrinsic(
802         ecx: &mut MiriInterpCx<'mir, 'tcx>,
803         instance: ty::Instance<'tcx>,
804         args: &[OpTy<'tcx, Provenance>],
805         dest: &PlaceTy<'tcx, Provenance>,
806         ret: Option<mir::BasicBlock>,
807         unwind: StackPopUnwind,
808     ) -> InterpResult<'tcx> {
809         ecx.call_intrinsic(instance, args, dest, ret, unwind)
810     }
811
812     #[inline(always)]
813     fn assert_panic(
814         ecx: &mut MiriInterpCx<'mir, 'tcx>,
815         msg: &mir::AssertMessage<'tcx>,
816         unwind: Option<mir::BasicBlock>,
817     ) -> InterpResult<'tcx> {
818         ecx.assert_panic(msg, unwind)
819     }
820
821     #[inline(always)]
822     fn abort(_ecx: &mut MiriInterpCx<'mir, 'tcx>, msg: String) -> InterpResult<'tcx, !> {
823         throw_machine_stop!(TerminationInfo::Abort(msg))
824     }
825
826     #[inline(always)]
827     fn binary_ptr_op(
828         ecx: &MiriInterpCx<'mir, 'tcx>,
829         bin_op: mir::BinOp,
830         left: &ImmTy<'tcx, Provenance>,
831         right: &ImmTy<'tcx, Provenance>,
832     ) -> InterpResult<'tcx, (Scalar<Provenance>, bool, Ty<'tcx>)> {
833         ecx.binary_ptr_op(bin_op, left, right)
834     }
835
836     fn thread_local_static_base_pointer(
837         ecx: &mut MiriInterpCx<'mir, 'tcx>,
838         def_id: DefId,
839     ) -> InterpResult<'tcx, Pointer<Provenance>> {
840         ecx.get_or_create_thread_local_alloc(def_id)
841     }
842
843     fn extern_static_base_pointer(
844         ecx: &MiriInterpCx<'mir, 'tcx>,
845         def_id: DefId,
846     ) -> InterpResult<'tcx, Pointer<Provenance>> {
847         let link_name = ecx.item_link_name(def_id);
848         if let Some(&ptr) = ecx.machine.extern_statics.get(&link_name) {
849             // Various parts of the engine rely on `get_alloc_info` for size and alignment
850             // information. That uses the type information of this static.
851             // Make sure it matches the Miri allocation for this.
852             let Provenance::Concrete { alloc_id, .. } = ptr.provenance else {
853                 panic!("extern_statics cannot contain wildcards")
854             };
855             let (shim_size, shim_align, _kind) = ecx.get_alloc_info(alloc_id);
856             let extern_decl_layout =
857                 ecx.tcx.layout_of(ty::ParamEnv::empty().and(ecx.tcx.type_of(def_id))).unwrap();
858             if extern_decl_layout.size != shim_size || extern_decl_layout.align.abi != shim_align {
859                 throw_unsup_format!(
860                     "`extern` static `{name}` from crate `{krate}` has been declared \
861                     with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
862                     but Miri emulates it via an extern static shim \
863                     with a size of {shim_size} bytes and alignment of {shim_align} bytes",
864                     name = ecx.tcx.def_path_str(def_id),
865                     krate = ecx.tcx.crate_name(def_id.krate),
866                     decl_size = extern_decl_layout.size.bytes(),
867                     decl_align = extern_decl_layout.align.abi.bytes(),
868                     shim_size = shim_size.bytes(),
869                     shim_align = shim_align.bytes(),
870                 )
871             }
872             Ok(ptr)
873         } else {
874             throw_unsup_format!(
875                 "`extern` static `{name}` from crate `{krate}` is not supported by Miri",
876                 name = ecx.tcx.def_path_str(def_id),
877                 krate = ecx.tcx.crate_name(def_id.krate),
878             )
879         }
880     }
881
882     fn adjust_allocation<'b>(
883         ecx: &MiriInterpCx<'mir, 'tcx>,
884         id: AllocId,
885         alloc: Cow<'b, Allocation>,
886         kind: Option<MemoryKind<Self::MemoryKind>>,
887     ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>> {
888         let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
889         if ecx.machine.tracked_alloc_ids.contains(&id) {
890             ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc(
891                 id,
892                 alloc.size(),
893                 alloc.align,
894                 kind,
895             ));
896         }
897
898         let alloc = alloc.into_owned();
899         let stacks = ecx.machine.stacked_borrows.as_ref().map(|stacked_borrows| {
900             Stacks::new_allocation(
901                 id,
902                 alloc.size(),
903                 stacked_borrows,
904                 kind,
905                 ecx.machine.current_span(),
906             )
907         });
908         let race_alloc = ecx.machine.data_race.as_ref().map(|data_race| {
909             data_race::AllocExtra::new_allocation(
910                 data_race,
911                 &ecx.machine.threads,
912                 alloc.size(),
913                 kind,
914             )
915         });
916         let buffer_alloc = ecx.machine.weak_memory.then(weak_memory::AllocExtra::new_allocation);
917         let alloc: Allocation<Provenance, Self::AllocExtra> = alloc.adjust_from_tcx(
918             &ecx.tcx,
919             AllocExtra {
920                 stacked_borrows: stacks.map(RefCell::new),
921                 data_race: race_alloc,
922                 weak_memory: buffer_alloc,
923             },
924             |ptr| ecx.global_base_pointer(ptr),
925         )?;
926         Ok(Cow::Owned(alloc))
927     }
928
929     fn adjust_alloc_base_pointer(
930         ecx: &MiriInterpCx<'mir, 'tcx>,
931         ptr: Pointer<AllocId>,
932     ) -> Pointer<Provenance> {
933         if cfg!(debug_assertions) {
934             // The machine promises to never call us on thread-local or extern statics.
935             let alloc_id = ptr.provenance;
936             match ecx.tcx.try_get_global_alloc(alloc_id) {
937                 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => {
938                     panic!("adjust_alloc_base_pointer called on thread-local static")
939                 }
940                 Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_foreign_item(def_id) => {
941                     panic!("adjust_alloc_base_pointer called on extern static")
942                 }
943                 _ => {}
944             }
945         }
946         let absolute_addr = intptrcast::GlobalStateInner::rel_ptr_to_addr(ecx, ptr);
947         let sb_tag = if let Some(stacked_borrows) = &ecx.machine.stacked_borrows {
948             stacked_borrows.borrow_mut().base_ptr_tag(ptr.provenance, &ecx.machine)
949         } else {
950             // Value does not matter, SB is disabled
951             SbTag::default()
952         };
953         Pointer::new(
954             Provenance::Concrete { alloc_id: ptr.provenance, sb: sb_tag },
955             Size::from_bytes(absolute_addr),
956         )
957     }
958
959     #[inline(always)]
960     fn ptr_from_addr_cast(
961         ecx: &MiriInterpCx<'mir, 'tcx>,
962         addr: u64,
963     ) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>> {
964         intptrcast::GlobalStateInner::ptr_from_addr_cast(ecx, addr)
965     }
966
967     fn expose_ptr(
968         ecx: &mut InterpCx<'mir, 'tcx, Self>,
969         ptr: Pointer<Self::Provenance>,
970     ) -> InterpResult<'tcx> {
971         match ptr.provenance {
972             Provenance::Concrete { alloc_id, sb } =>
973                 intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, sb),
974             Provenance::Wildcard => {
975                 // No need to do anything for wildcard pointers as
976                 // their provenances have already been previously exposed.
977                 Ok(())
978             }
979         }
980     }
981
982     /// Convert a pointer with provenance into an allocation-offset pair,
983     /// or a `None` with an absolute address if that conversion is not possible.
984     fn ptr_get_alloc(
985         ecx: &MiriInterpCx<'mir, 'tcx>,
986         ptr: Pointer<Self::Provenance>,
987     ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
988         let rel = intptrcast::GlobalStateInner::abs_ptr_to_rel(ecx, ptr);
989
990         rel.map(|(alloc_id, size)| {
991             let sb = match ptr.provenance {
992                 Provenance::Concrete { sb, .. } => ProvenanceExtra::Concrete(sb),
993                 Provenance::Wildcard => ProvenanceExtra::Wildcard,
994             };
995             (alloc_id, size, sb)
996         })
997     }
998
999     #[inline(always)]
1000     fn before_memory_read(
1001         _tcx: TyCtxt<'tcx>,
1002         machine: &Self,
1003         alloc_extra: &AllocExtra,
1004         (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1005         range: AllocRange,
1006     ) -> InterpResult<'tcx> {
1007         if let Some(data_race) = &alloc_extra.data_race {
1008             data_race.read(
1009                 alloc_id,
1010                 range,
1011                 machine.data_race.as_ref().unwrap(),
1012                 &machine.threads,
1013             )?;
1014         }
1015         if let Some(stacked_borrows) = &alloc_extra.stacked_borrows {
1016             stacked_borrows.borrow_mut().before_memory_read(
1017                 alloc_id,
1018                 prov_extra,
1019                 range,
1020                 machine.stacked_borrows.as_ref().unwrap(),
1021                 machine.current_span(),
1022                 &machine.threads,
1023             )?;
1024         }
1025         if let Some(weak_memory) = &alloc_extra.weak_memory {
1026             weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap());
1027         }
1028         Ok(())
1029     }
1030
1031     #[inline(always)]
1032     fn before_memory_write(
1033         _tcx: TyCtxt<'tcx>,
1034         machine: &mut Self,
1035         alloc_extra: &mut AllocExtra,
1036         (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1037         range: AllocRange,
1038     ) -> InterpResult<'tcx> {
1039         if let Some(data_race) = &mut alloc_extra.data_race {
1040             data_race.write(
1041                 alloc_id,
1042                 range,
1043                 machine.data_race.as_mut().unwrap(),
1044                 &machine.threads,
1045             )?;
1046         }
1047         if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
1048             stacked_borrows.get_mut().before_memory_write(
1049                 alloc_id,
1050                 prov_extra,
1051                 range,
1052                 machine.stacked_borrows.as_ref().unwrap(),
1053                 machine.current_span(),
1054                 &machine.threads,
1055             )?;
1056         }
1057         if let Some(weak_memory) = &alloc_extra.weak_memory {
1058             weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap());
1059         }
1060         Ok(())
1061     }
1062
1063     #[inline(always)]
1064     fn before_memory_deallocation(
1065         _tcx: TyCtxt<'tcx>,
1066         machine: &mut Self,
1067         alloc_extra: &mut AllocExtra,
1068         (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra),
1069         range: AllocRange,
1070     ) -> InterpResult<'tcx> {
1071         if machine.tracked_alloc_ids.contains(&alloc_id) {
1072             machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
1073         }
1074         if let Some(data_race) = &mut alloc_extra.data_race {
1075             data_race.deallocate(
1076                 alloc_id,
1077                 range,
1078                 machine.data_race.as_mut().unwrap(),
1079                 &machine.threads,
1080             )?;
1081         }
1082         if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
1083             stacked_borrows.get_mut().before_memory_deallocation(
1084                 alloc_id,
1085                 prove_extra,
1086                 range,
1087                 machine.stacked_borrows.as_ref().unwrap(),
1088                 machine.current_span(),
1089                 &machine.threads,
1090             )
1091         } else {
1092             Ok(())
1093         }
1094     }
1095
1096     #[inline(always)]
1097     fn retag(
1098         ecx: &mut InterpCx<'mir, 'tcx, Self>,
1099         kind: mir::RetagKind,
1100         place: &PlaceTy<'tcx, Provenance>,
1101     ) -> InterpResult<'tcx> {
1102         if ecx.machine.stacked_borrows.is_some() { ecx.retag(kind, place) } else { Ok(()) }
1103     }
1104
1105     #[inline(always)]
1106     fn init_frame_extra(
1107         ecx: &mut InterpCx<'mir, 'tcx, Self>,
1108         frame: Frame<'mir, 'tcx, Provenance>,
1109     ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>> {
1110         // Start recording our event before doing anything else
1111         let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
1112             let fn_name = frame.instance.to_string();
1113             let entry = ecx.machine.string_cache.entry(fn_name.clone());
1114             let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name));
1115
1116             Some(profiler.start_recording_interval_event_detached(
1117                 *name,
1118                 measureme::EventId::from_label(*name),
1119                 ecx.get_active_thread().to_u32(),
1120             ))
1121         } else {
1122             None
1123         };
1124
1125         let stacked_borrows = ecx.machine.stacked_borrows.as_ref();
1126
1127         let extra = FrameData {
1128             stacked_borrows: stacked_borrows.map(|sb| sb.borrow_mut().new_frame(&ecx.machine)),
1129             catch_unwind: None,
1130             timing,
1131         };
1132         Ok(frame.with_extra(extra))
1133     }
1134
1135     fn stack<'a>(
1136         ecx: &'a InterpCx<'mir, 'tcx, Self>,
1137     ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] {
1138         ecx.active_thread_stack()
1139     }
1140
1141     fn stack_mut<'a>(
1142         ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
1143     ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> {
1144         ecx.active_thread_stack_mut()
1145     }
1146
1147     fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
1148         ecx.machine.basic_block_count += 1u64; // a u64 that is only incremented by 1 will "never" overflow
1149         ecx.machine.since_gc += 1;
1150         // Possibly report our progress.
1151         if let Some(report_progress) = ecx.machine.report_progress {
1152             if ecx.machine.basic_block_count % u64::from(report_progress) == 0 {
1153                 ecx.emit_diagnostic(NonHaltingDiagnostic::ProgressReport {
1154                     block_count: ecx.machine.basic_block_count,
1155                 });
1156             }
1157         }
1158
1159         // Search for SbTags to find all live pointers, then remove all other tags from borrow
1160         // stacks.
1161         // When debug assertions are enabled, run the GC as often as possible so that any cases
1162         // where it mistakenly removes an important tag become visible.
1163         if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval {
1164             ecx.machine.since_gc = 0;
1165             ecx.garbage_collect_tags()?;
1166         }
1167
1168         // These are our preemption points.
1169         ecx.maybe_preempt_active_thread();
1170
1171         // Make sure some time passes.
1172         ecx.machine.clock.tick();
1173
1174         Ok(())
1175     }
1176
1177     #[inline(always)]
1178     fn after_stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
1179         if ecx.machine.stacked_borrows.is_some() { ecx.retag_return_place() } else { Ok(()) }
1180     }
1181
1182     #[inline(always)]
1183     fn after_stack_pop(
1184         ecx: &mut InterpCx<'mir, 'tcx, Self>,
1185         mut frame: Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>,
1186         unwinding: bool,
1187     ) -> InterpResult<'tcx, StackPopJump> {
1188         let timing = frame.extra.timing.take();
1189         if let Some(stacked_borrows) = &ecx.machine.stacked_borrows {
1190             stacked_borrows.borrow_mut().end_call(&frame.extra);
1191         }
1192         let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
1193         if let Some(profiler) = ecx.machine.profiler.as_ref() {
1194             profiler.finish_recording_interval_event(timing.unwrap());
1195         }
1196         res
1197     }
1198 }