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