1use std::num::NonZero;
2use std::time::Duration;
3use std::{cmp, iter};
4
5use rand::RngCore;
6use rustc_abi::{Align, ExternAbi, FieldIdx, FieldsShape, Size, Variants};
7use rustc_apfloat::Float;
8use rustc_apfloat::ieee::{Double, Half, Quad, Single};
9use rustc_hir::Safety;
10use rustc_hir::def::{DefKind, Namespace};
11use rustc_hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE};
12use rustc_index::IndexVec;
13use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
14use rustc_middle::middle::dependency_format::Linkage;
15use rustc_middle::middle::exported_symbols::ExportedSymbol;
16use rustc_middle::ty::layout::{LayoutOf, MaybeResult, TyAndLayout};
17use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, UintTy};
18use rustc_session::config::CrateType;
19use rustc_span::{Span, Symbol};
20use rustc_symbol_mangling::mangle_internal_symbol;
21
22use crate::*;
23
24#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
26pub enum AccessKind {
27 Read,
28 Write,
29}
30
31fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option<Namespace>) -> Option<DefId> {
35 fn find_children<'tcx: 'a, 'a>(
37 tcx: TyCtxt<'tcx>,
38 item: DefId,
39 name: &'a str,
40 ) -> impl Iterator<Item = DefId> + 'a {
41 let name = Symbol::intern(name);
42 tcx.module_children(item)
43 .iter()
44 .filter(move |item| item.ident.name == name)
45 .map(move |item| item.res.def_id())
46 }
47
48 let (&crate_name, path) = path.split_first().expect("paths must have at least one segment");
50 let (modules, item) = if let Some(namespace) = namespace {
51 let (&item_name, modules) =
52 path.split_last().expect("non-module paths must have at least 2 segments");
53 (modules, Some((item_name, namespace)))
54 } else {
55 (path, None)
56 };
57
58 'crates: for krate in
63 tcx.crates(()).iter().filter(|&&krate| tcx.crate_name(krate).as_str() == crate_name)
64 {
65 let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX };
66 for &segment in modules {
68 let Some(next_item) = find_children(tcx, cur_item, segment)
69 .find(|item| tcx.def_kind(item) == DefKind::Mod)
70 else {
71 continue 'crates;
72 };
73 cur_item = next_item;
74 }
75 match item {
77 Some((item_name, namespace)) => {
78 let Some(item) = find_children(tcx, cur_item, item_name)
79 .find(|item| tcx.def_kind(item).ns() == Some(namespace))
80 else {
81 continue 'crates;
82 };
83 return Some(item);
84 }
85 None => {
86 return Some(cur_item);
88 }
89 }
90 }
91 None
93}
94
95pub fn try_resolve_path<'tcx>(
97 tcx: TyCtxt<'tcx>,
98 path: &[&str],
99 namespace: Namespace,
100) -> Option<ty::Instance<'tcx>> {
101 let did = try_resolve_did(tcx, path, Some(namespace))?;
102 Some(ty::Instance::mono(tcx, did))
103}
104
105#[track_caller]
107pub fn resolve_path<'tcx>(
108 tcx: TyCtxt<'tcx>,
109 path: &[&str],
110 namespace: Namespace,
111) -> ty::Instance<'tcx> {
112 try_resolve_path(tcx, path, namespace)
113 .unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
114}
115
116#[track_caller]
118pub fn path_ty_layout<'tcx>(cx: &impl LayoutOf<'tcx>, path: &[&str]) -> TyAndLayout<'tcx> {
119 let ty = resolve_path(cx.tcx(), path, Namespace::TypeNS).ty(cx.tcx(), cx.typing_env());
120 cx.layout_of(ty).to_result().ok().unwrap()
121}
122
123pub fn iter_exported_symbols<'tcx>(
125 tcx: TyCtxt<'tcx>,
126 mut f: impl FnMut(CrateNum, DefId) -> InterpResult<'tcx>,
127) -> InterpResult<'tcx> {
128 let crate_items = tcx.hir_crate_items(());
132 for def_id in crate_items.definitions() {
133 let exported = tcx.def_kind(def_id).has_codegen_attrs() && {
134 let codegen_attrs = tcx.codegen_fn_attrs(def_id);
135 codegen_attrs.contains_extern_indicator()
136 || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
137 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
138 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
139 };
140 if exported {
141 f(LOCAL_CRATE, def_id.into())?;
142 }
143 }
144
145 let dependency_formats = tcx.dependency_formats(());
150 let dependency_format = dependency_formats
152 .get(&CrateType::Executable)
153 .expect("interpreting a non-executable crate");
154 for cnum in dependency_format
155 .iter_enumerated()
156 .filter_map(|(num, &linkage)| (linkage != Linkage::NotLinked).then_some(num))
157 {
158 if cnum == LOCAL_CRATE {
159 continue; }
161
162 for &(symbol, _export_info) in tcx.exported_non_generic_symbols(cnum) {
165 if let ExportedSymbol::NonGeneric(def_id) = symbol {
166 f(cnum, def_id)?;
167 }
168 }
169 }
170 interp_ok(())
171}
172
173pub trait ToHost {
175 type HostFloat;
176 fn to_host(self) -> Self::HostFloat;
177}
178
179pub trait ToSoft {
181 type SoftFloat;
182 fn to_soft(self) -> Self::SoftFloat;
183}
184
185impl ToHost for rustc_apfloat::ieee::Double {
186 type HostFloat = f64;
187
188 fn to_host(self) -> Self::HostFloat {
189 f64::from_bits(self.to_bits().try_into().unwrap())
190 }
191}
192
193impl ToSoft for f64 {
194 type SoftFloat = rustc_apfloat::ieee::Double;
195
196 fn to_soft(self) -> Self::SoftFloat {
197 Float::from_bits(self.to_bits().into())
198 }
199}
200
201impl ToHost for rustc_apfloat::ieee::Single {
202 type HostFloat = f32;
203
204 fn to_host(self) -> Self::HostFloat {
205 f32::from_bits(self.to_bits().try_into().unwrap())
206 }
207}
208
209impl ToSoft for f32 {
210 type SoftFloat = rustc_apfloat::ieee::Single;
211
212 fn to_soft(self) -> Self::SoftFloat {
213 Float::from_bits(self.to_bits().into())
214 }
215}
216
217impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
218pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
219 fn have_module(&self, path: &[&str]) -> bool {
221 try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some()
222 }
223
224 fn eval_path(&self, path: &[&str]) -> MPlaceTy<'tcx> {
226 let this = self.eval_context_ref();
227 let instance = resolve_path(*this.tcx, path, Namespace::ValueNS);
228 this.eval_global(instance).unwrap_or_else(|err| {
230 panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
231 })
232 }
233 fn eval_path_scalar(&self, path: &[&str]) -> Scalar {
234 let this = self.eval_context_ref();
235 let val = this.eval_path(path);
236 this.read_scalar(&val)
237 .unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}"))
238 }
239
240 fn eval_libc(&self, name: &str) -> Scalar {
242 if self.eval_context_ref().tcx.sess.target.os == "windows" {
243 panic!(
244 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
245 );
246 }
247 self.eval_path_scalar(&["libc", name])
248 }
249
250 fn eval_libc_i32(&self, name: &str) -> i32 {
252 self.eval_libc(name).to_i32().unwrap_or_else(|_err| {
254 panic!("required libc item has unexpected type (not `i32`): {name}")
255 })
256 }
257
258 fn eval_libc_u32(&self, name: &str) -> u32 {
260 self.eval_libc(name).to_u32().unwrap_or_else(|_err| {
262 panic!("required libc item has unexpected type (not `u32`): {name}")
263 })
264 }
265
266 fn eval_libc_u64(&self, name: &str) -> u64 {
268 self.eval_libc(name).to_u64().unwrap_or_else(|_err| {
270 panic!("required libc item has unexpected type (not `u64`): {name}")
271 })
272 }
273
274 fn eval_windows(&self, module: &str, name: &str) -> Scalar {
276 self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name])
277 }
278
279 fn eval_windows_u32(&self, module: &str, name: &str) -> u32 {
281 self.eval_windows(module, name).to_u32().unwrap_or_else(|_err| {
283 panic!("required Windows item has unexpected type (not `u32`): {module}::{name}")
284 })
285 }
286
287 fn eval_windows_u64(&self, module: &str, name: &str) -> u64 {
289 self.eval_windows(module, name).to_u64().unwrap_or_else(|_err| {
291 panic!("required Windows item has unexpected type (not `u64`): {module}::{name}")
292 })
293 }
294
295 fn libc_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
297 let this = self.eval_context_ref();
298 if this.tcx.sess.target.os == "windows" {
299 panic!(
300 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
301 );
302 }
303 path_ty_layout(this, &["libc", name])
304 }
305
306 fn windows_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
308 let this = self.eval_context_ref();
309 path_ty_layout(this, &["std", "sys", "pal", "windows", "c", name])
310 }
311
312 fn libc_array_ty_layout(&self, name: &str, size: u64) -> TyAndLayout<'tcx> {
314 let this = self.eval_context_ref();
315 let elem_ty_layout = this.libc_ty_layout(name);
316 let array_ty = Ty::new_array(*this.tcx, elem_ty_layout.ty, size);
317 this.layout_of(array_ty).unwrap()
318 }
319
320 fn try_project_field_named<P: Projectable<'tcx, Provenance>>(
322 &self,
323 base: &P,
324 name: &str,
325 ) -> InterpResult<'tcx, Option<P>> {
326 let this = self.eval_context_ref();
327 let adt = base.layout().ty.ty_adt_def().unwrap();
328 for (idx, field) in adt.non_enum_variant().fields.iter_enumerated() {
329 if field.name.as_str() == name {
330 return interp_ok(Some(this.project_field(base, idx)?));
331 }
332 }
333 interp_ok(None)
334 }
335
336 fn project_field_named<P: Projectable<'tcx, Provenance>>(
338 &self,
339 base: &P,
340 name: &str,
341 ) -> InterpResult<'tcx, P> {
342 interp_ok(
343 self.try_project_field_named(base, name)?
344 .unwrap_or_else(|| bug!("no field named {} in type {}", name, base.layout().ty)),
345 )
346 }
347
348 fn write_int(
352 &mut self,
353 i: impl Into<i128>,
354 dest: &impl Writeable<'tcx, Provenance>,
355 ) -> InterpResult<'tcx> {
356 assert!(
357 dest.layout().backend_repr.is_scalar(),
358 "write_int on non-scalar type {}",
359 dest.layout().ty
360 );
361 let val = if dest.layout().backend_repr.is_signed() {
362 Scalar::from_int(i, dest.layout().size)
363 } else {
364 Scalar::from_uint(u128::try_from(i.into()).unwrap(), dest.layout().size)
366 };
367 self.eval_context_mut().write_scalar(val, dest)
368 }
369
370 fn write_int_fields(
372 &mut self,
373 values: &[i128],
374 dest: &impl Writeable<'tcx, Provenance>,
375 ) -> InterpResult<'tcx> {
376 let this = self.eval_context_mut();
377 for (idx, &val) in values.iter().enumerate() {
378 let idx = FieldIdx::from_usize(idx);
379 let field = this.project_field(dest, idx)?;
380 this.write_int(val, &field)?;
381 }
382 interp_ok(())
383 }
384
385 fn write_int_fields_named(
387 &mut self,
388 values: &[(&str, i128)],
389 dest: &impl Writeable<'tcx, Provenance>,
390 ) -> InterpResult<'tcx> {
391 let this = self.eval_context_mut();
392 for &(name, val) in values.iter() {
393 let field = this.project_field_named(dest, name)?;
394 this.write_int(val, &field)?;
395 }
396 interp_ok(())
397 }
398
399 fn write_null(&mut self, dest: &impl Writeable<'tcx, Provenance>) -> InterpResult<'tcx> {
401 self.write_int(0, dest)
402 }
403
404 fn ptr_is_null(&self, ptr: Pointer) -> InterpResult<'tcx, bool> {
406 interp_ok(ptr.addr().bytes() == 0)
407 }
408
409 fn gen_random(&mut self, ptr: Pointer, len: u64) -> InterpResult<'tcx> {
411 if len == 0 {
417 return interp_ok(());
418 }
419 let this = self.eval_context_mut();
420
421 let mut data = vec![0; usize::try_from(len).unwrap()];
422
423 if this.machine.communicate() {
424 getrandom::fill(&mut data)
426 .map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?;
427 } else {
428 let rng = this.machine.rng.get_mut();
429 rng.fill_bytes(&mut data);
430 }
431
432 this.write_bytes_ptr(ptr, data.iter().copied())
433 }
434
435 fn call_function(
441 &mut self,
442 f: ty::Instance<'tcx>,
443 caller_abi: ExternAbi,
444 args: &[ImmTy<'tcx>],
445 dest: Option<&MPlaceTy<'tcx>>,
446 cont: ReturnContinuation,
447 ) -> InterpResult<'tcx> {
448 let this = self.eval_context_mut();
449
450 let mir = this.load_mir(f.def, None)?;
452 let dest = match dest {
453 Some(dest) => dest.clone(),
454 None => MPlaceTy::fake_alloc_zst(this.machine.layouts.unit),
455 };
456
457 let sig = this.tcx.mk_fn_sig(
459 args.iter().map(|a| a.layout.ty),
460 dest.layout.ty,
461 false,
462 Safety::Safe,
463 caller_abi,
464 );
465 let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?;
466
467 this.init_stack_frame(
469 f,
470 mir,
471 caller_fn_abi,
472 &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
473 false,
474 &dest.into(),
475 cont,
476 )
477 }
478
479 fn visit_freeze_sensitive(
483 &self,
484 place: &MPlaceTy<'tcx>,
485 size: Size,
486 mut action: impl FnMut(AllocRange, bool) -> InterpResult<'tcx>,
487 ) -> InterpResult<'tcx> {
488 let this = self.eval_context_ref();
489 trace!("visit_frozen(place={:?}, size={:?})", *place, size);
490 debug_assert_eq!(
491 size,
492 this.size_and_align_of_val(place)?
493 .map(|(size, _)| size)
494 .unwrap_or_else(|| place.layout.size)
495 );
496 let start_addr = place.ptr().addr();
500 let mut cur_addr = start_addr;
501 let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer, unsafe_cell_size: Size| {
504 let unsafe_cell_addr = unsafe_cell_ptr.addr();
507 assert!(unsafe_cell_addr >= cur_addr);
508 let frozen_size = unsafe_cell_addr - cur_addr;
509 if frozen_size != Size::ZERO {
511 action(alloc_range(cur_addr - start_addr, frozen_size), true)?;
512 }
513 cur_addr += frozen_size;
514 if unsafe_cell_size != Size::ZERO {
516 action(
517 alloc_range(cur_addr - start_addr, unsafe_cell_size),
518 false,
519 )?;
520 }
521 cur_addr += unsafe_cell_size;
522 interp_ok(())
524 };
525 {
527 let mut visitor = UnsafeCellVisitor {
528 ecx: this,
529 unsafe_cell_action: |place| {
530 trace!("unsafe_cell_action on {:?}", place.ptr());
531 let unsafe_cell_size = this
533 .size_and_align_of_val(place)?
534 .map(|(size, _)| size)
535 .unwrap_or_else(|| place.layout.size);
537 if unsafe_cell_size != Size::ZERO {
539 unsafe_cell_action(&place.ptr(), unsafe_cell_size)
540 } else {
541 interp_ok(())
542 }
543 },
544 };
545 visitor.visit_value(place)?;
546 }
547 unsafe_cell_action(&place.ptr().wrapping_offset(size, this), Size::ZERO)?;
550 return interp_ok(());
552
553 struct UnsafeCellVisitor<'ecx, 'tcx, F>
556 where
557 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
558 {
559 ecx: &'ecx MiriInterpCx<'tcx>,
560 unsafe_cell_action: F,
561 }
562
563 impl<'ecx, 'tcx, F> ValueVisitor<'tcx, MiriMachine<'tcx>> for UnsafeCellVisitor<'ecx, 'tcx, F>
564 where
565 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
566 {
567 type V = MPlaceTy<'tcx>;
568
569 #[inline(always)]
570 fn ecx(&self) -> &MiriInterpCx<'tcx> {
571 self.ecx
572 }
573
574 fn aggregate_field_iter(
575 memory_index: &IndexVec<FieldIdx, u32>,
576 ) -> impl Iterator<Item = FieldIdx> + 'static {
577 let inverse_memory_index = memory_index.invert_bijective_mapping();
578 inverse_memory_index.into_iter()
579 }
580
581 fn visit_value(&mut self, v: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
583 trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
584 let is_unsafe_cell = match v.layout.ty.kind() {
585 ty::Adt(adt, _) =>
586 Some(adt.did()) == self.ecx.tcx.lang_items().unsafe_cell_type(),
587 _ => false,
588 };
589 if is_unsafe_cell {
590 (self.unsafe_cell_action)(v)
592 } else if self.ecx.type_is_freeze(v.layout.ty) {
593 interp_ok(())
595 } else if matches!(v.layout.fields, FieldsShape::Union(..)) {
596 (self.unsafe_cell_action)(v)
598 } else {
599 match v.layout.variants {
606 Variants::Multiple { .. } => {
607 (self.unsafe_cell_action)(v)
615 }
616 Variants::Single { .. } | Variants::Empty => {
617 self.walk_value(v)
620 }
621 }
622 }
623 }
624
625 fn visit_union(
626 &mut self,
627 _v: &MPlaceTy<'tcx>,
628 _fields: NonZero<usize>,
629 ) -> InterpResult<'tcx> {
630 bug!("we should have already handled unions in `visit_value`")
631 }
632 }
633 }
634
635 fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> {
639 if !self.eval_context_ref().machine.communicate() {
640 self.reject_in_isolation(name, RejectOpWith::Abort)?;
641 }
642 interp_ok(())
643 }
644
645 fn reject_in_isolation(&self, op_name: &str, reject_with: RejectOpWith) -> InterpResult<'tcx> {
648 let this = self.eval_context_ref();
649 match reject_with {
650 RejectOpWith::Abort => isolation_abort_error(op_name),
651 RejectOpWith::WarningWithoutBacktrace => {
652 let mut emitted_warnings = this.machine.reject_in_isolation_warned.borrow_mut();
653 if !emitted_warnings.contains(op_name) {
654 emitted_warnings.insert(op_name.to_owned());
656 this.tcx
657 .dcx()
658 .warn(format!("{op_name} was made to return an error due to isolation"));
659 }
660
661 interp_ok(())
662 }
663 RejectOpWith::Warning => {
664 this.emit_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string()));
665 interp_ok(())
666 }
667 RejectOpWith::NoWarning => interp_ok(()), }
669 }
670
671 fn assert_target_os(&self, target_os: &str, name: &str) {
675 assert_eq!(
676 self.eval_context_ref().tcx.sess.target.os,
677 target_os,
678 "`{name}` is only available on the `{target_os}` target OS",
679 )
680 }
681
682 fn check_target_os(&self, target_oses: &[&str], name: Symbol) -> InterpResult<'tcx> {
686 let target_os = self.eval_context_ref().tcx.sess.target.os.as_ref();
687 if !target_oses.contains(&target_os) {
688 throw_unsup_format!("`{name}` is not supported on {target_os}");
689 }
690 interp_ok(())
691 }
692
693 fn assert_target_os_is_unix(&self, name: &str) {
697 assert!(self.target_os_is_unix(), "`{name}` is only available for unix targets",);
698 }
699
700 fn target_os_is_unix(&self) -> bool {
701 self.eval_context_ref().tcx.sess.target.families.iter().any(|f| f == "unix")
702 }
703
704 fn deref_pointer_as(
706 &self,
707 op: &impl Projectable<'tcx, Provenance>,
708 layout: TyAndLayout<'tcx>,
709 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
710 let this = self.eval_context_ref();
711 let ptr = this.read_pointer(op)?;
712 interp_ok(this.ptr_to_mplace(ptr, layout))
713 }
714
715 fn deref_pointer_and_offset(
717 &self,
718 op: &impl Projectable<'tcx, Provenance>,
719 offset: u64,
720 base_layout: TyAndLayout<'tcx>,
721 value_layout: TyAndLayout<'tcx>,
722 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
723 let this = self.eval_context_ref();
724 let op_place = this.deref_pointer_as(op, base_layout)?;
725 let offset = Size::from_bytes(offset);
726
727 assert!(base_layout.size >= offset + value_layout.size);
729 let value_place = op_place.offset(offset, value_layout, this)?;
730 interp_ok(value_place)
731 }
732
733 fn deref_pointer_and_read(
734 &self,
735 op: &impl Projectable<'tcx, Provenance>,
736 offset: u64,
737 base_layout: TyAndLayout<'tcx>,
738 value_layout: TyAndLayout<'tcx>,
739 ) -> InterpResult<'tcx, Scalar> {
740 let this = self.eval_context_ref();
741 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
742 this.read_scalar(&value_place)
743 }
744
745 fn deref_pointer_and_write(
746 &mut self,
747 op: &impl Projectable<'tcx, Provenance>,
748 offset: u64,
749 value: impl Into<Scalar>,
750 base_layout: TyAndLayout<'tcx>,
751 value_layout: TyAndLayout<'tcx>,
752 ) -> InterpResult<'tcx, ()> {
753 let this = self.eval_context_mut();
754 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
755 this.write_scalar(value, &value_place)
756 }
757
758 fn read_timespec(&mut self, tp: &MPlaceTy<'tcx>) -> InterpResult<'tcx, Option<Duration>> {
762 let this = self.eval_context_mut();
763 let seconds_place = this.project_field(tp, FieldIdx::ZERO)?;
764 let seconds_scalar = this.read_scalar(&seconds_place)?;
765 let seconds = seconds_scalar.to_target_isize(this)?;
766 let nanoseconds_place = this.project_field(tp, FieldIdx::ONE)?;
767 let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?;
768 let nanoseconds = nanoseconds_scalar.to_target_isize(this)?;
769
770 interp_ok(
771 try {
772 let seconds: u64 = seconds.try_into().ok()?;
774 let nanoseconds: u32 = nanoseconds.try_into().ok()?;
776 if nanoseconds >= 1_000_000_000 {
777 None?
779 }
780 Duration::new(seconds, nanoseconds)
781 },
782 )
783 }
784
785 fn read_byte_slice<'a>(&'a self, slice: &ImmTy<'tcx>) -> InterpResult<'tcx, &'a [u8]>
787 where
788 'tcx: 'a,
789 {
790 let this = self.eval_context_ref();
791 let (ptr, len) = slice.to_scalar_pair();
792 let ptr = ptr.to_pointer(this)?;
793 let len = len.to_target_usize(this)?;
794 let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
795 interp_ok(bytes)
796 }
797
798 fn read_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a [u8]>
800 where
801 'tcx: 'a,
802 {
803 let this = self.eval_context_ref();
804 let size1 = Size::from_bytes(1);
805
806 let mut len = Size::ZERO;
808 loop {
809 let alloc = this.get_ptr_alloc(ptr.wrapping_offset(len, this), size1)?.unwrap(); let byte = alloc.read_integer(alloc_range(Size::ZERO, size1))?.to_u8()?;
813 if byte == 0 {
814 break;
815 } else {
816 len += size1;
817 }
818 }
819
820 this.read_bytes_ptr_strip_provenance(ptr, len)
822 }
823
824 fn write_c_str(
830 &mut self,
831 c_str: &[u8],
832 ptr: Pointer,
833 size: u64,
834 ) -> InterpResult<'tcx, (bool, u64)> {
835 let string_length = u64::try_from(c_str.len()).unwrap();
838 let string_length = string_length.strict_add(1);
839 if size < string_length {
840 return interp_ok((false, string_length));
841 }
842 self.eval_context_mut()
843 .write_bytes_ptr(ptr, c_str.iter().copied().chain(iter::once(0u8)))?;
844 interp_ok((true, string_length))
845 }
846
847 fn read_c_str_with_char_size<T>(
850 &self,
851 mut ptr: Pointer,
852 size: Size,
853 align: Align,
854 ) -> InterpResult<'tcx, Vec<T>>
855 where
856 T: TryFrom<u128>,
857 <T as TryFrom<u128>>::Error: std::fmt::Debug,
858 {
859 assert_ne!(size, Size::ZERO);
860
861 let this = self.eval_context_ref();
862
863 this.check_ptr_align(ptr, align)?;
864
865 let mut wchars = Vec::new();
866 loop {
867 let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?;
871 if wchar_int == 0 {
872 break;
873 } else {
874 wchars.push(wchar_int.try_into().unwrap());
875 ptr = ptr.wrapping_offset(size, this);
876 }
877 }
878
879 interp_ok(wchars)
880 }
881
882 fn read_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u16>> {
884 self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap())
885 }
886
887 fn write_wide_str(
894 &mut self,
895 wide_str: &[u16],
896 ptr: Pointer,
897 size: u64,
898 ) -> InterpResult<'tcx, (bool, u64)> {
899 let string_length = u64::try_from(wide_str.len()).unwrap();
902 let string_length = string_length.strict_add(1);
903 if size < string_length {
904 return interp_ok((false, string_length));
905 }
906
907 let size2 = Size::from_bytes(2);
909 let this = self.eval_context_mut();
910 this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
911 let mut alloc = this.get_ptr_alloc_mut(ptr, size2 * string_length)?.unwrap(); for (offset, wchar) in wide_str.iter().copied().chain(iter::once(0x0000)).enumerate() {
913 let offset = u64::try_from(offset).unwrap();
914 alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?;
915 }
916 interp_ok((true, string_length))
917 }
918
919 fn read_wchar_t_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u32>> {
922 let this = self.eval_context_ref();
923 let wchar_t = if this.tcx.sess.target.os == "windows" {
924 this.machine.layouts.u16
926 } else {
927 this.libc_ty_layout("wchar_t")
928 };
929 self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi)
930 }
931
932 fn frame_in_std(&self) -> bool {
933 let this = self.eval_context_ref();
934 let frame = this.frame();
935 let instance: Option<_> = try {
937 let scope = frame.current_source_info()?.scope;
938 let inlined_parent = frame.body().source_scopes[scope].inlined_parent_scope?;
939 let source = &frame.body().source_scopes[inlined_parent];
940 source.inlined.expect("inlined_parent_scope points to scope without inline info").0
941 };
942 let instance = instance.unwrap_or(frame.instance());
944 let frame_crate = this.tcx.def_path(instance.def_id()).krate;
949 let crate_name = this.tcx.crate_name(frame_crate);
950 let crate_name = crate_name.as_str();
951 crate_name == "std" || crate_name == "std_miri_test"
953 }
954
955 fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) {
957 let this = self.eval_context_mut();
958 let provenance = mplace.ptr().into_pointer_or_addr().unwrap().provenance;
960 this.alloc_mark_immutable(provenance.get_alloc_id().unwrap()).unwrap();
961 }
962
963 fn float_to_int_checked(
967 &self,
968 src: &ImmTy<'tcx>,
969 cast_to: TyAndLayout<'tcx>,
970 round: rustc_apfloat::Round,
971 ) -> InterpResult<'tcx, Option<ImmTy<'tcx>>> {
972 let this = self.eval_context_ref();
973
974 fn float_to_int_inner<'tcx, F: rustc_apfloat::Float>(
975 ecx: &MiriInterpCx<'tcx>,
976 src: F,
977 cast_to: TyAndLayout<'tcx>,
978 round: rustc_apfloat::Round,
979 ) -> (Scalar, rustc_apfloat::Status) {
980 let int_size = cast_to.layout.size;
981 match cast_to.ty.kind() {
982 ty::Uint(_) => {
984 let res = src.to_u128_r(int_size.bits_usize(), round, &mut false);
985 (Scalar::from_uint(res.value, int_size), res.status)
986 }
987 ty::Int(_) => {
989 let res = src.to_i128_r(int_size.bits_usize(), round, &mut false);
990 (Scalar::from_int(res.value, int_size), res.status)
991 }
992 _ =>
994 span_bug!(
995 ecx.cur_span(),
996 "attempted float-to-int conversion with non-int output type {}",
997 cast_to.ty,
998 ),
999 }
1000 }
1001
1002 let ty::Float(fty) = src.layout.ty.kind() else {
1003 bug!("float_to_int_checked: non-float input type {}", src.layout.ty)
1004 };
1005
1006 let (val, status) = match fty {
1007 FloatTy::F16 =>
1008 float_to_int_inner::<Half>(this, src.to_scalar().to_f16()?, cast_to, round),
1009 FloatTy::F32 =>
1010 float_to_int_inner::<Single>(this, src.to_scalar().to_f32()?, cast_to, round),
1011 FloatTy::F64 =>
1012 float_to_int_inner::<Double>(this, src.to_scalar().to_f64()?, cast_to, round),
1013 FloatTy::F128 =>
1014 float_to_int_inner::<Quad>(this, src.to_scalar().to_f128()?, cast_to, round),
1015 };
1016
1017 if status.intersects(
1018 rustc_apfloat::Status::INVALID_OP
1019 | rustc_apfloat::Status::OVERFLOW
1020 | rustc_apfloat::Status::UNDERFLOW,
1021 ) {
1022 interp_ok(None)
1025 } else {
1026 interp_ok(Some(ImmTy::from_scalar(val, cast_to)))
1029 }
1030 }
1031
1032 fn get_twice_wide_int_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
1034 let this = self.eval_context_ref();
1035 match ty.kind() {
1036 ty::Uint(UintTy::U8) => this.tcx.types.u16,
1038 ty::Uint(UintTy::U16) => this.tcx.types.u32,
1039 ty::Uint(UintTy::U32) => this.tcx.types.u64,
1040 ty::Uint(UintTy::U64) => this.tcx.types.u128,
1041 ty::Int(IntTy::I8) => this.tcx.types.i16,
1043 ty::Int(IntTy::I16) => this.tcx.types.i32,
1044 ty::Int(IntTy::I32) => this.tcx.types.i64,
1045 ty::Int(IntTy::I64) => this.tcx.types.i128,
1046 _ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"),
1047 }
1048 }
1049
1050 fn expect_target_feature_for_intrinsic(
1055 &self,
1056 intrinsic: Symbol,
1057 target_feature: &str,
1058 ) -> InterpResult<'tcx, ()> {
1059 let this = self.eval_context_ref();
1060 if !this.tcx.sess.unstable_target_features.contains(&Symbol::intern(target_feature)) {
1061 throw_ub_format!(
1062 "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}"
1063 );
1064 }
1065 interp_ok(())
1066 }
1067
1068 fn lookup_link_section(
1070 &mut self,
1071 include_name: impl Fn(&str) -> bool,
1072 ) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
1073 let this = self.eval_context_mut();
1074 let tcx = this.tcx.tcx;
1075
1076 let mut array = vec![];
1077
1078 iter_exported_symbols(tcx, |_cnum, def_id| {
1079 let attrs = tcx.codegen_fn_attrs(def_id);
1080 let Some(link_section) = attrs.link_section else {
1081 return interp_ok(());
1082 };
1083 if include_name(link_section.as_str()) {
1084 let instance = ty::Instance::mono(tcx, def_id);
1085 let const_val = this.eval_global(instance).unwrap_or_else(|err| {
1086 panic!(
1087 "failed to evaluate static in required link_section: {def_id:?}\n{err:?}"
1088 )
1089 });
1090 match const_val.layout.ty.kind() {
1091 ty::FnPtr(..) => {
1092 array.push(this.read_immediate(&const_val)?);
1093 }
1094 ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => {
1095 let mut elems = this.project_array_fields(&const_val)?;
1096 while let Some((_idx, elem)) = elems.next(this)? {
1097 array.push(this.read_immediate(&elem)?);
1098 }
1099 }
1100 _ =>
1101 throw_unsup_format!(
1102 "only function pointers and arrays of function pointers are supported in well-known linker sections"
1103 ),
1104 }
1105 }
1106 interp_ok(())
1107 })?;
1108
1109 interp_ok(array)
1110 }
1111
1112 fn mangle_internal_symbol<'a>(&'a mut self, name: &'static str) -> &'a str
1113 where
1114 'tcx: 'a,
1115 {
1116 let this = self.eval_context_mut();
1117 let tcx = *this.tcx;
1118 this.machine
1119 .mangle_internal_symbol_cache
1120 .entry(name)
1121 .or_insert_with(|| mangle_internal_symbol(tcx, name))
1122 }
1123}
1124
1125impl<'tcx> MiriMachine<'tcx> {
1126 pub fn current_span(&self) -> Span {
1131 self.threads.active_thread_ref().current_span()
1132 }
1133
1134 pub fn caller_span(&self) -> Span {
1140 let frame_idx = self.top_user_relevant_frame().unwrap();
1143 let frame_idx = cmp::min(frame_idx, self.stack().len().saturating_sub(2));
1144 self.stack()[frame_idx].current_span()
1145 }
1146
1147 fn stack(&self) -> &[Frame<'tcx, Provenance, machine::FrameExtra<'tcx>>] {
1148 self.threads.active_thread_stack()
1149 }
1150
1151 fn top_user_relevant_frame(&self) -> Option<usize> {
1152 self.threads.active_thread_ref().top_user_relevant_frame()
1153 }
1154
1155 pub fn is_user_relevant(&self, frame: &Frame<'tcx, Provenance>) -> bool {
1157 let def_id = frame.instance().def_id();
1158 (def_id.is_local() || self.local_crates.contains(&def_id.krate))
1159 && !frame.instance().def.requires_caller_location(self.tcx)
1160 }
1161}
1162
1163pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
1164 throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
1165 "{name} not available when isolation is enabled",
1166 )))
1167}
1168
1169pub fn get_local_crates(tcx: TyCtxt<'_>) -> Vec<CrateNum> {
1172 let local_crate_names = std::env::var("MIRI_LOCAL_CRATES")
1175 .map(|crates| crates.split(',').map(|krate| krate.to_string()).collect::<Vec<_>>())
1176 .unwrap_or_default();
1177 let mut local_crates = Vec::new();
1178 for &crate_num in tcx.crates(()) {
1179 let name = tcx.crate_name(crate_num);
1180 let name = name.as_str();
1181 if local_crate_names.iter().any(|local_name| local_name == name) {
1182 local_crates.push(crate_num);
1183 }
1184 }
1185 local_crates
1186}
1187
1188pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar {
1189 let val = if b { -1 } else { 0 };
1193 Scalar::from_int(val, size)
1194}
1195
1196pub(crate) fn simd_element_to_bool(elem: ImmTy<'_>) -> InterpResult<'_, bool> {
1197 assert!(
1198 matches!(elem.layout.ty.kind(), ty::Int(_) | ty::Uint(_)),
1199 "SIMD mask element type must be an integer, but this is `{}`",
1200 elem.layout.ty
1201 );
1202 let val = elem.to_scalar().to_int(elem.layout.size)?;
1203 interp_ok(match val {
1204 0 => false,
1205 -1 => true,
1206 _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
1207 })
1208}
1209
1210pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
1214 if success {
1215 u32::try_from(len.strict_sub(1)).unwrap()
1218 } else {
1219 u32::try_from(len).unwrap()
1222 }
1223}
1224
1225pub trait ToUsize {
1227 fn to_usize(self) -> usize;
1228}
1229
1230impl ToUsize for u32 {
1231 fn to_usize(self) -> usize {
1232 self.try_into().unwrap()
1233 }
1234}
1235
1236pub trait ToU64 {
1239 fn to_u64(self) -> u64;
1240}
1241
1242impl ToU64 for usize {
1243 fn to_u64(self) -> u64 {
1244 self.try_into().unwrap()
1245 }
1246}
1247
1248#[macro_export]
1254macro_rules! enter_trace_span {
1255 ($($tt:tt)*) => {
1256 rustc_const_eval::enter_trace_span!($crate::MiriMachine<'static>, $($tt)*)
1257 };
1258}