1use std::num::NonZero;
2use std::sync::Mutex;
3use std::{assert_matches, cmp, iter};
4
5use rand::Rng;
6use rustc_abi::{Align, ExternAbi, FieldIdx, FieldsShape, Size, Variants};
7use rustc_data_structures::fx::{FxBuildHasher, FxHashSet};
8use rustc_hir::def::{DefKind, Namespace};
9use rustc_hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE};
10use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
11use rustc_middle::middle::dependency_format::Linkage;
12use rustc_middle::middle::exported_symbols::ExportedSymbol;
13use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
14use rustc_middle::ty::{self, FnSigKind, IntTy, Ty, TyCtxt, UintTy};
15use rustc_span::{Span, Symbol};
16use rustc_structures::CrateType;
17use rustc_symbol_mangling::mangle_internal_symbol;
18use rustc_target::spec::Os;
19
20use crate::*;
21
22fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option<Namespace>) -> Option<DefId> {
26 let _trace = enter_trace_span!("try_resolve_did", ?path);
27
28 fn find_children<'tcx: 'a, 'a>(
30 tcx: TyCtxt<'tcx>,
31 item: DefId,
32 name: &'a str,
33 ) -> impl Iterator<Item = DefId> + 'a {
34 let name = Symbol::intern(name);
35 tcx.module_children(item)
36 .iter()
37 .filter(move |item| item.ident.name == name)
38 .map(move |item| item.res.def_id())
39 }
40
41 let (&crate_name, path) = path.split_first().expect("paths must have at least one segment");
43 let (modules, item) = if let Some(namespace) = namespace {
44 let (&item_name, modules) =
45 path.split_last().expect("non-module paths must have at least 2 segments");
46 (modules, Some((item_name, namespace)))
47 } else {
48 (path, None)
49 };
50
51 'crates: for krate in
56 tcx.crates(()).iter().filter(|&&krate| tcx.crate_name(krate).as_str() == crate_name)
57 {
58 let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX };
59 for &segment in modules {
61 let Some(next_item) = find_children(tcx, cur_item, segment)
62 .find(|&item| tcx.def_kind(item) == DefKind::Mod)
63 else {
64 continue 'crates;
65 };
66 cur_item = next_item;
67 }
68 match item {
70 Some((item_name, namespace)) => {
71 let Some(item) = find_children(tcx, cur_item, item_name)
72 .find(|&item| tcx.def_kind(item).ns() == Some(namespace))
73 else {
74 continue 'crates;
75 };
76 return Some(item);
77 }
78 None => {
79 return Some(cur_item);
81 }
82 }
83 }
84 None
86}
87
88pub fn try_resolve_path<'tcx>(
90 tcx: TyCtxt<'tcx>,
91 path: &[&str],
92 namespace: Namespace,
93) -> Option<ty::Instance<'tcx>> {
94 let did = try_resolve_did(tcx, path, Some(namespace))?;
95 Some(ty::Instance::mono(tcx, did))
96}
97
98#[track_caller]
100pub fn resolve_path<'tcx>(
101 tcx: TyCtxt<'tcx>,
102 path: &[&str],
103 namespace: Namespace,
104) -> ty::Instance<'tcx> {
105 try_resolve_path(tcx, path, namespace)
106 .unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
107}
108
109#[track_caller]
111pub fn path_ty_layout<'tcx>(cx: &impl LayoutOf<'tcx>, path: &[&str]) -> TyAndLayout<'tcx> {
112 use rustc_middle::ty::layout::MaybeResult;
113
114 let ty = resolve_path(cx.tcx(), path, Namespace::TypeNS).ty(cx.tcx(), cx.typing_env());
115 cx.layout_of(ty).to_result().ok().unwrap()
116}
117
118pub fn iter_exported_symbols<'tcx>(
120 tcx: TyCtxt<'tcx>,
121 mut f: impl FnMut(CrateNum, DefId, bool) -> InterpResult<'tcx>,
122) -> InterpResult<'tcx> {
123 let crate_items = tcx.hir_crate_items(());
127 for def_id in crate_items.definitions() {
128 if !tcx.def_kind(def_id).has_codegen_attrs() || tcx.is_foreign_item(def_id) {
129 continue;
130 }
131 let codegen_attrs = tcx.codegen_fn_attrs(def_id);
132 let used = codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
133 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER);
134 if !(used || codegen_attrs.contains_extern_indicator()) {
135 continue;
136 }
137 f(LOCAL_CRATE, def_id.into(), used)?;
138 }
139
140 let dependency_formats = tcx.dependency_formats(());
144 let dependency_format = dependency_formats
146 .get(&CrateType::Executable)
147 .expect("interpreting a non-executable crate");
148 for cnum in dependency_format
149 .iter_enumerated()
150 .filter_map(|(num, &linkage)| (linkage != Linkage::NotLinked).then_some(num))
151 {
152 if cnum == LOCAL_CRATE {
153 continue; }
155
156 for &(symbol, export_info) in tcx.exported_non_generic_symbols(cnum) {
157 if let ExportedSymbol::NonGeneric(def_id) = symbol
158 && !tcx.is_foreign_item(def_id)
160 {
161 f(cnum, def_id, export_info.used)?;
162 }
163 }
164 }
165 interp_ok(())
166}
167
168impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
169pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
170 fn have_module(&self, path: &[&str]) -> bool {
172 try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some()
173 }
174
175 fn eval_path(&self, path: &[&str]) -> MPlaceTy<'tcx> {
177 let this = self.eval_context_ref();
178 let instance = resolve_path(*this.tcx, path, Namespace::ValueNS);
179 this.eval_global(instance).unwrap_or_else(|err| {
181 panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
182 })
183 }
184 fn eval_path_scalar(&self, path: &[&str]) -> Scalar {
185 let this = self.eval_context_ref();
186 let val = this.eval_path(path);
187 this.read_scalar(&val)
188 .unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}"))
189 }
190
191 fn eval_libc(&self, name: &str) -> Scalar {
193 if self.eval_context_ref().tcx.sess.target.os == Os::Windows {
194 panic!(
195 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
196 );
197 }
198 self.eval_path_scalar(&["libc", name])
199 }
200
201 fn eval_libc_i16(&self, name: &str) -> i16 {
203 self.eval_libc(name).to_i16().unwrap_or_else(|_err| {
205 panic!("required libc item has unexpected type (not `i16`): {name}")
206 })
207 }
208
209 fn eval_libc_u16(&self, name: &str) -> u16 {
211 self.eval_libc(name).to_u16().unwrap_or_else(|_err| {
213 panic!("required libc item has unexpected type (not `u16`): {name}")
214 })
215 }
216
217 fn eval_libc_i32(&self, name: &str) -> i32 {
219 self.eval_libc(name).to_i32().unwrap_or_else(|_err| {
221 panic!("required libc item has unexpected type (not `i32`): {name}")
222 })
223 }
224
225 fn eval_libc_u32(&self, name: &str) -> u32 {
227 self.eval_libc(name).to_u32().unwrap_or_else(|_err| {
229 panic!("required libc item has unexpected type (not `u32`): {name}")
230 })
231 }
232
233 fn eval_libc_u64(&self, name: &str) -> u64 {
235 self.eval_libc(name).to_u64().unwrap_or_else(|_err| {
237 panic!("required libc item has unexpected type (not `u64`): {name}")
238 })
239 }
240
241 fn eval_windows(&self, module: &str, name: &str) -> Scalar {
243 self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name])
244 }
245
246 fn eval_windows_u32(&self, module: &str, name: &str) -> u32 {
248 self.eval_windows(module, name).to_u32().unwrap_or_else(|_err| {
250 panic!("required Windows item has unexpected type (not `u32`): {module}::{name}")
251 })
252 }
253
254 fn eval_windows_u64(&self, module: &str, name: &str) -> u64 {
256 self.eval_windows(module, name).to_u64().unwrap_or_else(|_err| {
258 panic!("required Windows item has unexpected type (not `u64`): {module}::{name}")
259 })
260 }
261
262 fn libc_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
264 let this = self.eval_context_ref();
265 if this.tcx.sess.target.os == Os::Windows {
266 panic!(
267 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
268 );
269 }
270 path_ty_layout(this, &["libc", name])
271 }
272
273 fn windows_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
275 let this = self.eval_context_ref();
276 path_ty_layout(this, &["std", "sys", "pal", "windows", "c", name])
277 }
278
279 fn try_project_field_named<P: Projectable<'tcx, Provenance>>(
281 &self,
282 base: &P,
283 name: &str,
284 ) -> InterpResult<'tcx, Option<P>> {
285 let this = self.eval_context_ref();
286 let adt = base.layout().ty.ty_adt_def().unwrap();
287 for (idx, field) in adt.non_enum_variant().fields.iter_enumerated() {
288 if field.name.as_str() == name {
289 return interp_ok(Some(this.project_field(base, idx)?));
290 }
291 }
292 interp_ok(None)
293 }
294
295 fn project_field_named<P: Projectable<'tcx, Provenance>>(
297 &self,
298 base: &P,
299 name: &str,
300 ) -> InterpResult<'tcx, P> {
301 interp_ok(
302 self.try_project_field_named(base, name)?
303 .unwrap_or_else(|| bug!("no field named {} in type {}", name, base.layout().ty)),
304 )
305 }
306
307 fn project_c_array_field<P: Projectable<'tcx, Provenance>>(
310 &self,
311 base: &P,
312 idx: u64,
313 ) -> InterpResult<'tcx, P> {
314 let this = self.eval_context_ref();
315 let offset = base.layout().size * idx; base.offset(offset, base.layout(), this)
317 }
318
319 fn write_int(
323 &mut self,
324 i: impl Into<i128>,
325 dest: &impl Writeable<'tcx, Provenance>,
326 ) -> InterpResult<'tcx> {
327 assert!(
328 dest.layout().backend_repr.is_scalar(),
329 "write_int on non-scalar type {}",
330 dest.layout().ty
331 );
332 let val = if dest.layout().backend_repr.is_signed() {
333 Scalar::from_int(i, dest.layout().size)
334 } else {
335 Scalar::from_uint(u128::try_from(i.into()).unwrap(), dest.layout().size)
337 };
338 self.eval_context_mut().write_scalar(val, dest)
339 }
340
341 fn write_int_fields(
343 &mut self,
344 values: &[i128],
345 dest: &impl Writeable<'tcx, Provenance>,
346 ) -> InterpResult<'tcx> {
347 let this = self.eval_context_mut();
348 for (idx, &val) in values.iter().enumerate() {
349 let idx = FieldIdx::from_usize(idx);
350 let field = this.project_field(dest, idx)?;
351 this.write_int(val, &field)?;
352 }
353 interp_ok(())
354 }
355
356 fn write_int_fields_named(
358 &mut self,
359 values: &[(&str, i128)],
360 dest: &impl Writeable<'tcx, Provenance>,
361 ) -> InterpResult<'tcx> {
362 let this = self.eval_context_mut();
363 for &(name, val) in values.iter() {
364 let field = this.project_field_named(dest, name)?;
365 this.write_int(val, &field)?;
366 }
367 interp_ok(())
368 }
369
370 fn ptr_is_null(&self, ptr: Pointer) -> InterpResult<'tcx, bool> {
372 interp_ok(ptr.addr().bytes() == 0)
373 }
374
375 fn gen_random(&mut self, ptr: Pointer, len: u64) -> InterpResult<'tcx> {
377 if len == 0 {
383 return interp_ok(());
384 }
385 let this = self.eval_context_mut();
386
387 let mut data = vec![0; usize::try_from(len).unwrap()];
388
389 if this.machine.communicate() {
390 getrandom::fill(&mut data)
392 .map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?;
393 } else {
394 let rng = this.machine.rng.get_mut();
395 rng.fill_bytes(&mut data);
396 }
397
398 this.write_bytes_ptr(ptr, data.iter().copied())
399 }
400
401 fn call_function(
407 &mut self,
408 f: ty::Instance<'tcx>,
409 caller_abi: ExternAbi,
410 args: &[ImmTy<'tcx>],
411 dest: Option<&MPlaceTy<'tcx>>,
412 cont: ReturnContinuation,
413 ) -> InterpResult<'tcx> {
414 let this = self.eval_context_mut();
415
416 let mir = this.load_mir(f.def, None)?;
418 let dest = match dest {
419 Some(dest) => dest.clone(),
420 None => MPlaceTy::fake_alloc_zst(this.machine.layouts.unit),
421 };
422
423 let with_caller_location = f.def.requires_caller_location(*this.tcx);
424
425 let sig = this.tcx.mk_fn_sig(
427 args.iter()
428 .map(|a| a.layout.ty)
429 .chain(with_caller_location.then(|| this.tcx.caller_location_ty())),
430 dest.layout.ty,
431 FnSigKind::default().set_abi(caller_abi).set_safety(rustc_hir::Safety::Safe),
434 );
435 let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?;
436
437 this.init_stack_frame(
439 f,
440 mir,
441 caller_fn_abi,
442 &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
443 with_caller_location,
444 &dest.into(),
445 cont,
446 )
447 }
448
449 fn call_thread_root_function(
451 &mut self,
452 f: ty::Instance<'tcx>,
453 caller_abi: ExternAbi,
454 args: &[ImmTy<'tcx>],
455 dest: Option<&MPlaceTy<'tcx>>,
456 span: Span,
457 ) -> InterpResult<'tcx> {
458 let this = self.eval_context_mut();
459 assert!(this.active_thread_stack().is_empty());
460 assert!(this.active_thread_ref().origin_span.is_dummy());
461 this.active_thread_mut().origin_span = span;
462 this.call_function(f, caller_abi, args, dest, ReturnContinuation::Stop { cleanup: true })
463 }
464
465 fn visit_freeze_sensitive(
469 &self,
470 place: &MPlaceTy<'tcx>,
471 size: Size,
472 mut action: impl FnMut(AllocRange, bool) -> InterpResult<'tcx>,
473 ) -> InterpResult<'tcx> {
474 let ecx = self.eval_context_ref();
475 trace!("visit_frozen(place={:?}, size={:?})", *place, size);
476 debug_assert_eq!(
477 size,
478 ecx.size_and_align_of_val(place)?
479 .map(|(size, _)| size)
480 .unwrap_or_else(|| place.layout.size)
481 );
482 let start_addr = place.ptr().addr();
486 let mut cur_addr = start_addr;
487 let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer, unsafe_cell_size: Size| {
490 let unsafe_cell_addr = unsafe_cell_ptr.addr();
493 assert!(unsafe_cell_addr >= cur_addr);
494 let frozen_size = unsafe_cell_addr - cur_addr;
495 if frozen_size != Size::ZERO {
497 action(alloc_range(cur_addr - start_addr, frozen_size), true)?;
498 }
499 cur_addr += frozen_size;
500 if unsafe_cell_size != Size::ZERO {
502 action(
503 alloc_range(cur_addr - start_addr, unsafe_cell_size),
504 false,
505 )?;
506 }
507 cur_addr += unsafe_cell_size;
508 interp_ok(())
510 };
511 {
513 let mut visitor = UnsafeCellVisitor {
514 ecx,
515 unsafe_cell_action: |place| {
516 trace!("unsafe_cell_action on {:?}", place.ptr());
517 let unsafe_cell_size = ecx
519 .size_and_align_of_val(place)?
520 .map(|(size, _)| size)
521 .unwrap_or_else(|| place.layout.size);
523 if unsafe_cell_size != Size::ZERO {
525 unsafe_cell_action(&place.ptr(), unsafe_cell_size)
526 } else {
527 interp_ok(())
528 }
529 },
530 };
531 visitor.visit_value(place)?;
532 }
533 unsafe_cell_action(&place.ptr().wrapping_offset(size, ecx), Size::ZERO)?;
536 return interp_ok(());
538
539 struct UnsafeCellVisitor<'ecx, 'tcx, F>
542 where
543 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
544 {
545 ecx: &'ecx MiriInterpCx<'tcx>,
546 unsafe_cell_action: F,
547 }
548
549 impl<'ecx, 'tcx, F> ValueVisitor<'tcx, MiriMachine<'tcx>> for UnsafeCellVisitor<'ecx, 'tcx, F>
550 where
551 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
552 {
553 type V = MPlaceTy<'tcx>;
554
555 #[inline(always)]
556 fn ecx(&self) -> &MiriInterpCx<'tcx> {
557 self.ecx
558 }
559
560 fn visit_value(&mut self, v: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
562 trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
563 let is_unsafe_cell = match v.layout.ty.kind() {
564 ty::Adt(adt, _) =>
565 Some(adt.did()) == self.ecx.tcx.lang_items().unsafe_cell_type(),
566 _ => false,
567 };
568 if is_unsafe_cell {
569 (self.unsafe_cell_action)(v)
571 } else if self.ecx.type_is_freeze(v.layout.ty) {
572 interp_ok(())
574 } else if matches!(v.layout.fields, FieldsShape::Union(..)) {
575 (self.unsafe_cell_action)(v)
577 } else {
578 match v.layout.variants {
585 Variants::Multiple { .. } => {
586 (self.unsafe_cell_action)(v)
594 }
595 Variants::Single { .. } | Variants::Empty => {
596 self.walk_value(v)
599 }
600 }
601 }
602 }
603
604 fn visit_union(
605 &mut self,
606 _v: &MPlaceTy<'tcx>,
607 _fields: NonZero<usize>,
608 ) -> InterpResult<'tcx> {
609 bug!("we should have already handled unions in `visit_value`")
610 }
611 }
612 }
613
614 fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> {
618 if !self.eval_context_ref().machine.communicate() {
619 self.reject_in_isolation(name, RejectOpWith::Abort)?;
620 }
621 interp_ok(())
622 }
623
624 fn reject_in_isolation(&self, op_name: &str, reject_with: RejectOpWith) -> InterpResult<'tcx> {
627 let this = self.eval_context_ref();
628 match reject_with {
629 RejectOpWith::Abort => isolation_abort_error(op_name),
630 RejectOpWith::WarningWithoutBacktrace => {
631 static DEDUP: Mutex<FxHashSet<String>> =
633 Mutex::new(FxHashSet::with_hasher(FxBuildHasher));
634 let mut emitted_warnings = DEDUP.lock().unwrap();
635 if !emitted_warnings.contains(op_name) {
636 emitted_warnings.insert(op_name.to_owned());
638 this.tcx
639 .dcx()
640 .warn(format!("{op_name} was made to return an error due to isolation"));
641 }
642
643 interp_ok(())
644 }
645 RejectOpWith::Warning => {
646 this.emit_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string()));
647 interp_ok(())
648 }
649 RejectOpWith::NoWarning => interp_ok(()), }
651 }
652
653 fn assert_target_os(&self, target_os: Os, name: &str) {
657 assert_eq!(
658 self.eval_context_ref().tcx.sess.target.os,
659 target_os,
660 "`{name}` is only available on the `{target_os}` target OS",
661 )
662 }
663
664 fn assert_target_os_is_linux_like(&self, name: &str) {
665 assert_matches!(
666 self.eval_context_ref().tcx.sess.target.os,
667 Os::Linux | Os::Android,
668 "`{name}` is only available on Linux-like targets",
669 )
670 }
671
672 fn check_target_os(&self, target_oses: &[Os], name: Symbol) -> InterpResult<'tcx> {
676 let target_os = &self.eval_context_ref().tcx.sess.target.os;
677 if !target_oses.contains(target_os) {
678 throw_unsup_format!("`{name}` is not supported on {target_os}");
679 }
680 interp_ok(())
681 }
682
683 fn assert_target_os_is_unix(&self, name: &str) {
687 assert!(self.target_os_is_unix(), "`{name}` is only available for unix targets",);
688 }
689
690 fn target_os_is_unix(&self) -> bool {
691 self.eval_context_ref().tcx.sess.target.families.iter().any(|f| f == "unix")
692 }
693
694 fn read_byte_slice<'a>(&'a self, slice: &ImmTy<'tcx>) -> InterpResult<'tcx, &'a [u8]>
696 where
697 'tcx: 'a,
698 {
699 let this = self.eval_context_ref();
700 let (ptr, len) = slice.to_scalar_pair();
701 let ptr = ptr.to_pointer(this);
702 let len = len.to_target_usize(this)?;
703 let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
704 interp_ok(bytes)
705 }
706
707 fn read_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a [u8]>
709 where
710 'tcx: 'a,
711 {
712 let this = self.eval_context_ref();
713 let size1 = Size::from_bytes(1);
714
715 let mut len = Size::ZERO;
717 loop {
718 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()?;
722 if byte == 0 {
723 break;
724 } else {
725 len += size1;
726 }
727 }
728
729 this.read_bytes_ptr_strip_provenance(ptr, len)
731 }
732
733 fn write_c_str(
739 &mut self,
740 c_str: &[u8],
741 ptr: Pointer,
742 size: u64,
743 ) -> InterpResult<'tcx, (bool, u64)> {
744 let string_length = u64::try_from(c_str.len()).unwrap();
747 let string_length = string_length.strict_add(1);
748 if size < string_length {
749 return interp_ok((false, string_length));
750 }
751 self.eval_context_mut()
752 .write_bytes_ptr(ptr, c_str.iter().copied().chain(iter::once(0u8)))?;
753 interp_ok((true, string_length))
754 }
755
756 fn read_c_str_with_char_size<T>(
759 &self,
760 mut ptr: Pointer,
761 size: Size,
762 align: Align,
763 ) -> InterpResult<'tcx, Vec<T>>
764 where
765 T: TryFrom<u128>,
766 <T as TryFrom<u128>>::Error: std::fmt::Debug,
767 {
768 assert_ne!(size, Size::ZERO);
769
770 let this = self.eval_context_ref();
771
772 this.check_ptr_align(ptr, align)?;
773
774 let mut wchars = Vec::new();
775 loop {
776 let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?;
780 if wchar_int == 0 {
781 break;
782 } else {
783 wchars.push(wchar_int.try_into().unwrap());
784 ptr = ptr.wrapping_offset(size, this);
785 }
786 }
787
788 interp_ok(wchars)
789 }
790
791 fn read_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u16>> {
793 self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap())
794 }
795
796 fn write_wide_str(
803 &mut self,
804 wide_str: &[u16],
805 ptr: Pointer,
806 size: u64,
807 ) -> InterpResult<'tcx, (bool, u64)> {
808 let string_length = u64::try_from(wide_str.len()).unwrap();
811 let string_length = string_length.strict_add(1);
812 if size < string_length {
813 return interp_ok((false, string_length));
814 }
815
816 let size2 = Size::from_bytes(2);
818 let this = self.eval_context_mut();
819 this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
820 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() {
822 let offset = u64::try_from(offset).unwrap();
823 alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?;
824 }
825 interp_ok((true, string_length))
826 }
827
828 fn read_wchar_t_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u32>> {
831 let this = self.eval_context_ref();
832 let wchar_t = if this.tcx.sess.target.os == Os::Windows {
833 this.machine.layouts.u16
835 } else {
836 this.libc_ty_layout("wchar_t")
837 };
838 self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align)
839 }
840
841 fn frame_in_std(&self) -> bool {
842 let this = self.eval_context_ref();
843 let frame = this.frame();
844 let instance: Option<_> = try {
846 let scope = frame.current_source_info()?.scope;
847 let inlined_parent = frame.body().source_scopes[scope].inlined_parent_scope?;
848 let source = &frame.body().source_scopes[inlined_parent];
849 source.inlined.expect("inlined_parent_scope points to scope without inline info").0
850 };
851 let instance = instance.unwrap_or(frame.instance());
853 let frame_crate = this.tcx.def_path(instance.def_id()).krate;
858 let crate_name = this.tcx.crate_name(frame_crate);
859 let crate_name = crate_name.as_str();
860 crate_name == "std"
861 }
862
863 fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) {
865 let this = self.eval_context_mut();
866 let provenance = mplace.ptr().into_pointer_or_addr().unwrap().provenance;
868 this.alloc_mark_immutable(provenance.get_alloc_id().unwrap()).unwrap();
869 }
870
871 fn get_twice_wide_int_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
873 let this = self.eval_context_ref();
874 match ty.kind() {
875 ty::Uint(UintTy::U8) => this.tcx.types.u16,
877 ty::Uint(UintTy::U16) => this.tcx.types.u32,
878 ty::Uint(UintTy::U32) => this.tcx.types.u64,
879 ty::Uint(UintTy::U64) => this.tcx.types.u128,
880 ty::Int(IntTy::I8) => this.tcx.types.i16,
882 ty::Int(IntTy::I16) => this.tcx.types.i32,
883 ty::Int(IntTy::I32) => this.tcx.types.i64,
884 ty::Int(IntTy::I64) => this.tcx.types.i128,
885 _ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"),
886 }
887 }
888
889 fn expect_target_feature_for_intrinsic(
894 &self,
895 intrinsic: Symbol,
896 target_feature: &str,
897 ) -> InterpResult<'tcx, ()> {
898 let this = self.eval_context_ref();
899 if !this.tcx.sess.internal_target_features.contains(&Symbol::intern(target_feature)) {
900 throw_ub_format!(
901 "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}"
902 );
903 }
904 interp_ok(())
905 }
906
907 fn lookup_link_section(
910 &self,
911 include_name: impl Fn(&str) -> bool,
912 ) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> {
913 let this = self.eval_context_ref();
914 let tcx = this.tcx.tcx;
915
916 let mut array = vec![];
917
918 iter_exported_symbols(tcx, |_cnum, def_id, used| {
919 let attrs = tcx.codegen_fn_attrs(def_id);
920 if !used {
921 return interp_ok(());
924 }
925 let Some(link_section) = attrs.link_section else {
926 return interp_ok(());
927 };
928 if include_name(link_section.as_str()) {
929 let instance = ty::Instance::mono(tcx, def_id);
930 let span = tcx.def_span(def_id);
931 let const_val = this.eval_global(instance).unwrap_or_else(|err| {
932 panic!(
933 "failed to evaluate static in required link_section: {def_id:?}\n{err:?}"
934 )
935 });
936 match const_val.layout.ty.kind() {
937 ty::FnPtr(..) => {
938 array.push((this.read_immediate(&const_val)?, span));
939 }
940 ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => {
941 let mut elems = this.project_array_fields(&const_val)?;
942 while let Some((_idx, elem)) = elems.next(this)? {
943 array.push((this.read_immediate(&elem)?, span));
944 }
945 }
946 _ =>
947 throw_unsup_format!(
948 "only function pointers and arrays of function pointers are supported in well-known linker sections"
949 ),
950 }
951 }
952 interp_ok(())
953 })?;
954
955 interp_ok(array)
956 }
957
958 fn mangle_internal_symbol<'a>(&'a mut self, name: &'static str) -> &'a str
959 where
960 'tcx: 'a,
961 {
962 let this = self.eval_context_mut();
963 let tcx = *this.tcx;
964 this.machine
965 .mangle_internal_symbol_cache
966 .entry(name)
967 .or_insert_with(|| mangle_internal_symbol(tcx, name))
968 }
969}
970
971impl<'tcx> MiriMachine<'tcx> {
972 pub fn current_user_relevant_span(&self) -> Span {
977 self.threads.active_thread_ref().current_user_relevant_span()
978 }
979
980 pub fn caller_span(&self) -> Span {
986 let frame_idx = self.top_user_relevant_frame().unwrap();
989 let frame_idx = cmp::min(frame_idx, self.stack().len().saturating_sub(2));
990 self.stack()[frame_idx].current_span()
991 }
992
993 fn stack(&self) -> &[Frame<'tcx, Provenance, machine::FrameExtra<'tcx>>] {
994 self.threads.active_thread_stack()
995 }
996
997 fn top_user_relevant_frame(&self) -> Option<usize> {
998 self.threads.active_thread_ref().top_user_relevant_frame()
999 }
1000
1001 pub fn user_relevance(&self, frame: &Frame<'tcx, Provenance>) -> u8 {
1003 if frame.instance().def.requires_caller_location(self.tcx) {
1004 return 0;
1005 }
1006 if self.is_local(frame.instance()) {
1007 u8::MAX
1008 } else {
1009 1
1012 }
1013 }
1014}
1015
1016pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
1017 throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
1018 "{name} not available when isolation is enabled",
1019 )))
1020}
1021
1022pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar {
1023 let val = if b { -1 } else { 0 };
1027 Scalar::from_int(val, size)
1028}
1029
1030pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
1034 if success {
1035 u32::try_from(len.strict_sub(1)).unwrap()
1038 } else {
1039 u32::try_from(len).unwrap()
1042 }
1043}
1044
1045pub fn is_no_core(tcx: TyCtxt<'_>) -> bool {
1047 rustc_attr_ir::find_attr!(tcx, crate, NoCore)
1048}
1049
1050pub trait ToUsize {
1052 fn to_usize(self) -> usize;
1053}
1054
1055impl ToUsize for u32 {
1056 fn to_usize(self) -> usize {
1057 self.try_into().unwrap()
1058 }
1059}
1060
1061pub trait ToU64 {
1064 fn to_u64(self) -> u64;
1065}
1066
1067impl ToU64 for usize {
1068 fn to_u64(self) -> u64 {
1069 self.try_into().unwrap()
1070 }
1071}
1072
1073#[macro_export]
1079macro_rules! enter_trace_span {
1080 ($($tt:tt)*) => {
1081 rustc_const_eval::enter_trace_span!($crate::MiriMachine<'static>, $($tt)*)
1082 };
1083}