1use std::collections::hash_map::Entry;
2use std::io::Write;
3use std::mem;
4use std::path::Path;
5
6use rustc_abi::{Align, ExternAbi, Size};
7use rustc_ast::expand::allocator::NO_ALLOC_SHIM_IS_UNSTABLE;
8use rustc_attr_ir::Linkage;
9use rustc_data_structures::either::Either;
10use rustc_hir::def::DefKind;
11use rustc_hir::def_id::CrateNum;
12use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
13use rustc_middle::mir::interpret::AllocInit;
14use rustc_middle::ty::{Instance, Ty};
15use rustc_middle::{mir, ty};
16use rustc_span::Symbol;
17use rustc_target::callconv::FnAbi;
18use rustc_target::spec::Os;
19
20use super::alloc::EvalContextExt as _;
21use super::backtrace::EvalContextExt as _;
22use crate::concurrency::GenmcEvalContextExt as _;
23use crate::eval::{ExitType, ProgramExitHandler};
24use crate::helpers::EvalContextExt as _;
25use crate::*;
26
27#[derive(Debug, Copy, Clone)]
29pub struct DynSym(Symbol);
30
31#[expect(clippy::should_implement_trait)]
32impl DynSym {
33 pub fn from_str(name: &str) -> Self {
34 DynSym(Symbol::intern(name))
35 }
36}
37
38impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
39pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
40 fn emulate_foreign_item(
47 &mut self,
48 link_name: Symbol,
49 abi: &FnAbi<'tcx, Ty<'tcx>>,
50 args: &[OpTy<'tcx>],
51 dest: &PlaceTy<'tcx>,
52 ret: Option<mir::BasicBlock>,
53 unwind: mir::UnwindAction,
54 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
55 let ecx = self.eval_context_mut();
56
57 if let Some(shim) = ecx.machine.allocator_shim_symbols.get(&link_name) {
59 match *shim {
60 Either::Left(other_fn) => {
61 let handler = ecx
62 .lookup_exported_fn(other_fn)?
63 .expect("missing alloc error handler symbol");
64 return interp_ok(Some(handler));
65 }
66 Either::Right(special) => {
67 ecx.rust_special_allocator_method(special, link_name, abi, args, dest)?;
68 ecx.return_to_block(ret)?;
69 return interp_ok(None);
70 }
71 }
72 }
73
74 let dest = ecx.force_allocation(dest)?;
76
77 let res = ecx.emulate_foreign_item_inner(link_name, abi, args, &dest)?;
79 res.jump_to_next_block(ecx, &dest.clone().into(), ret, Some(unwind), |ecx| {
80 if let Some(body) = ecx.lookup_exported_fn(link_name)? {
81 return interp_ok(Some(body));
82 }
83
84 throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(format!(
85 "can't call foreign function `{link_name}` on OS `{os}`",
86 os = ecx.tcx.sess.target.os,
87 )));
88 })
89 }
90
91 fn is_dyn_sym(&self, name: &str) -> bool {
92 let ecx = self.eval_context_ref();
93 match &ecx.tcx.sess.target.os {
94 _ if ecx.target_os_is_unix() =>
95 shims::unix::foreign_items::is_dyn_sym(name, &ecx.tcx.sess.target),
96 Os::Windows => shims::windows::foreign_items::is_dyn_sym(name),
97 _ => false,
98 }
99 }
100
101 fn emulate_dyn_sym(
103 &mut self,
104 sym: DynSym,
105 abi: &FnAbi<'tcx, Ty<'tcx>>,
106 args: &[OpTy<'tcx>],
107 dest: &PlaceTy<'tcx>,
108 ret: Option<mir::BasicBlock>,
109 unwind: mir::UnwindAction,
110 ) -> InterpResult<'tcx> {
111 let res = self.emulate_foreign_item(sym.0, abi, args, dest, ret, unwind)?;
112 assert!(res.is_none(), "DynSyms that delegate are not supported");
113 interp_ok(())
114 }
115
116 fn lookup_exported_symbol(
118 &self,
119 link_name: Symbol,
120 ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
121 let ecx = self.eval_context_ref();
122 let tcx = ecx.tcx.tcx;
123
124 let mut cache = ecx.machine.exported_symbols_cache.borrow_mut();
127 let entry = cache.entry(link_name);
128 let instance = *match entry {
129 Entry::Occupied(e) => e.into_mut(),
130 Entry::Vacant(e) => {
131 struct SymbolTarget<'tcx> {
134 instance: ty::Instance<'tcx>,
135 cnum: CrateNum,
136 is_weak: bool,
137 }
138 let mut symbol_target: Option<SymbolTarget<'tcx>> = None;
139 helpers::iter_exported_symbols(tcx, |cnum, def_id, _used| {
140 let attrs = tcx.codegen_fn_attrs(def_id);
141 if !(attrs.symbol_name.is_some()
143 || attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL))
144 {
145 return interp_ok(());
146 }
147
148 let instance = Instance::mono(tcx, def_id);
149 let symbol_name = tcx.symbol_name(instance).name;
150 let is_weak = attrs.linkage == Some(Linkage::WeakAny);
151 if symbol_name == link_name.as_str() {
152 if let Some(original) = &symbol_target {
153 match (is_weak, original.is_weak) {
156 (false, true) => {
157 symbol_target = Some(SymbolTarget {
160 instance: ty::Instance::mono(tcx, def_id),
161 cnum,
162 is_weak,
163 });
164 }
165 (true, false) => {
166 }
168 (true, true) | (false, false) => {
169 let original_span = tcx.def_span(original.instance.def_id());
176 let span = tcx.def_span(def_id);
177 if original_span.lo_hi() < span.lo_hi() {
178 throw_machine_stop!(
179 TerminationInfo::MultipleSymbolDefinitions {
180 link_name,
181 first: original_span,
182 first_crate: tcx.crate_name(original.cnum),
183 second: span,
184 second_crate: tcx.crate_name(cnum),
185 }
186 );
187 } else {
188 throw_machine_stop!(
189 TerminationInfo::MultipleSymbolDefinitions {
190 link_name,
191 first: span,
192 first_crate: tcx.crate_name(cnum),
193 second: original_span,
194 second_crate: tcx.crate_name(original.cnum),
195 }
196 );
197 }
198 }
199 }
200 } else {
201 symbol_target = Some(SymbolTarget {
202 instance: ty::Instance::mono(tcx, def_id),
203 cnum,
204 is_weak,
205 });
206 }
207 }
208 interp_ok(())
209 })?;
210
211 e.insert(symbol_target.map(|SymbolTarget { instance, .. }| instance))
212 }
213 };
214 drop(cache);
215 interp_ok(instance)
216 }
217
218 fn lookup_exported_fn(
220 &self,
221 link_name: Symbol,
222 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
223 let ecx = self.eval_context_ref();
224 let instance = ecx.lookup_exported_symbol(link_name)?;
225 if let Some(instance) = &instance {
226 if !matches!(ecx.tcx.def_kind(instance.def_id()), DefKind::Fn | DefKind::AssocFn) {
227 throw_ub_format!(
228 "attempt to call an exported symbol that is not defined as a function"
229 );
230 }
231 }
232 match instance {
233 None => interp_ok(None),
234 Some(instance) => interp_ok(Some((ecx.load_mir(instance.def, None)?, instance))),
235 }
236 }
237
238 fn lookup_exported_static(
240 &self,
241 link_name: Symbol,
242 ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
243 let ecx = self.eval_context_ref();
244 let instance = ecx.lookup_exported_symbol(link_name)?;
245 if let Some(instance) = &instance {
246 if !matches!(ecx.tcx.def_kind(instance.def_id()), DefKind::Static { .. }) {
247 throw_ub_format!(
248 "attempt to access an exported symbol `{link_name}` that is not defined as a static"
249 );
250 }
251 }
252 interp_ok(instance)
253 }
254}
255
256impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
257trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
258 fn emulate_foreign_item_inner(
259 &mut self,
260 link_name: Symbol,
261 abi: &FnAbi<'tcx, Ty<'tcx>>,
262 args: &[OpTy<'tcx>],
263 dest: &MPlaceTy<'tcx>,
264 ) -> InterpResult<'tcx, EmulateItemResult> {
265 let ecx = self.eval_context_mut();
266
267 #[cfg(all(feature = "native-lib", unix))]
269 if !ecx.machine.native_lib.is_empty() {
270 use crate::shims::native_lib::EvalContextExt as _;
271 if ecx.call_native_fn(link_name, dest, args)? {
275 return interp_ok(EmulateItemResult::NeedsReturn);
276 }
277 }
278 match link_name.as_str() {
317 name if name == ecx.mangle_internal_symbol(NO_ALLOC_SHIM_IS_UNSTABLE) => {
319 let [] = &ecx
322 .check_shim_sig(shim_sig!(extern "Rust" fn() -> ()), (link_name, abi, args))?;
323 }
324
325 "miri_alloc" => {
327 let [size, align] = &ecx.check_shim_sig(
328 shim_sig!(extern "Rust" fn(usize, usize) -> *()),
329 (link_name, abi, args),
330 )?;
331 let size = ecx.read_target_usize(size)?;
332 let align = ecx.read_target_usize(align)?;
333
334 ecx.check_rust_alloc_request(size, align)?;
335
336 let ptr = ecx.allocate_ptr(
337 Size::from_bytes(size),
338 Align::from_bytes(align).unwrap(),
339 MiriMemoryKind::Miri.into(),
340 AllocInit::Uninit,
341 )?;
342
343 ecx.write_pointer(ptr, dest)?;
344 }
345 "miri_dealloc" => {
346 let [ptr, old_size, align] = &ecx.check_shim_sig(
347 shim_sig!(extern "Rust" fn(*(), usize, usize) -> ()),
348 (link_name, abi, args),
349 )?;
350 let ptr = ecx.read_pointer(ptr)?;
351 let old_size = ecx.read_target_usize(old_size)?;
352 let align = ecx.read_target_usize(align)?;
353
354 ecx.deallocate_ptr(
356 ptr,
357 Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
358 MiriMemoryKind::Miri.into(),
359 )?;
360 }
361 "miri_track_alloc" => {
362 let [ptr] = &ecx.check_shim_sig(
363 shim_sig!(extern "Rust" fn(*()) -> ()),
364 (link_name, abi, args),
365 )?;
366 let ptr = ecx.read_pointer(ptr)?;
367 let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
368 err_machine_stop!(TerminationInfo::Abort(format!(
369 "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
370 )))
371 })?;
372 if ecx.machine.tracked_alloc_ids.insert(alloc_id) {
373 let info = ecx.get_alloc_info(alloc_id);
374 ecx.emit_diagnostic(NonHaltingDiagnostic::TrackingAlloc(
375 alloc_id, info.size, info.align,
376 ));
377 }
378 }
379 "miri_start_unwind" => {
380 let [payload] = &ecx.check_shim_sig(
381 shim_sig!(extern "Rust" fn(*()) -> !),
382 (link_name, abi, args),
383 )?;
384 ecx.handle_miri_start_unwind(payload)?;
385 return interp_ok(EmulateItemResult::NeedsUnwind);
386 }
387 "miri_run_provenance_gc" => {
388 let [] = &ecx
389 .check_shim_sig(shim_sig!(extern "Rust" fn() -> ()), (link_name, abi, args))?;
390 ecx.run_provenance_gc();
391 }
392 "miri_get_alloc_id" => {
393 let [ptr] = &ecx.check_shim_sig(
394 shim_sig!(extern "Rust" fn(*()) -> u64),
395 (link_name, abi, args),
396 )?;
397 let ptr = ecx.read_pointer(ptr)?;
398 let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
399 err_machine_stop!(TerminationInfo::Abort(format!(
400 "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
401 )))
402 })?;
403 ecx.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?;
404 }
405 "miri_print_borrow_state" => {
406 let [id, show_unnamed] = &ecx.check_shim_sig(
407 shim_sig!(extern "Rust" fn(u64, bool) -> ()),
408 (link_name, abi, args),
409 )?;
410 let id = ecx.read_scalar(id)?.to_u64()?;
411 let show_unnamed = ecx.read_scalar(show_unnamed)?.to_bool()?;
412 if let Some(id) = std::num::NonZero::new(id).map(AllocId)
413 && ecx.get_alloc_info(id).kind == AllocKind::LiveData
414 {
415 ecx.print_borrow_state(id, show_unnamed)?;
416 } else {
417 eprintln!("{id} is not the ID of a live data allocation");
418 }
419 }
420 "miri_pointer_name" => {
421 let [ptr, nth_parent, name] = &ecx.check_shim_sig(
424 shim_sig!(extern "Rust" fn(*(), u8, &[u8]) -> ()),
425 (link_name, abi, args),
426 )?;
427 let ptr = ecx.read_pointer(ptr)?;
428 let nth_parent = ecx.read_scalar(nth_parent)?.to_u8()?;
429 let name = ecx.read_immediate(name)?;
430
431 let name = ecx.read_byte_slice(&name)?;
432 let name = String::from_utf8_lossy(name).into_owned();
436 ecx.give_pointer_debug_name(ptr, nth_parent, &name)?;
437 }
438 "miri_static_root" => {
439 let [ptr] = &ecx.check_shim_sig(
440 shim_sig!(extern "Rust" fn(*()) -> ()),
441 (link_name, abi, args),
442 )?;
443 let ptr = ecx.read_pointer(ptr)?;
444 let (alloc_id, offset, _) = ecx.ptr_get_alloc_id(ptr, 0)?;
445 if offset != Size::ZERO {
446 throw_unsup_format!(
447 "pointer passed to `miri_static_root` must point to beginning of an allocated block"
448 );
449 }
450 ecx.machine.static_roots.push(alloc_id);
451 }
452 "miri_host_to_target_path" => {
453 let [ptr, out, out_size] = &ecx.check_shim_sig(
454 shim_sig!(extern "Rust" fn(*(), *(), usize) -> usize),
455 (link_name, abi, args),
456 )?;
457 let ptr = ecx.read_pointer(ptr)?;
458 let out = ecx.read_pointer(out)?;
459 let out_size = ecx.read_scalar(out_size)?.to_target_usize(ecx)?;
460
461 ecx.check_no_isolation("`miri_host_to_target_path`")?;
463
464 let path = ecx.read_os_str_from_c_str(ptr)?.to_owned();
466 let (success, needed_size) =
467 ecx.write_path_to_c_str(Path::new(&path), out, out_size)?;
468 ecx.write_int(if success { 0 } else { needed_size }, dest)?;
470 }
471 "miri_thread_spawn" => {
472 let [start_routine, func_arg] = &ecx.check_shim_sig(
473 shim_sig!(extern "Rust" fn(fn(..) -> _, *()) -> usize),
475 (link_name, abi, args),
476 )?;
477 let start_routine = ecx.read_pointer(start_routine)?;
478 let func_arg = ecx.read_immediate(func_arg)?;
479
480 ecx.start_regular_thread(
481 Some(dest.clone()),
482 start_routine,
483 ExternAbi::Rust,
484 func_arg,
485 ecx.machine.layouts.unit,
486 )?;
487 }
488 "miri_thread_join" => {
489 let [thread_id] = &ecx.check_shim_sig(
490 shim_sig!(extern "Rust" fn(usize) -> bool),
491 (link_name, abi, args),
492 )?;
493
494 let thread = ecx.read_target_usize(thread_id)?;
495 use crate::concurrency::thread::ThreadLookupError;
497 let thread = match ecx.thread_id_try_from(thread) {
498 Ok(id) | Err(ThreadLookupError::Terminated(id)) => Some(id),
499 Err(ThreadLookupError::InvalidId) => None,
500 };
501 if let Some(thread) = thread {
502 ecx.join_thread_exclusive(
503 thread,
504 Scalar::from_bool(true),
505 dest,
506 )?;
507 } else {
508 ecx.write_scalar(Scalar::from_bool(false), dest)?;
509 }
510 }
511 "miri_spin_loop" => {
513 let [] = &ecx
514 .check_shim_sig(shim_sig!(extern "Rust" fn() -> ()), (link_name, abi, args))?;
515
516 ecx.yield_active_thread();
518 }
519 "miri_backtrace_size" => {
521 ecx.handle_miri_backtrace_size(abi, link_name, args, dest)?;
522 }
523 "miri_get_backtrace" => {
525 ecx.handle_miri_get_backtrace(abi, link_name, args)?;
527 }
528 "miri_resolve_frame" => {
530 ecx.handle_miri_resolve_frame(abi, link_name, args, dest)?;
532 }
533 "miri_resolve_frame_names" => {
535 ecx.handle_miri_resolve_frame_names(abi, link_name, args)?;
536 }
537 "miri_write_to_stdout" | "miri_write_to_stderr" => {
539 let [msg] = &ecx.check_shim_sig(
540 shim_sig!(extern "Rust" fn(&[u8]) -> ()),
541 (link_name, abi, args),
542 )?;
543 let msg = ecx.read_immediate(msg)?;
544 let msg = ecx.read_byte_slice(&msg)?;
545 let _ignore = match link_name.as_str() {
547 "miri_write_to_stdout" => std::io::stdout().write_all(msg),
548 "miri_write_to_stderr" => std::io::stderr().write_all(msg),
549 _ => unreachable!(),
550 };
551 }
552 "miri_promise_symbolic_alignment" => {
554 use rustc_abi::AlignFromBytesError;
555
556 let [ptr, align] = &ecx.check_shim_sig(
557 shim_sig!(extern "Rust" fn(*(), usize) -> ()),
558 (link_name, abi, args),
559 )?;
560
561 let ptr = ecx.read_pointer(ptr)?;
562 let align = ecx.read_target_usize(align)?;
563 if !align.is_power_of_two() {
564 throw_unsup_format!(
565 "`miri_promise_symbolic_alignment`: alignment must be a power of 2, got {align}"
566 );
567 }
568 let align = Align::from_bytes(align).unwrap_or_else(|err| {
569 match err {
570 AlignFromBytesError::NotPowerOfTwo(_) => unreachable!(),
571 AlignFromBytesError::TooLarge(_) => Align::MAX,
573 }
574 });
575 let addr = ptr.addr();
576 if addr.bytes().strict_rem(align.bytes()) != 0 {
578 throw_unsup_format!(
579 "`miri_promise_symbolic_alignment`: pointer is not actually aligned"
580 );
581 }
582 if let Ok((alloc_id, offset, ..)) = ecx.ptr_try_get_alloc_id(ptr, 0) {
583 let alloc_align = ecx.get_alloc_info(alloc_id).align;
584 if align > alloc_align
587 && ecx
588 .machine
589 .symbolic_alignment
590 .get_mut()
591 .get(&alloc_id)
592 .is_none_or(|&(_, old_align)| align > old_align)
593 {
594 ecx.machine.symbolic_alignment.get_mut().insert(alloc_id, (offset, align));
595 }
596 }
597 }
598 "miri_genmc_assume" => {
600 let [condition] = &ecx.check_shim_sig(
601 shim_sig!(extern "Rust" fn(bool) -> ()),
602 (link_name, abi, args),
603 )?;
604
605 if ecx.machine.data_race.as_genmc_ref().is_some() {
606 ecx.handle_genmc_verifier_assume(condition)?;
607 } else {
608 throw_unsup_format!("miri_genmc_assume is only supported in GenMC mode")
609 }
610 }
611
612 "exit" => {
614 let [exit_code] = &ecx
615 .check_shim_sig(shim_sig!(extern "C" fn(i32) -> ()), (link_name, abi, args))?;
616 let exit_code = ecx.read_scalar(exit_code)?.to_i32()?;
617 let old_exit = mem::replace(&mut ecx.machine.exit_called, true);
619 if old_exit {
620 throw_ub_format!("`exit` called more than once");
621 }
622 ecx.replace_active_thread_stack({
624 let mut state_machine =
625 ProgramExitHandler::new(exit_code, ExitType::ExitCalled);
626 Box::new(move |m| state_machine.on_stack_empty(m))
627 });
628 return interp_ok(EmulateItemResult::AlreadyJumped);
630 }
631 "abort" => {
632 let [] =
634 &ecx.check_shim_sig(shim_sig!(extern "C" fn() -> ()), (link_name, abi, args))?;
635 throw_machine_stop!(TerminationInfo::Abort(
636 "the program aborted execution".to_owned()
637 ));
638 }
639
640 "malloc" => {
642 let [size] = &ecx.check_shim_sig(
643 shim_sig!(extern "C" fn(usize) -> *_),
644 (link_name, abi, args),
645 )?;
646 let size = ecx.read_target_usize(size)?;
647 if size <= ecx.max_size_of_val().bytes() {
648 let res = ecx.malloc(size, AllocInit::Uninit)?;
649 ecx.write_pointer(res, dest)?;
650 } else {
651 if ecx.target_os_is_unix() {
653 ecx.set_last_error(LibcError("ENOMEM"))?;
654 }
655 ecx.write_int(0, dest)?;
656 }
657 }
658 "calloc" => {
659 let [items, elem_size] = &ecx.check_shim_sig(
660 shim_sig!(extern "C" fn(usize, usize) -> *_),
661 (link_name, abi, args),
662 )?;
663 let items = ecx.read_target_usize(items)?;
664 let elem_size = ecx.read_target_usize(elem_size)?;
665 if let Some(size) = ecx.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
666 let res = ecx.malloc(size.bytes(), AllocInit::Zero)?;
667 ecx.write_pointer(res, dest)?;
668 } else {
669 if ecx.target_os_is_unix() {
671 ecx.set_last_error(LibcError("ENOMEM"))?;
672 }
673 ecx.write_int(0, dest)?;
674 }
675 }
676 "free" => {
677 let [ptr] = &ecx
678 .check_shim_sig(shim_sig!(extern "C" fn(*_) -> ()), (link_name, abi, args))?;
679 let ptr = ecx.read_pointer(ptr)?;
680 ecx.free(ptr)?;
681 }
682 "realloc" => {
683 let [old_ptr, new_size] = &ecx.check_shim_sig(
684 shim_sig!(extern "C" fn(*_, usize) -> *_),
685 (link_name, abi, args),
686 )?;
687 let old_ptr = ecx.read_pointer(old_ptr)?;
688 let new_size = ecx.read_target_usize(new_size)?;
689 if new_size <= ecx.max_size_of_val().bytes() {
690 let res = ecx.realloc(old_ptr, new_size)?;
691 ecx.write_pointer(res, dest)?;
692 } else {
693 if ecx.target_os_is_unix() {
695 ecx.set_last_error(LibcError("ENOMEM"))?;
696 }
697 ecx.write_int(0, dest)?;
698 }
699 }
700 "malloc_usable_size" => {
701 ecx.check_target_os(&[Os::Linux, Os::FreeBsd, Os::Android], link_name)?;
702
703 let [ptr] = &ecx.check_shim_sig(
704 shim_sig!(extern "C" fn(*_) -> usize),
705 (link_name, abi, args),
706 )?;
707 let ptr = ecx.read_pointer(ptr)?;
708 let size = if ecx.ptr_is_null(ptr)? {
709 0
710 } else {
711 let (alloc_id, offset, _) = ecx.ptr_get_alloc_id(ptr, 0)?;
712 if offset.bytes() != 0 {
713 throw_ub_format!(
714 "`malloc_usable_size` was called on a pointer that does not point to the beginning of its allocation"
715 );
716 }
717 let Some((alloc_kind, _)) = ecx.memory.alloc_map().get(alloc_id) else {
718 throw_ub_format!(
719 "`malloc_usable_size` was called on a pointer to memory not managed by the C allocator"
720 );
721 };
722 if *alloc_kind != MiriMemoryKind::C.into() {
723 throw_ub_format!(
724 "`malloc_usable_size` was called on a pointer to {alloc_kind} memory, which is not managed by the C allocator"
725 );
726 }
727 ecx.get_alloc_info(alloc_id).size.bytes()
728 };
729 ecx.write_scalar(Scalar::from_target_usize(size, ecx), dest)?;
730 }
731
732 "memcmp" => {
734 let [left, right, n] = &ecx.check_shim_sig(
735 shim_sig!(extern "C" fn(*_, *_, usize) -> i32),
736 (link_name, abi, args),
737 )?;
738 let left = ecx.read_pointer(left)?;
739 let right = ecx.read_pointer(right)?;
740 let n = Size::from_bytes(ecx.read_target_usize(n)?);
741
742 ecx.ptr_get_alloc_id(left, 0)?;
744 ecx.ptr_get_alloc_id(right, 0)?;
745
746 let result = {
751 let left_bytes = ecx.read_bytes_ptr_strip_provenance(left, n)?;
752 let right_bytes = ecx.read_bytes_ptr_strip_provenance(right, n)?;
753
754 use std::cmp::Ordering::*;
755 match left_bytes.cmp(right_bytes) {
756 Less => -1i32,
757 Equal => 0,
758 Greater => 1,
759 }
760 };
761
762 ecx.write_scalar(Scalar::from_i32(result), dest)?;
763 }
764 "memchr" => {
765 let [ptr, val, num] = &ecx.check_shim_sig(
766 shim_sig!(extern "C" fn(*_, i32, usize) -> *_),
767 (link_name, abi, args),
768 )?;
769 let ptr = ecx.read_pointer(ptr)?;
770 let val = ecx.read_scalar(val)?.to_i32()?;
771 let num = ecx.read_target_usize(num)?;
772 #[expect(clippy::as_conversions)]
774 let val = val as u8;
775
776 ecx.ptr_get_alloc_id(ptr, 0)?;
778
779 let needle_ptr = ecx.memchr(ptr, 0..num, val)?.map(|(_idx, ptr)| ptr);
782
783 if let Some(needle_ptr) = needle_ptr {
784 ecx.write_pointer(needle_ptr, dest)?;
785 } else {
786 ecx.write_int(0, dest)?;
787 }
788 }
789 "memrchr" => {
790 ecx.check_target_os(&[Os::Linux, Os::Android, Os::FreeBsd], link_name)?;
791
792 let [ptr, val, num] = &ecx.check_shim_sig(
793 shim_sig!(extern "C" fn(*_, i32, usize) -> *_),
794 (link_name, abi, args),
795 )?;
796 let ptr = ecx.read_pointer(ptr)?;
797 let val = ecx.read_scalar(val)?.to_i32()?;
798 let num = ecx.read_target_usize(num)?;
799 #[expect(clippy::as_conversions)]
801 let val = val as u8;
802
803 ecx.ptr_get_alloc_id(ptr, 0)?;
805
806 let needle_ptr = ecx.memchr(ptr, (0..num).rev(), val)?.map(|(_idx, ptr)| ptr);
808
809 if let Some(needle_ptr) = needle_ptr {
810 ecx.write_pointer(needle_ptr, dest)?;
811 } else {
812 ecx.write_int(0, dest)?;
813 }
814 }
815 "strlen" => {
816 let [ptr] = &ecx.check_shim_sig(
817 shim_sig!(extern "C" fn(*_) -> usize),
818 (link_name, abi, args),
819 )?;
820 let ptr = ecx.read_pointer(ptr)?;
821 let n = ecx.read_c_str(ptr)?.len();
823 ecx.write_scalar(Scalar::from_target_usize(u64::try_from(n).unwrap(), ecx), dest)?;
824 }
825 "strnlen" => {
826 let [ptr, num] = &ecx.check_shim_sig(
827 shim_sig!(extern "C" fn(*_, usize) -> usize),
828 (link_name, abi, args),
829 )?;
830 let ptr = ecx.read_pointer(ptr)?;
831 let num = ecx.read_target_usize(num)?;
832
833 ecx.ptr_get_alloc_id(ptr, 0)?;
835
836 let idx = ecx.memchr(ptr, 0..num, 0)?.map(|(idx, _ptr)| idx).unwrap_or(num);
839 ecx.write_scalar(Scalar::from_target_usize(idx, ecx), dest)?;
840 }
841 "wcslen" => {
842 let [ptr] = &ecx.check_shim_sig(
843 shim_sig!(extern "C" fn(*_) -> usize),
844 (link_name, abi, args),
845 )?;
846 let ptr = ecx.read_pointer(ptr)?;
847 let n = ecx.read_wchar_t_str(ptr)?.len();
849 ecx.write_scalar(Scalar::from_target_usize(u64::try_from(n).unwrap(), ecx), dest)?;
850 }
851 "memcpy" => {
852 let [ptr_dest, ptr_src, n] = &ecx.check_shim_sig(
853 shim_sig!(extern "C" fn(*_, *_, usize) -> *_),
854 (link_name, abi, args),
855 )?;
856 let ptr_dest = ecx.read_pointer(ptr_dest)?;
857 let ptr_src = ecx.read_pointer(ptr_src)?;
858 let n = ecx.read_target_usize(n)?;
859
860 ecx.ptr_get_alloc_id(ptr_dest, 0)?;
863 ecx.ptr_get_alloc_id(ptr_src, 0)?;
864
865 ecx.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
866 ecx.write_pointer(ptr_dest, dest)?;
867 }
868 "strcpy" => {
869 let [ptr_dest, ptr_src] = &ecx.check_shim_sig(
870 shim_sig!(extern "C" fn(*_, *_) -> *_),
871 (link_name, abi, args),
872 )?;
873 let ptr_dest = ecx.read_pointer(ptr_dest)?;
874 let ptr_src = ecx.read_pointer(ptr_src)?;
875
876 let n = ecx.read_c_str(ptr_src)?.len().strict_add(1);
883 ecx.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
884 ecx.write_pointer(ptr_dest, dest)?;
885 }
886 "memset" => {
887 let [ptr_dest, val, n] = &ecx.check_shim_sig(
888 shim_sig!(extern "C" fn(*_, i32, usize) -> *_),
889 (link_name, abi, args),
890 )?;
891 let ptr_dest = ecx.read_pointer(ptr_dest)?;
892 let val = ecx.read_scalar(val)?.to_i32()?;
893 let n = ecx.read_target_usize(n)?;
894 #[expect(clippy::as_conversions)]
896 let val = val as u8;
897
898 ecx.ptr_get_alloc_id(ptr_dest, 0)?;
900
901 let bytes = std::iter::repeat_n(val, n.try_into().unwrap());
902 ecx.write_bytes_ptr(ptr_dest, bytes)?;
903 ecx.write_pointer(ptr_dest, dest)?;
904 }
905
906 _ => {
908 if let res = shims::math::EvalContextExt::emulate_foreign_item_inner(
910 ecx, link_name, abi, args, dest,
911 )? && !matches!(res, EmulateItemResult::NotSupported)
912 {
913 return interp_ok(res);
914 }
915
916 return match &ecx.tcx.sess.target.os {
918 _ if ecx.target_os_is_unix() =>
919 shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner(
920 ecx, link_name, abi, args, dest,
921 ),
922 Os::Windows =>
923 shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner(
924 ecx, link_name, abi, args, dest,
925 ),
926 _ => interp_ok(EmulateItemResult::NotSupported),
927 };
928 }
929 };
930 interp_ok(EmulateItemResult::NeedsReturn)
933 }
934
935 fn memchr(
938 &self,
939 ptr: Pointer,
940 idxs: impl Iterator<Item = u64>,
941 needle: u8,
942 ) -> InterpResult<'tcx, Option<(u64, Pointer)>> {
943 let ecx = self.eval_context_ref();
944 for idx in idxs {
945 let ptr = ptr.wrapping_offset(Size::from_bytes(idx), ecx);
946 let place = ecx.ptr_to_mplace(ptr, ecx.machine.layouts.u8);
947 let val = ecx.read_scalar(&place)?.to_u8()?;
948 if val == needle {
949 return interp_ok(Some((idx, ptr)));
950 }
951 }
952 interp_ok(None)
953 }
954}