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::Safety;
10use rustc_hir::def::{DefKind, Namespace};
11use rustc_hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE};
12use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
13use rustc_middle::middle::dependency_format::Linkage;
14use rustc_middle::middle::exported_symbols::ExportedSymbol;
15use rustc_middle::ty::layout::{LayoutOf, MaybeResult, TyAndLayout};
16use rustc_middle::ty::{self, IntTy, Ty, TyCtxt, UintTy};
17use rustc_session::config::CrateType;
18use rustc_span::{Span, Symbol};
19use rustc_symbol_mangling::mangle_internal_symbol;
20use rustc_target::spec::Os;
21
22use crate::*;
23
24fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option<Namespace>) -> Option<DefId> {
28 let _trace = enter_trace_span!("try_resolve_did", ?path);
29
30 fn find_children<'tcx: 'a, 'a>(
32 tcx: TyCtxt<'tcx>,
33 item: DefId,
34 name: &'a str,
35 ) -> impl Iterator<Item = DefId> + 'a {
36 let name = Symbol::intern(name);
37 tcx.module_children(item)
38 .iter()
39 .filter(move |item| item.ident.name == name)
40 .map(move |item| item.res.def_id())
41 }
42
43 let (&crate_name, path) = path.split_first().expect("paths must have at least one segment");
45 let (modules, item) = if let Some(namespace) = namespace {
46 let (&item_name, modules) =
47 path.split_last().expect("non-module paths must have at least 2 segments");
48 (modules, Some((item_name, namespace)))
49 } else {
50 (path, None)
51 };
52
53 'crates: for krate in
58 tcx.crates(()).iter().filter(|&&krate| tcx.crate_name(krate).as_str() == crate_name)
59 {
60 let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX };
61 for &segment in modules {
63 let Some(next_item) = find_children(tcx, cur_item, segment)
64 .find(|&item| tcx.def_kind(item) == DefKind::Mod)
65 else {
66 continue 'crates;
67 };
68 cur_item = next_item;
69 }
70 match item {
72 Some((item_name, namespace)) => {
73 let Some(item) = find_children(tcx, cur_item, item_name)
74 .find(|&item| tcx.def_kind(item).ns() == Some(namespace))
75 else {
76 continue 'crates;
77 };
78 return Some(item);
79 }
80 None => {
81 return Some(cur_item);
83 }
84 }
85 }
86 None
88}
89
90pub fn try_resolve_path<'tcx>(
92 tcx: TyCtxt<'tcx>,
93 path: &[&str],
94 namespace: Namespace,
95) -> Option<ty::Instance<'tcx>> {
96 let did = try_resolve_did(tcx, path, Some(namespace))?;
97 Some(ty::Instance::mono(tcx, did))
98}
99
100#[track_caller]
102pub fn resolve_path<'tcx>(
103 tcx: TyCtxt<'tcx>,
104 path: &[&str],
105 namespace: Namespace,
106) -> ty::Instance<'tcx> {
107 try_resolve_path(tcx, path, namespace)
108 .unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
109}
110
111#[track_caller]
113pub fn path_ty_layout<'tcx>(cx: &impl LayoutOf<'tcx>, path: &[&str]) -> TyAndLayout<'tcx> {
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) -> InterpResult<'tcx>,
122) -> InterpResult<'tcx> {
123 let crate_items = tcx.hir_crate_items(());
127 for def_id in crate_items.definitions() {
128 let exported = tcx.def_kind(def_id).has_codegen_attrs() && {
129 let codegen_attrs = tcx.codegen_fn_attrs(def_id);
130 codegen_attrs.contains_extern_indicator()
131 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
132 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
133 };
134 if exported {
135 f(LOCAL_CRATE, def_id.into())?;
136 }
137 }
138
139 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) {
159 if let ExportedSymbol::NonGeneric(def_id) = symbol {
160 f(cnum, def_id)?;
161 }
162 }
163 }
164 interp_ok(())
165}
166
167impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
168pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
169 fn have_module(&self, path: &[&str]) -> bool {
171 try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some()
172 }
173
174 fn eval_path(&self, path: &[&str]) -> MPlaceTy<'tcx> {
176 let this = self.eval_context_ref();
177 let instance = resolve_path(*this.tcx, path, Namespace::ValueNS);
178 this.eval_global(instance).unwrap_or_else(|err| {
180 panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
181 })
182 }
183 fn eval_path_scalar(&self, path: &[&str]) -> Scalar {
184 let this = self.eval_context_ref();
185 let val = this.eval_path(path);
186 this.read_scalar(&val)
187 .unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}"))
188 }
189
190 fn eval_libc(&self, name: &str) -> Scalar {
192 if self.eval_context_ref().tcx.sess.target.os == Os::Windows {
193 panic!(
194 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
195 );
196 }
197 self.eval_path_scalar(&["libc", name])
198 }
199
200 fn eval_libc_i32(&self, name: &str) -> i32 {
202 self.eval_libc(name).to_i32().unwrap_or_else(|_err| {
204 panic!("required libc item has unexpected type (not `i32`): {name}")
205 })
206 }
207
208 fn eval_libc_u32(&self, name: &str) -> u32 {
210 self.eval_libc(name).to_u32().unwrap_or_else(|_err| {
212 panic!("required libc item has unexpected type (not `u32`): {name}")
213 })
214 }
215
216 fn eval_libc_u64(&self, name: &str) -> u64 {
218 self.eval_libc(name).to_u64().unwrap_or_else(|_err| {
220 panic!("required libc item has unexpected type (not `u64`): {name}")
221 })
222 }
223
224 fn eval_windows(&self, module: &str, name: &str) -> Scalar {
226 self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name])
227 }
228
229 fn eval_windows_u32(&self, module: &str, name: &str) -> u32 {
231 self.eval_windows(module, name).to_u32().unwrap_or_else(|_err| {
233 panic!("required Windows item has unexpected type (not `u32`): {module}::{name}")
234 })
235 }
236
237 fn eval_windows_u64(&self, module: &str, name: &str) -> u64 {
239 self.eval_windows(module, name).to_u64().unwrap_or_else(|_err| {
241 panic!("required Windows item has unexpected type (not `u64`): {module}::{name}")
242 })
243 }
244
245 fn libc_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
247 let this = self.eval_context_ref();
248 if this.tcx.sess.target.os == Os::Windows {
249 panic!(
250 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
251 );
252 }
253 path_ty_layout(this, &["libc", name])
254 }
255
256 fn windows_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
258 let this = self.eval_context_ref();
259 path_ty_layout(this, &["std", "sys", "pal", "windows", "c", name])
260 }
261
262 fn libc_array_ty_layout(&self, name: &str, size: u64) -> TyAndLayout<'tcx> {
264 let this = self.eval_context_ref();
265 let elem_ty_layout = this.libc_ty_layout(name);
266 let array_ty = Ty::new_array(*this.tcx, elem_ty_layout.ty, size);
267 this.layout_of(array_ty).unwrap()
268 }
269
270 fn try_project_field_named<P: Projectable<'tcx, Provenance>>(
272 &self,
273 base: &P,
274 name: &str,
275 ) -> InterpResult<'tcx, Option<P>> {
276 let this = self.eval_context_ref();
277 let adt = base.layout().ty.ty_adt_def().unwrap();
278 for (idx, field) in adt.non_enum_variant().fields.iter_enumerated() {
279 if field.name.as_str() == name {
280 return interp_ok(Some(this.project_field(base, idx)?));
281 }
282 }
283 interp_ok(None)
284 }
285
286 fn project_field_named<P: Projectable<'tcx, Provenance>>(
288 &self,
289 base: &P,
290 name: &str,
291 ) -> InterpResult<'tcx, P> {
292 interp_ok(
293 self.try_project_field_named(base, name)?
294 .unwrap_or_else(|| bug!("no field named {} in type {}", name, base.layout().ty)),
295 )
296 }
297
298 fn write_int(
302 &mut self,
303 i: impl Into<i128>,
304 dest: &impl Writeable<'tcx, Provenance>,
305 ) -> InterpResult<'tcx> {
306 assert!(
307 dest.layout().backend_repr.is_scalar(),
308 "write_int on non-scalar type {}",
309 dest.layout().ty
310 );
311 let val = if dest.layout().backend_repr.is_signed() {
312 Scalar::from_int(i, dest.layout().size)
313 } else {
314 Scalar::from_uint(u128::try_from(i.into()).unwrap(), dest.layout().size)
316 };
317 self.eval_context_mut().write_scalar(val, dest)
318 }
319
320 fn write_int_fields(
322 &mut self,
323 values: &[i128],
324 dest: &impl Writeable<'tcx, Provenance>,
325 ) -> InterpResult<'tcx> {
326 let this = self.eval_context_mut();
327 for (idx, &val) in values.iter().enumerate() {
328 let idx = FieldIdx::from_usize(idx);
329 let field = this.project_field(dest, idx)?;
330 this.write_int(val, &field)?;
331 }
332 interp_ok(())
333 }
334
335 fn write_int_fields_named(
337 &mut self,
338 values: &[(&str, i128)],
339 dest: &impl Writeable<'tcx, Provenance>,
340 ) -> InterpResult<'tcx> {
341 let this = self.eval_context_mut();
342 for &(name, val) in values.iter() {
343 let field = this.project_field_named(dest, name)?;
344 this.write_int(val, &field)?;
345 }
346 interp_ok(())
347 }
348
349 fn write_null(&mut self, dest: &impl Writeable<'tcx, Provenance>) -> InterpResult<'tcx> {
351 self.write_int(0, dest)
352 }
353
354 fn ptr_is_null(&self, ptr: Pointer) -> InterpResult<'tcx, bool> {
356 interp_ok(ptr.addr().bytes() == 0)
357 }
358
359 fn gen_random(&mut self, ptr: Pointer, len: u64) -> InterpResult<'tcx> {
361 if len == 0 {
367 return interp_ok(());
368 }
369 let this = self.eval_context_mut();
370
371 let mut data = vec![0; usize::try_from(len).unwrap()];
372
373 if this.machine.communicate() {
374 getrandom::fill(&mut data)
376 .map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?;
377 } else {
378 let rng = this.machine.rng.get_mut();
379 rng.fill_bytes(&mut data);
380 }
381
382 this.write_bytes_ptr(ptr, data.iter().copied())
383 }
384
385 fn call_function(
391 &mut self,
392 f: ty::Instance<'tcx>,
393 caller_abi: ExternAbi,
394 args: &[ImmTy<'tcx>],
395 dest: Option<&MPlaceTy<'tcx>>,
396 cont: ReturnContinuation,
397 ) -> InterpResult<'tcx> {
398 let this = self.eval_context_mut();
399
400 let mir = this.load_mir(f.def, None)?;
402 let dest = match dest {
403 Some(dest) => dest.clone(),
404 None => MPlaceTy::fake_alloc_zst(this.machine.layouts.unit),
405 };
406
407 let sig = this.tcx.mk_fn_sig(
409 args.iter().map(|a| a.layout.ty),
410 dest.layout.ty,
411 false,
412 Safety::Safe,
413 caller_abi,
414 );
415 let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?;
416
417 this.init_stack_frame(
419 f,
420 mir,
421 caller_fn_abi,
422 &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
423 false,
424 &dest.into(),
425 cont,
426 )
427 }
428
429 fn call_thread_root_function(
431 &mut self,
432 f: ty::Instance<'tcx>,
433 caller_abi: ExternAbi,
434 args: &[ImmTy<'tcx>],
435 dest: Option<&MPlaceTy<'tcx>>,
436 span: Span,
437 ) -> InterpResult<'tcx> {
438 let this = self.eval_context_mut();
439 assert!(this.active_thread_stack().is_empty());
440 assert!(this.active_thread_ref().origin_span.is_dummy());
441 this.active_thread_mut().origin_span = span;
442 this.call_function(f, caller_abi, args, dest, ReturnContinuation::Stop { cleanup: true })
443 }
444
445 fn visit_freeze_sensitive(
449 &self,
450 place: &MPlaceTy<'tcx>,
451 size: Size,
452 mut action: impl FnMut(AllocRange, bool) -> InterpResult<'tcx>,
453 ) -> InterpResult<'tcx> {
454 let this = self.eval_context_ref();
455 trace!("visit_frozen(place={:?}, size={:?})", *place, size);
456 debug_assert_eq!(
457 size,
458 this.size_and_align_of_val(place)?
459 .map(|(size, _)| size)
460 .unwrap_or_else(|| place.layout.size)
461 );
462 let start_addr = place.ptr().addr();
466 let mut cur_addr = start_addr;
467 let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer, unsafe_cell_size: Size| {
470 let unsafe_cell_addr = unsafe_cell_ptr.addr();
473 assert!(unsafe_cell_addr >= cur_addr);
474 let frozen_size = unsafe_cell_addr - cur_addr;
475 if frozen_size != Size::ZERO {
477 action(alloc_range(cur_addr - start_addr, frozen_size), true)?;
478 }
479 cur_addr += frozen_size;
480 if unsafe_cell_size != Size::ZERO {
482 action(
483 alloc_range(cur_addr - start_addr, unsafe_cell_size),
484 false,
485 )?;
486 }
487 cur_addr += unsafe_cell_size;
488 interp_ok(())
490 };
491 {
493 let mut visitor = UnsafeCellVisitor {
494 ecx: this,
495 unsafe_cell_action: |place| {
496 trace!("unsafe_cell_action on {:?}", place.ptr());
497 let unsafe_cell_size = this
499 .size_and_align_of_val(place)?
500 .map(|(size, _)| size)
501 .unwrap_or_else(|| place.layout.size);
503 if unsafe_cell_size != Size::ZERO {
505 unsafe_cell_action(&place.ptr(), unsafe_cell_size)
506 } else {
507 interp_ok(())
508 }
509 },
510 };
511 visitor.visit_value(place)?;
512 }
513 unsafe_cell_action(&place.ptr().wrapping_offset(size, this), Size::ZERO)?;
516 return interp_ok(());
518
519 struct UnsafeCellVisitor<'ecx, 'tcx, F>
522 where
523 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
524 {
525 ecx: &'ecx MiriInterpCx<'tcx>,
526 unsafe_cell_action: F,
527 }
528
529 impl<'ecx, 'tcx, F> ValueVisitor<'tcx, MiriMachine<'tcx>> for UnsafeCellVisitor<'ecx, 'tcx, F>
530 where
531 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
532 {
533 type V = MPlaceTy<'tcx>;
534
535 #[inline(always)]
536 fn ecx(&self) -> &MiriInterpCx<'tcx> {
537 self.ecx
538 }
539
540 fn visit_value(&mut self, v: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
542 trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
543 let is_unsafe_cell = match v.layout.ty.kind() {
544 ty::Adt(adt, _) =>
545 Some(adt.did()) == self.ecx.tcx.lang_items().unsafe_cell_type(),
546 _ => false,
547 };
548 if is_unsafe_cell {
549 (self.unsafe_cell_action)(v)
551 } else if self.ecx.type_is_freeze(v.layout.ty) {
552 interp_ok(())
554 } else if matches!(v.layout.fields, FieldsShape::Union(..)) {
555 (self.unsafe_cell_action)(v)
557 } else {
558 match v.layout.variants {
565 Variants::Multiple { .. } => {
566 (self.unsafe_cell_action)(v)
574 }
575 Variants::Single { .. } | Variants::Empty => {
576 self.walk_value(v)
579 }
580 }
581 }
582 }
583
584 fn visit_union(
585 &mut self,
586 _v: &MPlaceTy<'tcx>,
587 _fields: NonZero<usize>,
588 ) -> InterpResult<'tcx> {
589 bug!("we should have already handled unions in `visit_value`")
590 }
591 }
592 }
593
594 fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> {
598 if !self.eval_context_ref().machine.communicate() {
599 self.reject_in_isolation(name, RejectOpWith::Abort)?;
600 }
601 interp_ok(())
602 }
603
604 fn reject_in_isolation(&self, op_name: &str, reject_with: RejectOpWith) -> InterpResult<'tcx> {
607 let this = self.eval_context_ref();
608 match reject_with {
609 RejectOpWith::Abort => isolation_abort_error(op_name),
610 RejectOpWith::WarningWithoutBacktrace => {
611 static DEDUP: Mutex<FxHashSet<String>> =
613 Mutex::new(FxHashSet::with_hasher(FxBuildHasher));
614 let mut emitted_warnings = DEDUP.lock().unwrap();
615 if !emitted_warnings.contains(op_name) {
616 emitted_warnings.insert(op_name.to_owned());
618 this.tcx
619 .dcx()
620 .warn(format!("{op_name} was made to return an error due to isolation"));
621 }
622
623 interp_ok(())
624 }
625 RejectOpWith::Warning => {
626 this.emit_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string()));
627 interp_ok(())
628 }
629 RejectOpWith::NoWarning => interp_ok(()), }
631 }
632
633 fn assert_target_os(&self, target_os: Os, name: &str) {
637 assert_eq!(
638 self.eval_context_ref().tcx.sess.target.os,
639 target_os,
640 "`{name}` is only available on the `{target_os}` target OS",
641 )
642 }
643
644 fn check_target_os(&self, target_oses: &[Os], name: Symbol) -> InterpResult<'tcx> {
648 let target_os = &self.eval_context_ref().tcx.sess.target.os;
649 if !target_oses.contains(target_os) {
650 throw_unsup_format!("`{name}` is not supported on {target_os}");
651 }
652 interp_ok(())
653 }
654
655 fn assert_target_os_is_unix(&self, name: &str) {
659 assert!(self.target_os_is_unix(), "`{name}` is only available for unix targets",);
660 }
661
662 fn target_os_is_unix(&self) -> bool {
663 self.eval_context_ref().tcx.sess.target.families.iter().any(|f| f == "unix")
664 }
665
666 fn deref_pointer_as(
668 &self,
669 op: &impl Projectable<'tcx, Provenance>,
670 layout: TyAndLayout<'tcx>,
671 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
672 let this = self.eval_context_ref();
673 let ptr = this.read_pointer(op)?;
674 interp_ok(this.ptr_to_mplace(ptr, layout))
675 }
676
677 fn deref_pointer_and_offset(
679 &self,
680 op: &impl Projectable<'tcx, Provenance>,
681 offset: u64,
682 base_layout: TyAndLayout<'tcx>,
683 value_layout: TyAndLayout<'tcx>,
684 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
685 let this = self.eval_context_ref();
686 let op_place = this.deref_pointer_as(op, base_layout)?;
687 let offset = Size::from_bytes(offset);
688
689 assert!(base_layout.size >= offset + value_layout.size);
691 let value_place = op_place.offset(offset, value_layout, this)?;
692 interp_ok(value_place)
693 }
694
695 fn deref_pointer_and_read(
696 &self,
697 op: &impl Projectable<'tcx, Provenance>,
698 offset: u64,
699 base_layout: TyAndLayout<'tcx>,
700 value_layout: TyAndLayout<'tcx>,
701 ) -> InterpResult<'tcx, Scalar> {
702 let this = self.eval_context_ref();
703 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
704 this.read_scalar(&value_place)
705 }
706
707 fn deref_pointer_and_write(
708 &mut self,
709 op: &impl Projectable<'tcx, Provenance>,
710 offset: u64,
711 value: impl Into<Scalar>,
712 base_layout: TyAndLayout<'tcx>,
713 value_layout: TyAndLayout<'tcx>,
714 ) -> InterpResult<'tcx, ()> {
715 let this = self.eval_context_mut();
716 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
717 this.write_scalar(value, &value_place)
718 }
719
720 fn read_timespec(&mut self, tp: &MPlaceTy<'tcx>) -> InterpResult<'tcx, Option<Duration>> {
724 let this = self.eval_context_mut();
725 let seconds_place = this.project_field(tp, FieldIdx::ZERO)?;
726 let seconds_scalar = this.read_scalar(&seconds_place)?;
727 let seconds = seconds_scalar.to_target_isize(this)?;
728 let nanoseconds_place = this.project_field(tp, FieldIdx::ONE)?;
729 let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?;
730 let nanoseconds = nanoseconds_scalar.to_target_isize(this)?;
731
732 interp_ok(try {
733 let seconds: u64 = seconds.try_into().ok()?;
735 let nanoseconds: u32 = nanoseconds.try_into().ok()?;
737 if nanoseconds >= 1_000_000_000 {
738 None?
740 }
741 Duration::new(seconds, nanoseconds)
742 })
743 }
744
745 fn read_byte_slice<'a>(&'a self, slice: &ImmTy<'tcx>) -> InterpResult<'tcx, &'a [u8]>
747 where
748 'tcx: 'a,
749 {
750 let this = self.eval_context_ref();
751 let (ptr, len) = slice.to_scalar_pair();
752 let ptr = ptr.to_pointer(this)?;
753 let len = len.to_target_usize(this)?;
754 let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
755 interp_ok(bytes)
756 }
757
758 fn read_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a [u8]>
760 where
761 'tcx: 'a,
762 {
763 let this = self.eval_context_ref();
764 let size1 = Size::from_bytes(1);
765
766 let mut len = Size::ZERO;
768 loop {
769 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()?;
773 if byte == 0 {
774 break;
775 } else {
776 len += size1;
777 }
778 }
779
780 this.read_bytes_ptr_strip_provenance(ptr, len)
782 }
783
784 fn write_c_str(
790 &mut self,
791 c_str: &[u8],
792 ptr: Pointer,
793 size: u64,
794 ) -> InterpResult<'tcx, (bool, u64)> {
795 let string_length = u64::try_from(c_str.len()).unwrap();
798 let string_length = string_length.strict_add(1);
799 if size < string_length {
800 return interp_ok((false, string_length));
801 }
802 self.eval_context_mut()
803 .write_bytes_ptr(ptr, c_str.iter().copied().chain(iter::once(0u8)))?;
804 interp_ok((true, string_length))
805 }
806
807 fn read_c_str_with_char_size<T>(
810 &self,
811 mut ptr: Pointer,
812 size: Size,
813 align: Align,
814 ) -> InterpResult<'tcx, Vec<T>>
815 where
816 T: TryFrom<u128>,
817 <T as TryFrom<u128>>::Error: std::fmt::Debug,
818 {
819 assert_ne!(size, Size::ZERO);
820
821 let this = self.eval_context_ref();
822
823 this.check_ptr_align(ptr, align)?;
824
825 let mut wchars = Vec::new();
826 loop {
827 let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?;
831 if wchar_int == 0 {
832 break;
833 } else {
834 wchars.push(wchar_int.try_into().unwrap());
835 ptr = ptr.wrapping_offset(size, this);
836 }
837 }
838
839 interp_ok(wchars)
840 }
841
842 fn read_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u16>> {
844 self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap())
845 }
846
847 fn write_wide_str(
854 &mut self,
855 wide_str: &[u16],
856 ptr: Pointer,
857 size: u64,
858 ) -> InterpResult<'tcx, (bool, u64)> {
859 let string_length = u64::try_from(wide_str.len()).unwrap();
862 let string_length = string_length.strict_add(1);
863 if size < string_length {
864 return interp_ok((false, string_length));
865 }
866
867 let size2 = Size::from_bytes(2);
869 let this = self.eval_context_mut();
870 this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
871 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() {
873 let offset = u64::try_from(offset).unwrap();
874 alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?;
875 }
876 interp_ok((true, string_length))
877 }
878
879 fn read_wchar_t_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u32>> {
882 let this = self.eval_context_ref();
883 let wchar_t = if this.tcx.sess.target.os == Os::Windows {
884 this.machine.layouts.u16
886 } else {
887 this.libc_ty_layout("wchar_t")
888 };
889 self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi)
890 }
891
892 fn frame_in_std(&self) -> bool {
893 let this = self.eval_context_ref();
894 let frame = this.frame();
895 let instance: Option<_> = try {
897 let scope = frame.current_source_info()?.scope;
898 let inlined_parent = frame.body().source_scopes[scope].inlined_parent_scope?;
899 let source = &frame.body().source_scopes[inlined_parent];
900 source.inlined.expect("inlined_parent_scope points to scope without inline info").0
901 };
902 let instance = instance.unwrap_or(frame.instance());
904 let frame_crate = this.tcx.def_path(instance.def_id()).krate;
909 let crate_name = this.tcx.crate_name(frame_crate);
910 let crate_name = crate_name.as_str();
911 crate_name == "std" || crate_name == "std_miri_test"
913 }
914
915 fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) {
917 let this = self.eval_context_mut();
918 let provenance = mplace.ptr().into_pointer_or_addr().unwrap().provenance;
920 this.alloc_mark_immutable(provenance.get_alloc_id().unwrap()).unwrap();
921 }
922
923 fn get_twice_wide_int_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
925 let this = self.eval_context_ref();
926 match ty.kind() {
927 ty::Uint(UintTy::U8) => this.tcx.types.u16,
929 ty::Uint(UintTy::U16) => this.tcx.types.u32,
930 ty::Uint(UintTy::U32) => this.tcx.types.u64,
931 ty::Uint(UintTy::U64) => this.tcx.types.u128,
932 ty::Int(IntTy::I8) => this.tcx.types.i16,
934 ty::Int(IntTy::I16) => this.tcx.types.i32,
935 ty::Int(IntTy::I32) => this.tcx.types.i64,
936 ty::Int(IntTy::I64) => this.tcx.types.i128,
937 _ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"),
938 }
939 }
940
941 fn expect_target_feature_for_intrinsic(
946 &self,
947 intrinsic: Symbol,
948 target_feature: &str,
949 ) -> InterpResult<'tcx, ()> {
950 let this = self.eval_context_ref();
951 if !this.tcx.sess.unstable_target_features.contains(&Symbol::intern(target_feature)) {
952 throw_ub_format!(
953 "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}"
954 );
955 }
956 interp_ok(())
957 }
958
959 fn lookup_link_section(
962 &mut self,
963 include_name: impl Fn(&str) -> bool,
964 ) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> {
965 let this = self.eval_context_mut();
966 let tcx = this.tcx.tcx;
967
968 let mut array = vec![];
969
970 iter_exported_symbols(tcx, |_cnum, def_id| {
971 let attrs = tcx.codegen_fn_attrs(def_id);
972 let Some(link_section) = attrs.link_section else {
973 return interp_ok(());
974 };
975 if include_name(link_section.as_str()) {
976 let instance = ty::Instance::mono(tcx, def_id);
977 let span = tcx.def_span(def_id);
978 let const_val = this.eval_global(instance).unwrap_or_else(|err| {
979 panic!(
980 "failed to evaluate static in required link_section: {def_id:?}\n{err:?}"
981 )
982 });
983 match const_val.layout.ty.kind() {
984 ty::FnPtr(..) => {
985 array.push((this.read_immediate(&const_val)?, span));
986 }
987 ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => {
988 let mut elems = this.project_array_fields(&const_val)?;
989 while let Some((_idx, elem)) = elems.next(this)? {
990 array.push((this.read_immediate(&elem)?, span));
991 }
992 }
993 _ =>
994 throw_unsup_format!(
995 "only function pointers and arrays of function pointers are supported in well-known linker sections"
996 ),
997 }
998 }
999 interp_ok(())
1000 })?;
1001
1002 interp_ok(array)
1003 }
1004
1005 fn mangle_internal_symbol<'a>(&'a mut self, name: &'static str) -> &'a str
1006 where
1007 'tcx: 'a,
1008 {
1009 let this = self.eval_context_mut();
1010 let tcx = *this.tcx;
1011 this.machine
1012 .mangle_internal_symbol_cache
1013 .entry(name)
1014 .or_insert_with(|| mangle_internal_symbol(tcx, name))
1015 }
1016}
1017
1018impl<'tcx> MiriMachine<'tcx> {
1019 pub fn current_user_relevant_span(&self) -> Span {
1024 self.threads.active_thread_ref().current_user_relevant_span()
1025 }
1026
1027 pub fn caller_span(&self) -> Span {
1033 let frame_idx = self.top_user_relevant_frame().unwrap();
1036 let frame_idx = cmp::min(frame_idx, self.stack().len().saturating_sub(2));
1037 self.stack()[frame_idx].current_span()
1038 }
1039
1040 fn stack(&self) -> &[Frame<'tcx, Provenance, machine::FrameExtra<'tcx>>] {
1041 self.threads.active_thread_stack()
1042 }
1043
1044 fn top_user_relevant_frame(&self) -> Option<usize> {
1045 self.threads.active_thread_ref().top_user_relevant_frame()
1046 }
1047
1048 pub fn user_relevance(&self, frame: &Frame<'tcx, Provenance>) -> u8 {
1050 if frame.instance().def.requires_caller_location(self.tcx) {
1051 return 0;
1052 }
1053 if self.is_local(frame.instance()) {
1054 u8::MAX
1055 } else {
1056 1
1059 }
1060 }
1061}
1062
1063pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
1064 throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
1065 "{name} not available when isolation is enabled",
1066 )))
1067}
1068
1069pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar {
1070 let val = if b { -1 } else { 0 };
1074 Scalar::from_int(val, size)
1075}
1076
1077pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
1081 if success {
1082 u32::try_from(len.strict_sub(1)).unwrap()
1085 } else {
1086 u32::try_from(len).unwrap()
1089 }
1090}
1091
1092pub trait ToUsize {
1094 fn to_usize(self) -> usize;
1095}
1096
1097impl ToUsize for u32 {
1098 fn to_usize(self) -> usize {
1099 self.try_into().unwrap()
1100 }
1101}
1102
1103pub trait ToU64 {
1106 fn to_u64(self) -> u64;
1107}
1108
1109impl ToU64 for usize {
1110 fn to_u64(self) -> u64 {
1111 self.try_into().unwrap()
1112 }
1113}
1114
1115#[macro_export]
1121macro_rules! enter_trace_span {
1122 ($($tt:tt)*) => {
1123 rustc_const_eval::enter_trace_span!($crate::MiriMachine<'static>, $($tt)*)
1124 };
1125}