1use std::num::NonZero;
2use std::sync::Mutex;
3use std::time::Duration;
4use std::{cmp, iter};
5
6use rand::RngCore;
7use rustc_abi::{Align, ExternAbi, FieldIdx, FieldsShape, Size, Variants};
8use rustc_data_structures::fx::{FxBuildHasher, FxHashSet};
9use rustc_hir::def::{DefKind, Namespace};
10use rustc_hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE};
11use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
12use rustc_middle::middle::dependency_format::Linkage;
13use rustc_middle::middle::exported_symbols::ExportedSymbol;
14use rustc_middle::ty::layout::{LayoutOf, MaybeResult, TyAndLayout};
15use rustc_middle::ty::{self, FnSigKind, IntTy, Ty, TyCtxt, UintTy};
16use rustc_session::config::CrateType;
17use rustc_span::{Span, Symbol};
18use rustc_symbol_mangling::mangle_internal_symbol;
19use rustc_target::spec::Os;
20
21use crate::*;
22
23fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option<Namespace>) -> Option<DefId> {
27 let _trace = enter_trace_span!("try_resolve_did", ?path);
28
29 fn find_children<'tcx: 'a, 'a>(
31 tcx: TyCtxt<'tcx>,
32 item: DefId,
33 name: &'a str,
34 ) -> impl Iterator<Item = DefId> + 'a {
35 let name = Symbol::intern(name);
36 tcx.module_children(item)
37 .iter()
38 .filter(move |item| item.ident.name == name)
39 .map(move |item| item.res.def_id())
40 }
41
42 let (&crate_name, path) = path.split_first().expect("paths must have at least one segment");
44 let (modules, item) = if let Some(namespace) = namespace {
45 let (&item_name, modules) =
46 path.split_last().expect("non-module paths must have at least 2 segments");
47 (modules, Some((item_name, namespace)))
48 } else {
49 (path, None)
50 };
51
52 'crates: for krate in
57 tcx.crates(()).iter().filter(|&&krate| tcx.crate_name(krate).as_str() == crate_name)
58 {
59 let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX };
60 for &segment in modules {
62 let Some(next_item) = find_children(tcx, cur_item, segment)
63 .find(|&item| tcx.def_kind(item) == DefKind::Mod)
64 else {
65 continue 'crates;
66 };
67 cur_item = next_item;
68 }
69 match item {
71 Some((item_name, namespace)) => {
72 let Some(item) = find_children(tcx, cur_item, item_name)
73 .find(|&item| tcx.def_kind(item).ns() == Some(namespace))
74 else {
75 continue 'crates;
76 };
77 return Some(item);
78 }
79 None => {
80 return Some(cur_item);
82 }
83 }
84 }
85 None
87}
88
89pub fn try_resolve_path<'tcx>(
91 tcx: TyCtxt<'tcx>,
92 path: &[&str],
93 namespace: Namespace,
94) -> Option<ty::Instance<'tcx>> {
95 let did = try_resolve_did(tcx, path, Some(namespace))?;
96 Some(ty::Instance::mono(tcx, did))
97}
98
99#[track_caller]
101pub fn resolve_path<'tcx>(
102 tcx: TyCtxt<'tcx>,
103 path: &[&str],
104 namespace: Namespace,
105) -> ty::Instance<'tcx> {
106 try_resolve_path(tcx, path, namespace)
107 .unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
108}
109
110#[track_caller]
112pub fn path_ty_layout<'tcx>(cx: &impl LayoutOf<'tcx>, path: &[&str]) -> TyAndLayout<'tcx> {
113 let ty = resolve_path(cx.tcx(), path, Namespace::TypeNS).ty(cx.tcx(), cx.typing_env());
114 cx.layout_of(ty).to_result().ok().unwrap()
115}
116
117pub fn iter_exported_symbols<'tcx>(
119 tcx: TyCtxt<'tcx>,
120 mut f: impl FnMut(CrateNum, DefId) -> InterpResult<'tcx>,
121) -> InterpResult<'tcx> {
122 let crate_items = tcx.hir_crate_items(());
126 for def_id in crate_items.definitions() {
127 let exported = tcx.def_kind(def_id).has_codegen_attrs() && {
128 let codegen_attrs = tcx.codegen_fn_attrs(def_id);
129 codegen_attrs.contains_extern_indicator()
130 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
131 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
132 };
133 if exported {
134 f(LOCAL_CRATE, def_id.into())?;
135 }
136 }
137
138 let dependency_formats = tcx.dependency_formats(());
143 let dependency_format = dependency_formats
145 .get(&CrateType::Executable)
146 .expect("interpreting a non-executable crate");
147 for cnum in dependency_format
148 .iter_enumerated()
149 .filter_map(|(num, &linkage)| (linkage != Linkage::NotLinked).then_some(num))
150 {
151 if cnum == LOCAL_CRATE {
152 continue; }
154
155 for &(symbol, _export_info) in tcx.exported_non_generic_symbols(cnum) {
158 if let ExportedSymbol::NonGeneric(def_id) = symbol {
159 f(cnum, def_id)?;
160 }
161 }
162 }
163 interp_ok(())
164}
165
166impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
167pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
168 fn have_module(&self, path: &[&str]) -> bool {
170 try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some()
171 }
172
173 fn eval_path(&self, path: &[&str]) -> MPlaceTy<'tcx> {
175 let this = self.eval_context_ref();
176 let instance = resolve_path(*this.tcx, path, Namespace::ValueNS);
177 this.eval_global(instance).unwrap_or_else(|err| {
179 panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
180 })
181 }
182 fn eval_path_scalar(&self, path: &[&str]) -> Scalar {
183 let this = self.eval_context_ref();
184 let val = this.eval_path(path);
185 this.read_scalar(&val)
186 .unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}"))
187 }
188
189 fn eval_libc(&self, name: &str) -> Scalar {
191 if self.eval_context_ref().tcx.sess.target.os == Os::Windows {
192 panic!(
193 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
194 );
195 }
196 self.eval_path_scalar(&["libc", name])
197 }
198
199 fn eval_libc_i32(&self, name: &str) -> i32 {
201 self.eval_libc(name).to_i32().unwrap_or_else(|_err| {
203 panic!("required libc item has unexpected type (not `i32`): {name}")
204 })
205 }
206
207 fn eval_libc_u32(&self, name: &str) -> u32 {
209 self.eval_libc(name).to_u32().unwrap_or_else(|_err| {
211 panic!("required libc item has unexpected type (not `u32`): {name}")
212 })
213 }
214
215 fn eval_libc_u64(&self, name: &str) -> u64 {
217 self.eval_libc(name).to_u64().unwrap_or_else(|_err| {
219 panic!("required libc item has unexpected type (not `u64`): {name}")
220 })
221 }
222
223 fn eval_windows(&self, module: &str, name: &str) -> Scalar {
225 self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name])
226 }
227
228 fn eval_windows_u32(&self, module: &str, name: &str) -> u32 {
230 self.eval_windows(module, name).to_u32().unwrap_or_else(|_err| {
232 panic!("required Windows item has unexpected type (not `u32`): {module}::{name}")
233 })
234 }
235
236 fn eval_windows_u64(&self, module: &str, name: &str) -> u64 {
238 self.eval_windows(module, name).to_u64().unwrap_or_else(|_err| {
240 panic!("required Windows item has unexpected type (not `u64`): {module}::{name}")
241 })
242 }
243
244 fn libc_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
246 let this = self.eval_context_ref();
247 if this.tcx.sess.target.os == Os::Windows {
248 panic!(
249 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
250 );
251 }
252 path_ty_layout(this, &["libc", name])
253 }
254
255 fn windows_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
257 let this = self.eval_context_ref();
258 path_ty_layout(this, &["std", "sys", "pal", "windows", "c", name])
259 }
260
261 fn libc_array_ty_layout(&self, name: &str, size: u64) -> TyAndLayout<'tcx> {
263 let this = self.eval_context_ref();
264 let elem_ty_layout = this.libc_ty_layout(name);
265 let array_ty = Ty::new_array(*this.tcx, elem_ty_layout.ty, size);
266 this.layout_of(array_ty).unwrap()
267 }
268
269 fn try_project_field_named<P: Projectable<'tcx, Provenance>>(
271 &self,
272 base: &P,
273 name: &str,
274 ) -> InterpResult<'tcx, Option<P>> {
275 let this = self.eval_context_ref();
276 let adt = base.layout().ty.ty_adt_def().unwrap();
277 for (idx, field) in adt.non_enum_variant().fields.iter_enumerated() {
278 if field.name.as_str() == name {
279 return interp_ok(Some(this.project_field(base, idx)?));
280 }
281 }
282 interp_ok(None)
283 }
284
285 fn project_field_named<P: Projectable<'tcx, Provenance>>(
287 &self,
288 base: &P,
289 name: &str,
290 ) -> InterpResult<'tcx, P> {
291 interp_ok(
292 self.try_project_field_named(base, name)?
293 .unwrap_or_else(|| bug!("no field named {} in type {}", name, base.layout().ty)),
294 )
295 }
296
297 fn write_int(
301 &mut self,
302 i: impl Into<i128>,
303 dest: &impl Writeable<'tcx, Provenance>,
304 ) -> InterpResult<'tcx> {
305 assert!(
306 dest.layout().backend_repr.is_scalar(),
307 "write_int on non-scalar type {}",
308 dest.layout().ty
309 );
310 let val = if dest.layout().backend_repr.is_signed() {
311 Scalar::from_int(i, dest.layout().size)
312 } else {
313 Scalar::from_uint(u128::try_from(i.into()).unwrap(), dest.layout().size)
315 };
316 self.eval_context_mut().write_scalar(val, dest)
317 }
318
319 fn write_int_fields(
321 &mut self,
322 values: &[i128],
323 dest: &impl Writeable<'tcx, Provenance>,
324 ) -> InterpResult<'tcx> {
325 let this = self.eval_context_mut();
326 for (idx, &val) in values.iter().enumerate() {
327 let idx = FieldIdx::from_usize(idx);
328 let field = this.project_field(dest, idx)?;
329 this.write_int(val, &field)?;
330 }
331 interp_ok(())
332 }
333
334 fn write_int_fields_named(
336 &mut self,
337 values: &[(&str, i128)],
338 dest: &impl Writeable<'tcx, Provenance>,
339 ) -> InterpResult<'tcx> {
340 let this = self.eval_context_mut();
341 for &(name, val) in values.iter() {
342 let field = this.project_field_named(dest, name)?;
343 this.write_int(val, &field)?;
344 }
345 interp_ok(())
346 }
347
348 fn write_null(&mut self, dest: &impl Writeable<'tcx, Provenance>) -> InterpResult<'tcx> {
350 self.write_int(0, dest)
351 }
352
353 fn ptr_is_null(&self, ptr: Pointer) -> InterpResult<'tcx, bool> {
355 interp_ok(ptr.addr().bytes() == 0)
356 }
357
358 fn gen_random(&mut self, ptr: Pointer, len: u64) -> InterpResult<'tcx> {
360 if len == 0 {
366 return interp_ok(());
367 }
368 let this = self.eval_context_mut();
369
370 let mut data = vec![0; usize::try_from(len).unwrap()];
371
372 if this.machine.communicate() {
373 getrandom::fill(&mut data)
375 .map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?;
376 } else {
377 let rng = this.machine.rng.get_mut();
378 rng.fill_bytes(&mut data);
379 }
380
381 this.write_bytes_ptr(ptr, data.iter().copied())
382 }
383
384 fn call_function(
390 &mut self,
391 f: ty::Instance<'tcx>,
392 caller_abi: ExternAbi,
393 args: &[ImmTy<'tcx>],
394 dest: Option<&MPlaceTy<'tcx>>,
395 cont: ReturnContinuation,
396 ) -> InterpResult<'tcx> {
397 let this = self.eval_context_mut();
398
399 let mir = this.load_mir(f.def, None)?;
401 let dest = match dest {
402 Some(dest) => dest.clone(),
403 None => MPlaceTy::fake_alloc_zst(this.machine.layouts.unit),
404 };
405
406 let sig = this.tcx.mk_fn_sig(
408 args.iter().map(|a| a.layout.ty),
409 dest.layout.ty,
410 FnSigKind::default().set_abi(caller_abi).set_safe(true),
411 );
412 let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?;
413
414 this.init_stack_frame(
416 f,
417 mir,
418 caller_fn_abi,
419 &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
420 false,
421 &dest.into(),
422 cont,
423 )
424 }
425
426 fn call_thread_root_function(
428 &mut self,
429 f: ty::Instance<'tcx>,
430 caller_abi: ExternAbi,
431 args: &[ImmTy<'tcx>],
432 dest: Option<&MPlaceTy<'tcx>>,
433 span: Span,
434 ) -> InterpResult<'tcx> {
435 let this = self.eval_context_mut();
436 assert!(this.active_thread_stack().is_empty());
437 assert!(this.active_thread_ref().origin_span.is_dummy());
438 this.active_thread_mut().origin_span = span;
439 this.call_function(f, caller_abi, args, dest, ReturnContinuation::Stop { cleanup: true })
440 }
441
442 fn visit_freeze_sensitive(
446 &self,
447 place: &MPlaceTy<'tcx>,
448 size: Size,
449 mut action: impl FnMut(AllocRange, bool) -> InterpResult<'tcx>,
450 ) -> InterpResult<'tcx> {
451 let this = self.eval_context_ref();
452 trace!("visit_frozen(place={:?}, size={:?})", *place, size);
453 debug_assert_eq!(
454 size,
455 this.size_and_align_of_val(place)?
456 .map(|(size, _)| size)
457 .unwrap_or_else(|| place.layout.size)
458 );
459 let start_addr = place.ptr().addr();
463 let mut cur_addr = start_addr;
464 let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer, unsafe_cell_size: Size| {
467 let unsafe_cell_addr = unsafe_cell_ptr.addr();
470 assert!(unsafe_cell_addr >= cur_addr);
471 let frozen_size = unsafe_cell_addr - cur_addr;
472 if frozen_size != Size::ZERO {
474 action(alloc_range(cur_addr - start_addr, frozen_size), true)?;
475 }
476 cur_addr += frozen_size;
477 if unsafe_cell_size != Size::ZERO {
479 action(
480 alloc_range(cur_addr - start_addr, unsafe_cell_size),
481 false,
482 )?;
483 }
484 cur_addr += unsafe_cell_size;
485 interp_ok(())
487 };
488 {
490 let mut visitor = UnsafeCellVisitor {
491 ecx: this,
492 unsafe_cell_action: |place| {
493 trace!("unsafe_cell_action on {:?}", place.ptr());
494 let unsafe_cell_size = this
496 .size_and_align_of_val(place)?
497 .map(|(size, _)| size)
498 .unwrap_or_else(|| place.layout.size);
500 if unsafe_cell_size != Size::ZERO {
502 unsafe_cell_action(&place.ptr(), unsafe_cell_size)
503 } else {
504 interp_ok(())
505 }
506 },
507 };
508 visitor.visit_value(place)?;
509 }
510 unsafe_cell_action(&place.ptr().wrapping_offset(size, this), Size::ZERO)?;
513 return interp_ok(());
515
516 struct UnsafeCellVisitor<'ecx, 'tcx, F>
519 where
520 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
521 {
522 ecx: &'ecx MiriInterpCx<'tcx>,
523 unsafe_cell_action: F,
524 }
525
526 impl<'ecx, 'tcx, F> ValueVisitor<'tcx, MiriMachine<'tcx>> for UnsafeCellVisitor<'ecx, 'tcx, F>
527 where
528 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
529 {
530 type V = MPlaceTy<'tcx>;
531
532 #[inline(always)]
533 fn ecx(&self) -> &MiriInterpCx<'tcx> {
534 self.ecx
535 }
536
537 fn visit_value(&mut self, v: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
539 trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
540 let is_unsafe_cell = match v.layout.ty.kind() {
541 ty::Adt(adt, _) =>
542 Some(adt.did()) == self.ecx.tcx.lang_items().unsafe_cell_type(),
543 _ => false,
544 };
545 if is_unsafe_cell {
546 (self.unsafe_cell_action)(v)
548 } else if self.ecx.type_is_freeze(v.layout.ty) {
549 interp_ok(())
551 } else if matches!(v.layout.fields, FieldsShape::Union(..)) {
552 (self.unsafe_cell_action)(v)
554 } else {
555 match v.layout.variants {
562 Variants::Multiple { .. } => {
563 (self.unsafe_cell_action)(v)
571 }
572 Variants::Single { .. } | Variants::Empty => {
573 self.walk_value(v)
576 }
577 }
578 }
579 }
580
581 fn visit_union(
582 &mut self,
583 _v: &MPlaceTy<'tcx>,
584 _fields: NonZero<usize>,
585 ) -> InterpResult<'tcx> {
586 bug!("we should have already handled unions in `visit_value`")
587 }
588 }
589 }
590
591 fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> {
595 if !self.eval_context_ref().machine.communicate() {
596 self.reject_in_isolation(name, RejectOpWith::Abort)?;
597 }
598 interp_ok(())
599 }
600
601 fn reject_in_isolation(&self, op_name: &str, reject_with: RejectOpWith) -> InterpResult<'tcx> {
604 let this = self.eval_context_ref();
605 match reject_with {
606 RejectOpWith::Abort => isolation_abort_error(op_name),
607 RejectOpWith::WarningWithoutBacktrace => {
608 static DEDUP: Mutex<FxHashSet<String>> =
610 Mutex::new(FxHashSet::with_hasher(FxBuildHasher));
611 let mut emitted_warnings = DEDUP.lock().unwrap();
612 if !emitted_warnings.contains(op_name) {
613 emitted_warnings.insert(op_name.to_owned());
615 this.tcx
616 .dcx()
617 .warn(format!("{op_name} was made to return an error due to isolation"));
618 }
619
620 interp_ok(())
621 }
622 RejectOpWith::Warning => {
623 this.emit_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string()));
624 interp_ok(())
625 }
626 RejectOpWith::NoWarning => interp_ok(()), }
628 }
629
630 fn assert_target_os(&self, target_os: Os, name: &str) {
634 assert_eq!(
635 self.eval_context_ref().tcx.sess.target.os,
636 target_os,
637 "`{name}` is only available on the `{target_os}` target OS",
638 )
639 }
640
641 fn check_target_os(&self, target_oses: &[Os], name: Symbol) -> InterpResult<'tcx> {
645 let target_os = &self.eval_context_ref().tcx.sess.target.os;
646 if !target_oses.contains(target_os) {
647 throw_unsup_format!("`{name}` is not supported on {target_os}");
648 }
649 interp_ok(())
650 }
651
652 fn assert_target_os_is_unix(&self, name: &str) {
656 assert!(self.target_os_is_unix(), "`{name}` is only available for unix targets",);
657 }
658
659 fn target_os_is_unix(&self) -> bool {
660 self.eval_context_ref().tcx.sess.target.families.iter().any(|f| f == "unix")
661 }
662
663 fn deref_pointer_as(
665 &self,
666 op: &impl Projectable<'tcx, Provenance>,
667 layout: TyAndLayout<'tcx>,
668 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
669 let this = self.eval_context_ref();
670 let ptr = this.read_pointer(op)?;
671 interp_ok(this.ptr_to_mplace(ptr, layout))
672 }
673
674 fn deref_pointer_and_offset(
676 &self,
677 op: &impl Projectable<'tcx, Provenance>,
678 offset: u64,
679 base_layout: TyAndLayout<'tcx>,
680 value_layout: TyAndLayout<'tcx>,
681 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
682 let this = self.eval_context_ref();
683 let op_place = this.deref_pointer_as(op, base_layout)?;
684 let offset = Size::from_bytes(offset);
685
686 assert!(base_layout.size >= offset + value_layout.size);
688 let value_place = op_place.offset(offset, value_layout, this)?;
689 interp_ok(value_place)
690 }
691
692 fn deref_pointer_and_read(
693 &self,
694 op: &impl Projectable<'tcx, Provenance>,
695 offset: u64,
696 base_layout: TyAndLayout<'tcx>,
697 value_layout: TyAndLayout<'tcx>,
698 ) -> InterpResult<'tcx, Scalar> {
699 let this = self.eval_context_ref();
700 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
701 this.read_scalar(&value_place)
702 }
703
704 fn deref_pointer_and_write(
705 &mut self,
706 op: &impl Projectable<'tcx, Provenance>,
707 offset: u64,
708 value: impl Into<Scalar>,
709 base_layout: TyAndLayout<'tcx>,
710 value_layout: TyAndLayout<'tcx>,
711 ) -> InterpResult<'tcx, ()> {
712 let this = self.eval_context_mut();
713 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
714 this.write_scalar(value, &value_place)
715 }
716
717 fn read_timespec(&mut self, tp: &MPlaceTy<'tcx>) -> InterpResult<'tcx, Option<Duration>> {
721 let this = self.eval_context_mut();
722 let seconds_place = this.project_field(tp, FieldIdx::ZERO)?;
723 let seconds_scalar = this.read_scalar(&seconds_place)?;
724 let seconds = seconds_scalar.to_target_isize(this)?;
725 let nanoseconds_place = this.project_field(tp, FieldIdx::ONE)?;
726 let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?;
727 let nanoseconds = nanoseconds_scalar.to_target_isize(this)?;
728
729 interp_ok(try {
730 let seconds: u64 = seconds.try_into().ok()?;
732 let nanoseconds: u32 = nanoseconds.try_into().ok()?;
734 if nanoseconds >= 1_000_000_000 {
735 None?
737 }
738 Duration::new(seconds, nanoseconds)
739 })
740 }
741
742 fn read_byte_slice<'a>(&'a self, slice: &ImmTy<'tcx>) -> InterpResult<'tcx, &'a [u8]>
744 where
745 'tcx: 'a,
746 {
747 let this = self.eval_context_ref();
748 let (ptr, len) = slice.to_scalar_pair();
749 let ptr = ptr.to_pointer(this)?;
750 let len = len.to_target_usize(this)?;
751 let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
752 interp_ok(bytes)
753 }
754
755 fn read_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a [u8]>
757 where
758 'tcx: 'a,
759 {
760 let this = self.eval_context_ref();
761 let size1 = Size::from_bytes(1);
762
763 let mut len = Size::ZERO;
765 loop {
766 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()?;
770 if byte == 0 {
771 break;
772 } else {
773 len += size1;
774 }
775 }
776
777 this.read_bytes_ptr_strip_provenance(ptr, len)
779 }
780
781 fn write_c_str(
787 &mut self,
788 c_str: &[u8],
789 ptr: Pointer,
790 size: u64,
791 ) -> InterpResult<'tcx, (bool, u64)> {
792 let string_length = u64::try_from(c_str.len()).unwrap();
795 let string_length = string_length.strict_add(1);
796 if size < string_length {
797 return interp_ok((false, string_length));
798 }
799 self.eval_context_mut()
800 .write_bytes_ptr(ptr, c_str.iter().copied().chain(iter::once(0u8)))?;
801 interp_ok((true, string_length))
802 }
803
804 fn read_c_str_with_char_size<T>(
807 &self,
808 mut ptr: Pointer,
809 size: Size,
810 align: Align,
811 ) -> InterpResult<'tcx, Vec<T>>
812 where
813 T: TryFrom<u128>,
814 <T as TryFrom<u128>>::Error: std::fmt::Debug,
815 {
816 assert_ne!(size, Size::ZERO);
817
818 let this = self.eval_context_ref();
819
820 this.check_ptr_align(ptr, align)?;
821
822 let mut wchars = Vec::new();
823 loop {
824 let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?;
828 if wchar_int == 0 {
829 break;
830 } else {
831 wchars.push(wchar_int.try_into().unwrap());
832 ptr = ptr.wrapping_offset(size, this);
833 }
834 }
835
836 interp_ok(wchars)
837 }
838
839 fn read_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u16>> {
841 self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap())
842 }
843
844 fn write_wide_str(
851 &mut self,
852 wide_str: &[u16],
853 ptr: Pointer,
854 size: u64,
855 ) -> InterpResult<'tcx, (bool, u64)> {
856 let string_length = u64::try_from(wide_str.len()).unwrap();
859 let string_length = string_length.strict_add(1);
860 if size < string_length {
861 return interp_ok((false, string_length));
862 }
863
864 let size2 = Size::from_bytes(2);
866 let this = self.eval_context_mut();
867 this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
868 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() {
870 let offset = u64::try_from(offset).unwrap();
871 alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?;
872 }
873 interp_ok((true, string_length))
874 }
875
876 fn read_wchar_t_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u32>> {
879 let this = self.eval_context_ref();
880 let wchar_t = if this.tcx.sess.target.os == Os::Windows {
881 this.machine.layouts.u16
883 } else {
884 this.libc_ty_layout("wchar_t")
885 };
886 self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi)
887 }
888
889 fn frame_in_std(&self) -> bool {
890 let this = self.eval_context_ref();
891 let frame = this.frame();
892 let instance: Option<_> = try {
894 let scope = frame.current_source_info()?.scope;
895 let inlined_parent = frame.body().source_scopes[scope].inlined_parent_scope?;
896 let source = &frame.body().source_scopes[inlined_parent];
897 source.inlined.expect("inlined_parent_scope points to scope without inline info").0
898 };
899 let instance = instance.unwrap_or(frame.instance());
901 let frame_crate = this.tcx.def_path(instance.def_id()).krate;
906 let crate_name = this.tcx.crate_name(frame_crate);
907 let crate_name = crate_name.as_str();
908 crate_name == "std" || crate_name == "std_miri_test"
910 }
911
912 fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) {
914 let this = self.eval_context_mut();
915 let provenance = mplace.ptr().into_pointer_or_addr().unwrap().provenance;
917 this.alloc_mark_immutable(provenance.get_alloc_id().unwrap()).unwrap();
918 }
919
920 fn get_twice_wide_int_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
922 let this = self.eval_context_ref();
923 match ty.kind() {
924 ty::Uint(UintTy::U8) => this.tcx.types.u16,
926 ty::Uint(UintTy::U16) => this.tcx.types.u32,
927 ty::Uint(UintTy::U32) => this.tcx.types.u64,
928 ty::Uint(UintTy::U64) => this.tcx.types.u128,
929 ty::Int(IntTy::I8) => this.tcx.types.i16,
931 ty::Int(IntTy::I16) => this.tcx.types.i32,
932 ty::Int(IntTy::I32) => this.tcx.types.i64,
933 ty::Int(IntTy::I64) => this.tcx.types.i128,
934 _ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"),
935 }
936 }
937
938 fn expect_target_feature_for_intrinsic(
943 &self,
944 intrinsic: Symbol,
945 target_feature: &str,
946 ) -> InterpResult<'tcx, ()> {
947 let this = self.eval_context_ref();
948 if !this.tcx.sess.unstable_target_features.contains(&Symbol::intern(target_feature)) {
949 throw_ub_format!(
950 "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}"
951 );
952 }
953 interp_ok(())
954 }
955
956 fn lookup_link_section(
959 &mut self,
960 include_name: impl Fn(&str) -> bool,
961 ) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> {
962 let this = self.eval_context_mut();
963 let tcx = this.tcx.tcx;
964
965 let mut array = vec![];
966
967 iter_exported_symbols(tcx, |_cnum, def_id| {
968 let attrs = tcx.codegen_fn_attrs(def_id);
969 let Some(link_section) = attrs.link_section else {
970 return interp_ok(());
971 };
972 if include_name(link_section.as_str()) {
973 let instance = ty::Instance::mono(tcx, def_id);
974 let span = tcx.def_span(def_id);
975 let const_val = this.eval_global(instance).unwrap_or_else(|err| {
976 panic!(
977 "failed to evaluate static in required link_section: {def_id:?}\n{err:?}"
978 )
979 });
980 match const_val.layout.ty.kind() {
981 ty::FnPtr(..) => {
982 array.push((this.read_immediate(&const_val)?, span));
983 }
984 ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => {
985 let mut elems = this.project_array_fields(&const_val)?;
986 while let Some((_idx, elem)) = elems.next(this)? {
987 array.push((this.read_immediate(&elem)?, span));
988 }
989 }
990 _ =>
991 throw_unsup_format!(
992 "only function pointers and arrays of function pointers are supported in well-known linker sections"
993 ),
994 }
995 }
996 interp_ok(())
997 })?;
998
999 interp_ok(array)
1000 }
1001
1002 fn mangle_internal_symbol<'a>(&'a mut self, name: &'static str) -> &'a str
1003 where
1004 'tcx: 'a,
1005 {
1006 let this = self.eval_context_mut();
1007 let tcx = *this.tcx;
1008 this.machine
1009 .mangle_internal_symbol_cache
1010 .entry(name)
1011 .or_insert_with(|| mangle_internal_symbol(tcx, name))
1012 }
1013}
1014
1015impl<'tcx> MiriMachine<'tcx> {
1016 pub fn current_user_relevant_span(&self) -> Span {
1021 self.threads.active_thread_ref().current_user_relevant_span()
1022 }
1023
1024 pub fn caller_span(&self) -> Span {
1030 let frame_idx = self.top_user_relevant_frame().unwrap();
1033 let frame_idx = cmp::min(frame_idx, self.stack().len().saturating_sub(2));
1034 self.stack()[frame_idx].current_span()
1035 }
1036
1037 fn stack(&self) -> &[Frame<'tcx, Provenance, machine::FrameExtra<'tcx>>] {
1038 self.threads.active_thread_stack()
1039 }
1040
1041 fn top_user_relevant_frame(&self) -> Option<usize> {
1042 self.threads.active_thread_ref().top_user_relevant_frame()
1043 }
1044
1045 pub fn user_relevance(&self, frame: &Frame<'tcx, Provenance>) -> u8 {
1047 if frame.instance().def.requires_caller_location(self.tcx) {
1048 return 0;
1049 }
1050 if self.is_local(frame.instance()) {
1051 u8::MAX
1052 } else {
1053 1
1056 }
1057 }
1058}
1059
1060pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
1061 throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
1062 "{name} not available when isolation is enabled",
1063 )))
1064}
1065
1066pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar {
1067 let val = if b { -1 } else { 0 };
1071 Scalar::from_int(val, size)
1072}
1073
1074pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
1078 if success {
1079 u32::try_from(len.strict_sub(1)).unwrap()
1082 } else {
1083 u32::try_from(len).unwrap()
1086 }
1087}
1088
1089pub trait ToUsize {
1091 fn to_usize(self) -> usize;
1092}
1093
1094impl ToUsize for u32 {
1095 fn to_usize(self) -> usize {
1096 self.try_into().unwrap()
1097 }
1098}
1099
1100pub trait ToU64 {
1103 fn to_u64(self) -> u64;
1104}
1105
1106impl ToU64 for usize {
1107 fn to_u64(self) -> u64 {
1108 self.try_into().unwrap()
1109 }
1110}
1111
1112#[macro_export]
1118macro_rules! enter_trace_span {
1119 ($($tt:tt)*) => {
1120 rustc_const_eval::enter_trace_span!($crate::MiriMachine<'static>, $($tt)*)
1121 };
1122}