1use std::num::NonZero;
2use std::sync::Mutex;
3use std::{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, MaybeResult, TyAndLayout};
14use rustc_middle::ty::{self, FnSigKind, IntTy, Ty, TyCtxt, UintTy};
15use rustc_session::config::CrateType;
16use rustc_span::{Span, Symbol};
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 let ty = resolve_path(cx.tcx(), path, Namespace::TypeNS).ty(cx.tcx(), cx.typing_env());
113 cx.layout_of(ty).to_result().ok().unwrap()
114}
115
116pub fn iter_exported_symbols<'tcx>(
118 tcx: TyCtxt<'tcx>,
119 mut f: impl FnMut(CrateNum, DefId) -> InterpResult<'tcx>,
120) -> InterpResult<'tcx> {
121 let crate_items = tcx.hir_crate_items(());
125 for def_id in crate_items.definitions() {
126 let exported = tcx.def_kind(def_id).has_codegen_attrs() && {
127 let codegen_attrs = tcx.codegen_fn_attrs(def_id);
128 codegen_attrs.contains_extern_indicator()
129 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
130 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
131 };
132 if exported {
133 f(LOCAL_CRATE, def_id.into())?;
134 }
135 }
136
137 let dependency_formats = tcx.dependency_formats(());
142 let dependency_format = dependency_formats
144 .get(&CrateType::Executable)
145 .expect("interpreting a non-executable crate");
146 for cnum in dependency_format
147 .iter_enumerated()
148 .filter_map(|(num, &linkage)| (linkage != Linkage::NotLinked).then_some(num))
149 {
150 if cnum == LOCAL_CRATE {
151 continue; }
153
154 for &(symbol, _export_info) in tcx.exported_non_generic_symbols(cnum) {
157 if let ExportedSymbol::NonGeneric(def_id) = symbol {
158 f(cnum, def_id)?;
159 }
160 }
161 }
162 interp_ok(())
163}
164
165impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
166pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
167 fn have_module(&self, path: &[&str]) -> bool {
169 try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some()
170 }
171
172 fn eval_path(&self, path: &[&str]) -> MPlaceTy<'tcx> {
174 let this = self.eval_context_ref();
175 let instance = resolve_path(*this.tcx, path, Namespace::ValueNS);
176 this.eval_global(instance).unwrap_or_else(|err| {
178 panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
179 })
180 }
181 fn eval_path_scalar(&self, path: &[&str]) -> Scalar {
182 let this = self.eval_context_ref();
183 let val = this.eval_path(path);
184 this.read_scalar(&val)
185 .unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}"))
186 }
187
188 fn eval_libc(&self, name: &str) -> Scalar {
190 if self.eval_context_ref().tcx.sess.target.os == Os::Windows {
191 panic!(
192 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
193 );
194 }
195 self.eval_path_scalar(&["libc", name])
196 }
197
198 fn eval_libc_i32(&self, name: &str) -> i32 {
200 self.eval_libc(name).to_i32().unwrap_or_else(|_err| {
202 panic!("required libc item has unexpected type (not `i32`): {name}")
203 })
204 }
205
206 fn eval_libc_u32(&self, name: &str) -> u32 {
208 self.eval_libc(name).to_u32().unwrap_or_else(|_err| {
210 panic!("required libc item has unexpected type (not `u32`): {name}")
211 })
212 }
213
214 fn eval_libc_u64(&self, name: &str) -> u64 {
216 self.eval_libc(name).to_u64().unwrap_or_else(|_err| {
218 panic!("required libc item has unexpected type (not `u64`): {name}")
219 })
220 }
221
222 fn eval_windows(&self, module: &str, name: &str) -> Scalar {
224 self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name])
225 }
226
227 fn eval_windows_u32(&self, module: &str, name: &str) -> u32 {
229 self.eval_windows(module, name).to_u32().unwrap_or_else(|_err| {
231 panic!("required Windows item has unexpected type (not `u32`): {module}::{name}")
232 })
233 }
234
235 fn eval_windows_u64(&self, module: &str, name: &str) -> u64 {
237 self.eval_windows(module, name).to_u64().unwrap_or_else(|_err| {
239 panic!("required Windows item has unexpected type (not `u64`): {module}::{name}")
240 })
241 }
242
243 fn libc_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
245 let this = self.eval_context_ref();
246 if this.tcx.sess.target.os == Os::Windows {
247 panic!(
248 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
249 );
250 }
251 path_ty_layout(this, &["libc", name])
252 }
253
254 fn windows_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
256 let this = self.eval_context_ref();
257 path_ty_layout(this, &["std", "sys", "pal", "windows", "c", name])
258 }
259
260 fn libc_array_ty_layout(&self, name: &str, size: u64) -> TyAndLayout<'tcx> {
262 let this = self.eval_context_ref();
263 let elem_ty_layout = this.libc_ty_layout(name);
264 let array_ty = Ty::new_array(*this.tcx, elem_ty_layout.ty, size);
265 this.layout_of(array_ty).unwrap()
266 }
267
268 fn try_project_field_named<P: Projectable<'tcx, Provenance>>(
270 &self,
271 base: &P,
272 name: &str,
273 ) -> InterpResult<'tcx, Option<P>> {
274 let this = self.eval_context_ref();
275 let adt = base.layout().ty.ty_adt_def().unwrap();
276 for (idx, field) in adt.non_enum_variant().fields.iter_enumerated() {
277 if field.name.as_str() == name {
278 return interp_ok(Some(this.project_field(base, idx)?));
279 }
280 }
281 interp_ok(None)
282 }
283
284 fn project_field_named<P: Projectable<'tcx, Provenance>>(
286 &self,
287 base: &P,
288 name: &str,
289 ) -> InterpResult<'tcx, P> {
290 interp_ok(
291 self.try_project_field_named(base, name)?
292 .unwrap_or_else(|| bug!("no field named {} in type {}", name, base.layout().ty)),
293 )
294 }
295
296 fn write_int(
300 &mut self,
301 i: impl Into<i128>,
302 dest: &impl Writeable<'tcx, Provenance>,
303 ) -> InterpResult<'tcx> {
304 assert!(
305 dest.layout().backend_repr.is_scalar(),
306 "write_int on non-scalar type {}",
307 dest.layout().ty
308 );
309 let val = if dest.layout().backend_repr.is_signed() {
310 Scalar::from_int(i, dest.layout().size)
311 } else {
312 Scalar::from_uint(u128::try_from(i.into()).unwrap(), dest.layout().size)
314 };
315 self.eval_context_mut().write_scalar(val, dest)
316 }
317
318 fn write_int_fields(
320 &mut self,
321 values: &[i128],
322 dest: &impl Writeable<'tcx, Provenance>,
323 ) -> InterpResult<'tcx> {
324 let this = self.eval_context_mut();
325 for (idx, &val) in values.iter().enumerate() {
326 let idx = FieldIdx::from_usize(idx);
327 let field = this.project_field(dest, idx)?;
328 this.write_int(val, &field)?;
329 }
330 interp_ok(())
331 }
332
333 fn write_int_fields_named(
335 &mut self,
336 values: &[(&str, i128)],
337 dest: &impl Writeable<'tcx, Provenance>,
338 ) -> InterpResult<'tcx> {
339 let this = self.eval_context_mut();
340 for &(name, val) in values.iter() {
341 let field = this.project_field_named(dest, name)?;
342 this.write_int(val, &field)?;
343 }
344 interp_ok(())
345 }
346
347 fn write_null(&mut self, dest: &impl Writeable<'tcx, Provenance>) -> InterpResult<'tcx> {
349 self.write_int(0, dest)
350 }
351
352 fn ptr_is_null(&self, ptr: Pointer) -> InterpResult<'tcx, bool> {
354 interp_ok(ptr.addr().bytes() == 0)
355 }
356
357 fn gen_random(&mut self, ptr: Pointer, len: u64) -> InterpResult<'tcx> {
359 if len == 0 {
365 return interp_ok(());
366 }
367 let this = self.eval_context_mut();
368
369 let mut data = vec![0; usize::try_from(len).unwrap()];
370
371 if this.machine.communicate() {
372 getrandom::fill(&mut data)
374 .map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?;
375 } else {
376 let rng = this.machine.rng.get_mut();
377 rng.fill_bytes(&mut data);
378 }
379
380 this.write_bytes_ptr(ptr, data.iter().copied())
381 }
382
383 fn call_function(
389 &mut self,
390 f: ty::Instance<'tcx>,
391 caller_abi: ExternAbi,
392 args: &[ImmTy<'tcx>],
393 dest: Option<&MPlaceTy<'tcx>>,
394 cont: ReturnContinuation,
395 ) -> InterpResult<'tcx> {
396 let this = self.eval_context_mut();
397
398 let mir = this.load_mir(f.def, None)?;
400 let dest = match dest {
401 Some(dest) => dest.clone(),
402 None => MPlaceTy::fake_alloc_zst(this.machine.layouts.unit),
403 };
404
405 let sig = this.tcx.mk_fn_sig(
407 args.iter().map(|a| a.layout.ty),
408 dest.layout.ty,
409 FnSigKind::default().set_abi(caller_abi).set_safety(rustc_hir::Safety::Safe),
410 );
411 let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?;
412
413 this.init_stack_frame(
415 f,
416 mir,
417 caller_fn_abi,
418 &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
419 false,
420 &dest.into(),
421 cont,
422 )
423 }
424
425 fn call_thread_root_function(
427 &mut self,
428 f: ty::Instance<'tcx>,
429 caller_abi: ExternAbi,
430 args: &[ImmTy<'tcx>],
431 dest: Option<&MPlaceTy<'tcx>>,
432 span: Span,
433 ) -> InterpResult<'tcx> {
434 let this = self.eval_context_mut();
435 assert!(this.active_thread_stack().is_empty());
436 assert!(this.active_thread_ref().origin_span.is_dummy());
437 this.active_thread_mut().origin_span = span;
438 this.call_function(f, caller_abi, args, dest, ReturnContinuation::Stop { cleanup: true })
439 }
440
441 fn visit_freeze_sensitive(
445 &self,
446 place: &MPlaceTy<'tcx>,
447 size: Size,
448 mut action: impl FnMut(AllocRange, bool) -> InterpResult<'tcx>,
449 ) -> InterpResult<'tcx> {
450 let this = self.eval_context_ref();
451 trace!("visit_frozen(place={:?}, size={:?})", *place, size);
452 debug_assert_eq!(
453 size,
454 this.size_and_align_of_val(place)?
455 .map(|(size, _)| size)
456 .unwrap_or_else(|| place.layout.size)
457 );
458 let start_addr = place.ptr().addr();
462 let mut cur_addr = start_addr;
463 let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer, unsafe_cell_size: Size| {
466 let unsafe_cell_addr = unsafe_cell_ptr.addr();
469 assert!(unsafe_cell_addr >= cur_addr);
470 let frozen_size = unsafe_cell_addr - cur_addr;
471 if frozen_size != Size::ZERO {
473 action(alloc_range(cur_addr - start_addr, frozen_size), true)?;
474 }
475 cur_addr += frozen_size;
476 if unsafe_cell_size != Size::ZERO {
478 action(
479 alloc_range(cur_addr - start_addr, unsafe_cell_size),
480 false,
481 )?;
482 }
483 cur_addr += unsafe_cell_size;
484 interp_ok(())
486 };
487 {
489 let mut visitor = UnsafeCellVisitor {
490 ecx: this,
491 unsafe_cell_action: |place| {
492 trace!("unsafe_cell_action on {:?}", place.ptr());
493 let unsafe_cell_size = this
495 .size_and_align_of_val(place)?
496 .map(|(size, _)| size)
497 .unwrap_or_else(|| place.layout.size);
499 if unsafe_cell_size != Size::ZERO {
501 unsafe_cell_action(&place.ptr(), unsafe_cell_size)
502 } else {
503 interp_ok(())
504 }
505 },
506 };
507 visitor.visit_value(place)?;
508 }
509 unsafe_cell_action(&place.ptr().wrapping_offset(size, this), Size::ZERO)?;
512 return interp_ok(());
514
515 struct UnsafeCellVisitor<'ecx, 'tcx, F>
518 where
519 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
520 {
521 ecx: &'ecx MiriInterpCx<'tcx>,
522 unsafe_cell_action: F,
523 }
524
525 impl<'ecx, 'tcx, F> ValueVisitor<'tcx, MiriMachine<'tcx>> for UnsafeCellVisitor<'ecx, 'tcx, F>
526 where
527 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
528 {
529 type V = MPlaceTy<'tcx>;
530
531 #[inline(always)]
532 fn ecx(&self) -> &MiriInterpCx<'tcx> {
533 self.ecx
534 }
535
536 fn visit_value(&mut self, v: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
538 trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
539 let is_unsafe_cell = match v.layout.ty.kind() {
540 ty::Adt(adt, _) =>
541 Some(adt.did()) == self.ecx.tcx.lang_items().unsafe_cell_type(),
542 _ => false,
543 };
544 if is_unsafe_cell {
545 (self.unsafe_cell_action)(v)
547 } else if self.ecx.type_is_freeze(v.layout.ty) {
548 interp_ok(())
550 } else if matches!(v.layout.fields, FieldsShape::Union(..)) {
551 (self.unsafe_cell_action)(v)
553 } else {
554 match v.layout.variants {
561 Variants::Multiple { .. } => {
562 (self.unsafe_cell_action)(v)
570 }
571 Variants::Single { .. } | Variants::Empty => {
572 self.walk_value(v)
575 }
576 }
577 }
578 }
579
580 fn visit_union(
581 &mut self,
582 _v: &MPlaceTy<'tcx>,
583 _fields: NonZero<usize>,
584 ) -> InterpResult<'tcx> {
585 bug!("we should have already handled unions in `visit_value`")
586 }
587 }
588 }
589
590 fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> {
594 if !self.eval_context_ref().machine.communicate() {
595 self.reject_in_isolation(name, RejectOpWith::Abort)?;
596 }
597 interp_ok(())
598 }
599
600 fn reject_in_isolation(&self, op_name: &str, reject_with: RejectOpWith) -> InterpResult<'tcx> {
603 let this = self.eval_context_ref();
604 match reject_with {
605 RejectOpWith::Abort => isolation_abort_error(op_name),
606 RejectOpWith::WarningWithoutBacktrace => {
607 static DEDUP: Mutex<FxHashSet<String>> =
609 Mutex::new(FxHashSet::with_hasher(FxBuildHasher));
610 let mut emitted_warnings = DEDUP.lock().unwrap();
611 if !emitted_warnings.contains(op_name) {
612 emitted_warnings.insert(op_name.to_owned());
614 this.tcx
615 .dcx()
616 .warn(format!("{op_name} was made to return an error due to isolation"));
617 }
618
619 interp_ok(())
620 }
621 RejectOpWith::Warning => {
622 this.emit_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string()));
623 interp_ok(())
624 }
625 RejectOpWith::NoWarning => interp_ok(()), }
627 }
628
629 fn assert_target_os(&self, target_os: Os, name: &str) {
633 assert_eq!(
634 self.eval_context_ref().tcx.sess.target.os,
635 target_os,
636 "`{name}` is only available on the `{target_os}` target OS",
637 )
638 }
639
640 fn check_target_os(&self, target_oses: &[Os], name: Symbol) -> InterpResult<'tcx> {
644 let target_os = &self.eval_context_ref().tcx.sess.target.os;
645 if !target_oses.contains(target_os) {
646 throw_unsup_format!("`{name}` is not supported on {target_os}");
647 }
648 interp_ok(())
649 }
650
651 fn assert_target_os_is_unix(&self, name: &str) {
655 assert!(self.target_os_is_unix(), "`{name}` is only available for unix targets",);
656 }
657
658 fn target_os_is_unix(&self) -> bool {
659 self.eval_context_ref().tcx.sess.target.families.iter().any(|f| f == "unix")
660 }
661
662 fn deref_pointer_as(
664 &self,
665 op: &impl Projectable<'tcx, Provenance>,
666 layout: TyAndLayout<'tcx>,
667 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
668 let this = self.eval_context_ref();
669 let ptr = this.read_pointer(op)?;
670 interp_ok(this.ptr_to_mplace(ptr, layout))
671 }
672
673 fn deref_pointer_and_offset(
675 &self,
676 op: &impl Projectable<'tcx, Provenance>,
677 offset: u64,
678 base_layout: TyAndLayout<'tcx>,
679 value_layout: TyAndLayout<'tcx>,
680 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
681 let this = self.eval_context_ref();
682 let op_place = this.deref_pointer_as(op, base_layout)?;
683 let offset = Size::from_bytes(offset);
684
685 assert!(base_layout.size >= offset + value_layout.size);
687 let value_place = op_place.offset(offset, value_layout, this)?;
688 interp_ok(value_place)
689 }
690
691 fn deref_pointer_and_read(
692 &self,
693 op: &impl Projectable<'tcx, Provenance>,
694 offset: u64,
695 base_layout: TyAndLayout<'tcx>,
696 value_layout: TyAndLayout<'tcx>,
697 ) -> InterpResult<'tcx, Scalar> {
698 let this = self.eval_context_ref();
699 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
700 this.read_scalar(&value_place)
701 }
702
703 fn deref_pointer_and_write(
704 &mut self,
705 op: &impl Projectable<'tcx, Provenance>,
706 offset: u64,
707 value: impl Into<Scalar>,
708 base_layout: TyAndLayout<'tcx>,
709 value_layout: TyAndLayout<'tcx>,
710 ) -> InterpResult<'tcx, ()> {
711 let this = self.eval_context_mut();
712 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
713 this.write_scalar(value, &value_place)
714 }
715
716 fn read_byte_slice<'a>(&'a self, slice: &ImmTy<'tcx>) -> InterpResult<'tcx, &'a [u8]>
718 where
719 'tcx: 'a,
720 {
721 let this = self.eval_context_ref();
722 let (ptr, len) = slice.to_scalar_pair();
723 let ptr = ptr.to_pointer(this)?;
724 let len = len.to_target_usize(this)?;
725 let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
726 interp_ok(bytes)
727 }
728
729 fn read_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a [u8]>
731 where
732 'tcx: 'a,
733 {
734 let this = self.eval_context_ref();
735 let size1 = Size::from_bytes(1);
736
737 let mut len = Size::ZERO;
739 loop {
740 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()?;
744 if byte == 0 {
745 break;
746 } else {
747 len += size1;
748 }
749 }
750
751 this.read_bytes_ptr_strip_provenance(ptr, len)
753 }
754
755 fn write_c_str(
761 &mut self,
762 c_str: &[u8],
763 ptr: Pointer,
764 size: u64,
765 ) -> InterpResult<'tcx, (bool, u64)> {
766 let string_length = u64::try_from(c_str.len()).unwrap();
769 let string_length = string_length.strict_add(1);
770 if size < string_length {
771 return interp_ok((false, string_length));
772 }
773 self.eval_context_mut()
774 .write_bytes_ptr(ptr, c_str.iter().copied().chain(iter::once(0u8)))?;
775 interp_ok((true, string_length))
776 }
777
778 fn read_c_str_with_char_size<T>(
781 &self,
782 mut ptr: Pointer,
783 size: Size,
784 align: Align,
785 ) -> InterpResult<'tcx, Vec<T>>
786 where
787 T: TryFrom<u128>,
788 <T as TryFrom<u128>>::Error: std::fmt::Debug,
789 {
790 assert_ne!(size, Size::ZERO);
791
792 let this = self.eval_context_ref();
793
794 this.check_ptr_align(ptr, align)?;
795
796 let mut wchars = Vec::new();
797 loop {
798 let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?;
802 if wchar_int == 0 {
803 break;
804 } else {
805 wchars.push(wchar_int.try_into().unwrap());
806 ptr = ptr.wrapping_offset(size, this);
807 }
808 }
809
810 interp_ok(wchars)
811 }
812
813 fn read_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u16>> {
815 self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap())
816 }
817
818 fn write_wide_str(
825 &mut self,
826 wide_str: &[u16],
827 ptr: Pointer,
828 size: u64,
829 ) -> InterpResult<'tcx, (bool, u64)> {
830 let string_length = u64::try_from(wide_str.len()).unwrap();
833 let string_length = string_length.strict_add(1);
834 if size < string_length {
835 return interp_ok((false, string_length));
836 }
837
838 let size2 = Size::from_bytes(2);
840 let this = self.eval_context_mut();
841 this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
842 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() {
844 let offset = u64::try_from(offset).unwrap();
845 alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?;
846 }
847 interp_ok((true, string_length))
848 }
849
850 fn read_wchar_t_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u32>> {
853 let this = self.eval_context_ref();
854 let wchar_t = if this.tcx.sess.target.os == Os::Windows {
855 this.machine.layouts.u16
857 } else {
858 this.libc_ty_layout("wchar_t")
859 };
860 self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi)
861 }
862
863 fn frame_in_std(&self) -> bool {
864 let this = self.eval_context_ref();
865 let frame = this.frame();
866 let instance: Option<_> = try {
868 let scope = frame.current_source_info()?.scope;
869 let inlined_parent = frame.body().source_scopes[scope].inlined_parent_scope?;
870 let source = &frame.body().source_scopes[inlined_parent];
871 source.inlined.expect("inlined_parent_scope points to scope without inline info").0
872 };
873 let instance = instance.unwrap_or(frame.instance());
875 let frame_crate = this.tcx.def_path(instance.def_id()).krate;
880 let crate_name = this.tcx.crate_name(frame_crate);
881 let crate_name = crate_name.as_str();
882 crate_name == "std"
883 }
884
885 fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) {
887 let this = self.eval_context_mut();
888 let provenance = mplace.ptr().into_pointer_or_addr().unwrap().provenance;
890 this.alloc_mark_immutable(provenance.get_alloc_id().unwrap()).unwrap();
891 }
892
893 fn get_twice_wide_int_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
895 let this = self.eval_context_ref();
896 match ty.kind() {
897 ty::Uint(UintTy::U8) => this.tcx.types.u16,
899 ty::Uint(UintTy::U16) => this.tcx.types.u32,
900 ty::Uint(UintTy::U32) => this.tcx.types.u64,
901 ty::Uint(UintTy::U64) => this.tcx.types.u128,
902 ty::Int(IntTy::I8) => this.tcx.types.i16,
904 ty::Int(IntTy::I16) => this.tcx.types.i32,
905 ty::Int(IntTy::I32) => this.tcx.types.i64,
906 ty::Int(IntTy::I64) => this.tcx.types.i128,
907 _ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"),
908 }
909 }
910
911 fn expect_target_feature_for_intrinsic(
916 &self,
917 intrinsic: Symbol,
918 target_feature: &str,
919 ) -> InterpResult<'tcx, ()> {
920 let this = self.eval_context_ref();
921 if !this.tcx.sess.unstable_target_features.contains(&Symbol::intern(target_feature)) {
922 throw_ub_format!(
923 "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}"
924 );
925 }
926 interp_ok(())
927 }
928
929 fn lookup_link_section(
932 &mut self,
933 include_name: impl Fn(&str) -> bool,
934 ) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> {
935 let this = self.eval_context_mut();
936 let tcx = this.tcx.tcx;
937
938 let mut array = vec![];
939
940 iter_exported_symbols(tcx, |_cnum, def_id| {
941 let attrs = tcx.codegen_fn_attrs(def_id);
942 let Some(link_section) = attrs.link_section else {
943 return interp_ok(());
944 };
945 if include_name(link_section.as_str()) {
946 let instance = ty::Instance::mono(tcx, def_id);
947 let span = tcx.def_span(def_id);
948 let const_val = this.eval_global(instance).unwrap_or_else(|err| {
949 panic!(
950 "failed to evaluate static in required link_section: {def_id:?}\n{err:?}"
951 )
952 });
953 match const_val.layout.ty.kind() {
954 ty::FnPtr(..) => {
955 array.push((this.read_immediate(&const_val)?, span));
956 }
957 ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => {
958 let mut elems = this.project_array_fields(&const_val)?;
959 while let Some((_idx, elem)) = elems.next(this)? {
960 array.push((this.read_immediate(&elem)?, span));
961 }
962 }
963 _ =>
964 throw_unsup_format!(
965 "only function pointers and arrays of function pointers are supported in well-known linker sections"
966 ),
967 }
968 }
969 interp_ok(())
970 })?;
971
972 interp_ok(array)
973 }
974
975 fn mangle_internal_symbol<'a>(&'a mut self, name: &'static str) -> &'a str
976 where
977 'tcx: 'a,
978 {
979 let this = self.eval_context_mut();
980 let tcx = *this.tcx;
981 this.machine
982 .mangle_internal_symbol_cache
983 .entry(name)
984 .or_insert_with(|| mangle_internal_symbol(tcx, name))
985 }
986}
987
988impl<'tcx> MiriMachine<'tcx> {
989 pub fn current_user_relevant_span(&self) -> Span {
994 self.threads.active_thread_ref().current_user_relevant_span()
995 }
996
997 pub fn caller_span(&self) -> Span {
1003 let frame_idx = self.top_user_relevant_frame().unwrap();
1006 let frame_idx = cmp::min(frame_idx, self.stack().len().saturating_sub(2));
1007 self.stack()[frame_idx].current_span()
1008 }
1009
1010 fn stack(&self) -> &[Frame<'tcx, Provenance, machine::FrameExtra<'tcx>>] {
1011 self.threads.active_thread_stack()
1012 }
1013
1014 fn top_user_relevant_frame(&self) -> Option<usize> {
1015 self.threads.active_thread_ref().top_user_relevant_frame()
1016 }
1017
1018 pub fn user_relevance(&self, frame: &Frame<'tcx, Provenance>) -> u8 {
1020 if frame.instance().def.requires_caller_location(self.tcx) {
1021 return 0;
1022 }
1023 if self.is_local(frame.instance()) {
1024 u8::MAX
1025 } else {
1026 1
1029 }
1030 }
1031}
1032
1033pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
1034 throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
1035 "{name} not available when isolation is enabled",
1036 )))
1037}
1038
1039pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar {
1040 let val = if b { -1 } else { 0 };
1044 Scalar::from_int(val, size)
1045}
1046
1047pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
1051 if success {
1052 u32::try_from(len.strict_sub(1)).unwrap()
1055 } else {
1056 u32::try_from(len).unwrap()
1059 }
1060}
1061
1062pub fn is_no_core(tcx: TyCtxt<'_>) -> bool {
1064 rustc_hir::find_attr!(tcx, crate, NoCore)
1065}
1066
1067pub trait ToUsize {
1069 fn to_usize(self) -> usize;
1070}
1071
1072impl ToUsize for u32 {
1073 fn to_usize(self) -> usize {
1074 self.try_into().unwrap()
1075 }
1076}
1077
1078pub trait ToU64 {
1081 fn to_u64(self) -> u64;
1082}
1083
1084impl ToU64 for usize {
1085 fn to_u64(self) -> u64 {
1086 self.try_into().unwrap()
1087 }
1088}
1089
1090#[macro_export]
1096macro_rules! enter_trace_span {
1097 ($($tt:tt)*) => {
1098 rustc_const_eval::enter_trace_span!($crate::MiriMachine<'static>, $($tt)*)
1099 };
1100}