1use std::collections::hash_map::Entry;
2use std::io::Write;
3use std::path::Path;
4
5use rustc_abi::{Align, CanonAbi, ExternAbi, Size};
6use rustc_ast::expand::allocator::NO_ALLOC_SHIM_IS_UNSTABLE;
7use rustc_data_structures::either::Either;
8use rustc_hir::attrs::Linkage;
9use rustc_hir::def::DefKind;
10use rustc_hir::def_id::CrateNum;
11use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
12use rustc_middle::mir::interpret::AllocInit;
13use rustc_middle::ty::{Instance, Ty};
14use rustc_middle::{mir, ty};
15use rustc_span::Symbol;
16use rustc_target::callconv::FnAbi;
17use rustc_target::spec::Os;
18
19use super::alloc::EvalContextExt as _;
20use super::backtrace::EvalContextExt as _;
21use crate::concurrency::GenmcEvalContextExt as _;
22use crate::helpers::EvalContextExt as _;
23use crate::*;
24
25#[derive(Debug, Copy, Clone)]
27pub struct DynSym(Symbol);
28
29#[expect(clippy::should_implement_trait)]
30impl DynSym {
31 pub fn from_str(name: &str) -> Self {
32 DynSym(Symbol::intern(name))
33 }
34}
35
36impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
37pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
38 fn emulate_foreign_item(
45 &mut self,
46 link_name: Symbol,
47 abi: &FnAbi<'tcx, Ty<'tcx>>,
48 args: &[OpTy<'tcx>],
49 dest: &PlaceTy<'tcx>,
50 ret: Option<mir::BasicBlock>,
51 unwind: mir::UnwindAction,
52 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
53 let this = self.eval_context_mut();
54
55 if let Some(shim) = this.machine.allocator_shim_symbols.get(&link_name) {
57 match *shim {
58 Either::Left(other_fn) => {
59 let handler = this
60 .lookup_exported_symbol(other_fn)?
61 .expect("missing alloc error handler symbol");
62 return interp_ok(Some(handler));
63 }
64 Either::Right(special) => {
65 this.rust_special_allocator_method(special, link_name, abi, args, dest)?;
66 this.return_to_block(ret)?;
67 return interp_ok(None);
68 }
69 }
70 }
71
72 let dest = this.force_allocation(dest)?;
74
75 let res = this.emulate_foreign_item_inner(link_name, abi, args, &dest)?;
77 res.jump_to_next_block(this, &dest, ret, Some(unwind), |this| {
78 if let Some(body) = this.lookup_exported_symbol(link_name)? {
79 return interp_ok(Some(body));
80 }
81
82 throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(format!(
83 "can't call foreign function `{link_name}` on OS `{os}`",
84 os = this.tcx.sess.target.os,
85 )));
86 })
87 }
88
89 fn is_dyn_sym(&self, name: &str) -> bool {
90 let this = self.eval_context_ref();
91 match &this.tcx.sess.target.os {
92 os if this.target_os_is_unix() => shims::unix::foreign_items::is_dyn_sym(name, os),
93 Os::Windows => shims::windows::foreign_items::is_dyn_sym(name),
94 _ => false,
95 }
96 }
97
98 fn emulate_dyn_sym(
100 &mut self,
101 sym: DynSym,
102 abi: &FnAbi<'tcx, Ty<'tcx>>,
103 args: &[OpTy<'tcx>],
104 dest: &PlaceTy<'tcx>,
105 ret: Option<mir::BasicBlock>,
106 unwind: mir::UnwindAction,
107 ) -> InterpResult<'tcx> {
108 let res = self.emulate_foreign_item(sym.0, abi, args, dest, ret, unwind)?;
109 assert!(res.is_none(), "DynSyms that delegate are not supported");
110 interp_ok(())
111 }
112
113 fn lookup_exported_symbol(
115 &mut self,
116 link_name: Symbol,
117 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
118 let this = self.eval_context_mut();
119 let tcx = this.tcx.tcx;
120
121 let entry = this.machine.exported_symbols_cache.entry(link_name);
124 let instance = *match entry {
125 Entry::Occupied(e) => e.into_mut(),
126 Entry::Vacant(e) => {
127 struct SymbolTarget<'tcx> {
130 instance: ty::Instance<'tcx>,
131 cnum: CrateNum,
132 is_weak: bool,
133 }
134 let mut symbol_target: Option<SymbolTarget<'tcx>> = None;
135 helpers::iter_exported_symbols(tcx, |cnum, def_id| {
136 let attrs = tcx.codegen_fn_attrs(def_id);
137 if tcx.is_foreign_item(def_id) {
139 return interp_ok(());
140 }
141 if !(attrs.symbol_name.is_some()
143 || attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
144 || attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL))
145 {
146 return interp_ok(());
147 }
148
149 let instance = Instance::mono(tcx, def_id);
150 let symbol_name = tcx.symbol_name(instance).name;
151 let is_weak = attrs.linkage == Some(Linkage::WeakAny);
152 if symbol_name == link_name.as_str() {
153 if let Some(original) = &symbol_target {
154 match (is_weak, original.is_weak) {
157 (false, true) => {
158 symbol_target = Some(SymbolTarget {
161 instance: ty::Instance::mono(tcx, def_id),
162 cnum,
163 is_weak,
164 });
165 }
166 (true, false) => {
167 }
169 (true, true) | (false, false) => {
170 let original_span =
177 tcx.def_span(original.instance.def_id()).data();
178 let span = tcx.def_span(def_id).data();
179 if original_span < span {
180 throw_machine_stop!(
181 TerminationInfo::MultipleSymbolDefinitions {
182 link_name,
183 first: original_span,
184 first_crate: tcx.crate_name(original.cnum),
185 second: span,
186 second_crate: tcx.crate_name(cnum),
187 }
188 );
189 } else {
190 throw_machine_stop!(
191 TerminationInfo::MultipleSymbolDefinitions {
192 link_name,
193 first: span,
194 first_crate: tcx.crate_name(cnum),
195 second: original_span,
196 second_crate: tcx.crate_name(original.cnum),
197 }
198 );
199 }
200 }
201 }
202 } else {
203 symbol_target = Some(SymbolTarget {
204 instance: ty::Instance::mono(tcx, def_id),
205 cnum,
206 is_weak,
207 });
208 }
209 }
210 interp_ok(())
211 })?;
212
213 if let Some(SymbolTarget { instance, .. }) = symbol_target {
217 if !matches!(tcx.def_kind(instance.def_id()), DefKind::Fn | DefKind::AssocFn) {
218 throw_ub_format!(
219 "attempt to call an exported symbol that is not defined as a function"
220 );
221 }
222 }
223
224 e.insert(symbol_target.map(|SymbolTarget { instance, .. }| instance))
225 }
226 };
227 match instance {
228 None => interp_ok(None), Some(instance) => interp_ok(Some((this.load_mir(instance.def, None)?, instance))),
230 }
231 }
232}
233
234impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
235trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
236 fn emulate_foreign_item_inner(
237 &mut self,
238 link_name: Symbol,
239 abi: &FnAbi<'tcx, Ty<'tcx>>,
240 args: &[OpTy<'tcx>],
241 dest: &MPlaceTy<'tcx>,
242 ) -> InterpResult<'tcx, EmulateItemResult> {
243 let this = self.eval_context_mut();
244
245 #[cfg(all(feature = "native-lib", unix))]
247 if !this.machine.native_lib.is_empty() {
248 use crate::shims::native_lib::EvalContextExt as _;
249 if this.call_native_fn(link_name, dest, args)? {
253 return interp_ok(EmulateItemResult::NeedsReturn);
254 }
255 }
256 match link_name.as_str() {
295 name if name == this.mangle_internal_symbol(NO_ALLOC_SHIM_IS_UNSTABLE) => {
297 let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
300 }
301
302 "miri_alloc" => {
304 let [size, align] =
305 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
306 let size = this.read_target_usize(size)?;
307 let align = this.read_target_usize(align)?;
308
309 this.check_rust_alloc_request(size, align)?;
310
311 let ptr = this.allocate_ptr(
312 Size::from_bytes(size),
313 Align::from_bytes(align).unwrap(),
314 MiriMemoryKind::Miri.into(),
315 AllocInit::Uninit,
316 )?;
317
318 this.write_pointer(ptr, dest)?;
319 }
320 "miri_dealloc" => {
321 let [ptr, old_size, align] =
322 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
323 let ptr = this.read_pointer(ptr)?;
324 let old_size = this.read_target_usize(old_size)?;
325 let align = this.read_target_usize(align)?;
326
327 this.deallocate_ptr(
329 ptr,
330 Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
331 MiriMemoryKind::Miri.into(),
332 )?;
333 }
334 "miri_track_alloc" => {
335 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
336 let ptr = this.read_pointer(ptr)?;
337 let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
338 err_machine_stop!(TerminationInfo::Abort(format!(
339 "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
340 )))
341 })?;
342 if this.machine.tracked_alloc_ids.insert(alloc_id) {
343 let info = this.get_alloc_info(alloc_id);
344 this.emit_diagnostic(NonHaltingDiagnostic::TrackingAlloc(
345 alloc_id, info.size, info.align,
346 ));
347 }
348 }
349 "miri_start_unwind" => {
350 let [payload] =
351 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
352 this.handle_miri_start_unwind(payload)?;
353 return interp_ok(EmulateItemResult::NeedsUnwind);
354 }
355 "miri_run_provenance_gc" => {
356 let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
357 this.run_provenance_gc();
358 }
359 "miri_get_alloc_id" => {
360 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
361 let ptr = this.read_pointer(ptr)?;
362 let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
363 err_machine_stop!(TerminationInfo::Abort(format!(
364 "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
365 )))
366 })?;
367 this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?;
368 }
369 "miri_print_borrow_state" => {
370 let [id, show_unnamed] =
371 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
372 let id = this.read_scalar(id)?.to_u64()?;
373 let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?;
374 if let Some(id) = std::num::NonZero::new(id).map(AllocId)
375 && this.get_alloc_info(id).kind == AllocKind::LiveData
376 {
377 this.print_borrow_state(id, show_unnamed)?;
378 } else {
379 eprintln!("{id} is not the ID of a live data allocation");
380 }
381 }
382 "miri_pointer_name" => {
383 let [ptr, nth_parent, name] =
386 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
387 let ptr = this.read_pointer(ptr)?;
388 let nth_parent = this.read_scalar(nth_parent)?.to_u8()?;
389 let name = this.read_immediate(name)?;
390
391 let name = this.read_byte_slice(&name)?;
392 let name = String::from_utf8_lossy(name).into_owned();
396 this.give_pointer_debug_name(ptr, nth_parent, &name)?;
397 }
398 "miri_static_root" => {
399 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
400 let ptr = this.read_pointer(ptr)?;
401 let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
402 if offset != Size::ZERO {
403 throw_unsup_format!(
404 "pointer passed to `miri_static_root` must point to beginning of an allocated block"
405 );
406 }
407 this.machine.static_roots.push(alloc_id);
408 }
409 "miri_host_to_target_path" => {
410 let [ptr, out, out_size] =
411 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
412 let ptr = this.read_pointer(ptr)?;
413 let out = this.read_pointer(out)?;
414 let out_size = this.read_scalar(out_size)?.to_target_usize(this)?;
415
416 this.check_no_isolation("`miri_host_to_target_path`")?;
418
419 let path = this.read_os_str_from_c_str(ptr)?.to_owned();
421 let (success, needed_size) =
422 this.write_path_to_c_str(Path::new(&path), out, out_size)?;
423 this.write_int(if success { 0 } else { needed_size }, dest)?;
425 }
426 "miri_thread_spawn" => {
427 let [start_routine, func_arg] =
429 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
430 let start_routine = this.read_pointer(start_routine)?;
431 let func_arg = this.read_immediate(func_arg)?;
432
433 this.start_regular_thread(
434 Some(dest.clone()),
435 start_routine,
436 ExternAbi::Rust,
437 func_arg,
438 this.machine.layouts.unit,
439 )?;
440 }
441 "miri_thread_join" => {
442 let [thread_id] = this.check_shim_sig(
443 shim_sig!(extern "Rust" fn(usize) -> bool),
444 link_name,
445 abi,
446 args,
447 )?;
448
449 let thread = this.read_target_usize(thread_id)?;
450 use crate::concurrency::thread::ThreadLookupError;
452 let thread = match this.thread_id_try_from(thread) {
453 Ok(id) | Err(ThreadLookupError::Terminated(id)) => Some(id),
454 Err(ThreadLookupError::InvalidId) => None,
455 };
456 if let Some(thread) = thread {
457 this.join_thread_exclusive(
458 thread,
459 Scalar::from_bool(true),
460 dest,
461 )?;
462 } else {
463 this.write_scalar(Scalar::from_bool(false), dest)?;
464 }
465 }
466 "miri_spin_loop" => {
468 let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
469
470 this.yield_active_thread();
472 }
473 "miri_backtrace_size" => {
475 this.handle_miri_backtrace_size(abi, link_name, args, dest)?;
476 }
477 "miri_get_backtrace" => {
479 this.handle_miri_get_backtrace(abi, link_name, args)?;
481 }
482 "miri_resolve_frame" => {
484 this.handle_miri_resolve_frame(abi, link_name, args, dest)?;
486 }
487 "miri_resolve_frame_names" => {
489 this.handle_miri_resolve_frame_names(abi, link_name, args)?;
490 }
491 "miri_write_to_stdout" | "miri_write_to_stderr" => {
494 let [msg] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
495 let msg = this.read_immediate(msg)?;
496 let msg = this.read_byte_slice(&msg)?;
497 let _ignore = match link_name.as_str() {
499 "miri_write_to_stdout" => std::io::stdout().write_all(msg),
500 "miri_write_to_stderr" => std::io::stderr().write_all(msg),
501 _ => unreachable!(),
502 };
503 }
504 "miri_promise_symbolic_alignment" => {
506 use rustc_abi::AlignFromBytesError;
507
508 let [ptr, align] =
509 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
510 let ptr = this.read_pointer(ptr)?;
511 let align = this.read_target_usize(align)?;
512 if !align.is_power_of_two() {
513 throw_unsup_format!(
514 "`miri_promise_symbolic_alignment`: alignment must be a power of 2, got {align}"
515 );
516 }
517 let align = Align::from_bytes(align).unwrap_or_else(|err| {
518 match err {
519 AlignFromBytesError::NotPowerOfTwo(_) => unreachable!(),
520 AlignFromBytesError::TooLarge(_) => Align::MAX,
522 }
523 });
524 let addr = ptr.addr();
525 if addr.bytes().strict_rem(align.bytes()) != 0 {
527 throw_unsup_format!(
528 "`miri_promise_symbolic_alignment`: pointer is not actually aligned"
529 );
530 }
531 if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr, 0) {
532 let alloc_align = this.get_alloc_info(alloc_id).align;
533 if align > alloc_align
536 && this
537 .machine
538 .symbolic_alignment
539 .get_mut()
540 .get(&alloc_id)
541 .is_none_or(|&(_, old_align)| align > old_align)
542 {
543 this.machine.symbolic_alignment.get_mut().insert(alloc_id, (offset, align));
544 }
545 }
546 }
547 "miri_genmc_assume" => {
549 let [condition] =
550 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
551 if this.machine.data_race.as_genmc_ref().is_some() {
552 this.handle_genmc_verifier_assume(condition)?;
553 } else {
554 throw_unsup_format!("miri_genmc_assume is only supported in GenMC mode")
555 }
556 }
557
558 "exit" => {
560 let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
562 let code = this.read_scalar(code)?.to_i32()?;
563 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
564 genmc_ctx.handle_exit(
566 this.machine.threads.active_thread(),
567 code,
568 crate::concurrency::ExitType::ExitCalled,
569 )?;
570 return interp_ok(EmulateItemResult::AlreadyJumped);
571 }
572 throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false });
573 }
574 "abort" => {
575 let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
577 throw_machine_stop!(TerminationInfo::Abort(
578 "the program aborted execution".to_owned()
579 ));
580 }
581
582 "malloc" => {
584 let [size] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
585 let size = this.read_target_usize(size)?;
586 if size <= this.max_size_of_val().bytes() {
587 let res = this.malloc(size, AllocInit::Uninit)?;
588 this.write_pointer(res, dest)?;
589 } else {
590 if this.target_os_is_unix() {
592 this.set_last_error(LibcError("ENOMEM"))?;
593 }
594 this.write_null(dest)?;
595 }
596 }
597 "calloc" => {
598 let [items, elem_size] =
599 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
600 let items = this.read_target_usize(items)?;
601 let elem_size = this.read_target_usize(elem_size)?;
602 if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
603 let res = this.malloc(size.bytes(), AllocInit::Zero)?;
604 this.write_pointer(res, dest)?;
605 } else {
606 if this.target_os_is_unix() {
608 this.set_last_error(LibcError("ENOMEM"))?;
609 }
610 this.write_null(dest)?;
611 }
612 }
613 "free" => {
614 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
615 let ptr = this.read_pointer(ptr)?;
616 this.free(ptr)?;
617 }
618 "realloc" => {
619 let [old_ptr, new_size] =
620 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
621 let old_ptr = this.read_pointer(old_ptr)?;
622 let new_size = this.read_target_usize(new_size)?;
623 if new_size <= this.max_size_of_val().bytes() {
624 let res = this.realloc(old_ptr, new_size)?;
625 this.write_pointer(res, dest)?;
626 } else {
627 if this.target_os_is_unix() {
629 this.set_last_error(LibcError("ENOMEM"))?;
630 }
631 this.write_null(dest)?;
632 }
633 }
634
635 "memcmp" => {
637 let [left, right, n] =
638 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
639 let left = this.read_pointer(left)?;
640 let right = this.read_pointer(right)?;
641 let n = Size::from_bytes(this.read_target_usize(n)?);
642
643 this.ptr_get_alloc_id(left, 0)?;
645 this.ptr_get_alloc_id(right, 0)?;
646
647 let result = {
652 let left_bytes = this.read_bytes_ptr_strip_provenance(left, n)?;
653 let right_bytes = this.read_bytes_ptr_strip_provenance(right, n)?;
654
655 use std::cmp::Ordering::*;
656 match left_bytes.cmp(right_bytes) {
657 Less => -1i32,
658 Equal => 0,
659 Greater => 1,
660 }
661 };
662
663 this.write_scalar(Scalar::from_i32(result), dest)?;
664 }
665 "memchr" => {
666 let [ptr, val, num] =
667 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
668 let ptr = this.read_pointer(ptr)?;
669 let val = this.read_scalar(val)?.to_i32()?;
670 let num = this.read_target_usize(num)?;
671 #[expect(clippy::as_conversions)]
673 let val = val as u8;
674
675 this.ptr_get_alloc_id(ptr, 0)?;
677
678 let needle_ptr = this.memchr(ptr, 0..num, val)?.map(|(_idx, ptr)| ptr);
681
682 if let Some(needle_ptr) = needle_ptr {
683 this.write_pointer(needle_ptr, dest)?;
684 } else {
685 this.write_null(dest)?;
686 }
687 }
688 "memrchr" => {
689 this.check_target_os(&[Os::Linux, Os::Android, Os::FreeBsd], link_name)?;
690
691 let [ptr, val, num] =
692 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
693 let ptr = this.read_pointer(ptr)?;
694 let val = this.read_scalar(val)?.to_i32()?;
695 let num = this.read_target_usize(num)?;
696 #[expect(clippy::as_conversions)]
698 let val = val as u8;
699
700 this.ptr_get_alloc_id(ptr, 0)?;
702
703 let needle_ptr = this.memchr(ptr, (0..num).rev(), val)?.map(|(_idx, ptr)| ptr);
705
706 if let Some(needle_ptr) = needle_ptr {
707 this.write_pointer(needle_ptr, dest)?;
708 } else {
709 this.write_null(dest)?;
710 }
711 }
712 "strlen" => {
713 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
714 let ptr = this.read_pointer(ptr)?;
715 let n = this.read_c_str(ptr)?.len();
717 this.write_scalar(
718 Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
719 dest,
720 )?;
721 }
722 "strnlen" => {
723 let [ptr, num] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
724 let ptr = this.read_pointer(ptr)?;
725 let num = this.read_target_usize(num)?;
726
727 this.ptr_get_alloc_id(ptr, 0)?;
729
730 let idx = this.memchr(ptr, 0..num, 0)?.map(|(idx, _ptr)| idx).unwrap_or(num);
733 this.write_scalar(Scalar::from_target_usize(idx, this), dest)?;
734 }
735 "wcslen" => {
736 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
737 let ptr = this.read_pointer(ptr)?;
738 let n = this.read_wchar_t_str(ptr)?.len();
740 this.write_scalar(
741 Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
742 dest,
743 )?;
744 }
745 "memcpy" => {
746 let [ptr_dest, ptr_src, n] =
747 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
748 let ptr_dest = this.read_pointer(ptr_dest)?;
749 let ptr_src = this.read_pointer(ptr_src)?;
750 let n = this.read_target_usize(n)?;
751
752 this.ptr_get_alloc_id(ptr_dest, 0)?;
755 this.ptr_get_alloc_id(ptr_src, 0)?;
756
757 this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
758 this.write_pointer(ptr_dest, dest)?;
759 }
760 "strcpy" => {
761 let [ptr_dest, ptr_src] =
762 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
763 let ptr_dest = this.read_pointer(ptr_dest)?;
764 let ptr_src = this.read_pointer(ptr_src)?;
765
766 let n = this.read_c_str(ptr_src)?.len().strict_add(1);
773 this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
774 this.write_pointer(ptr_dest, dest)?;
775 }
776 "memset" => {
777 let [ptr_dest, val, n] =
778 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
779 let ptr_dest = this.read_pointer(ptr_dest)?;
780 let val = this.read_scalar(val)?.to_i32()?;
781 let n = this.read_target_usize(n)?;
782 #[expect(clippy::as_conversions)]
784 let val = val as u8;
785
786 this.ptr_get_alloc_id(ptr_dest, 0)?;
788
789 let bytes = std::iter::repeat_n(val, n.try_into().unwrap());
790 this.write_bytes_ptr(ptr_dest, bytes)?;
791 this.write_pointer(ptr_dest, dest)?;
792 }
793
794 _ => {
796 if let res = shims::math::EvalContextExt::emulate_foreign_item_inner(
798 this, link_name, abi, args, dest,
799 )? && !matches!(res, EmulateItemResult::NotSupported)
800 {
801 return interp_ok(res);
802 }
803
804 return match &this.tcx.sess.target.os {
806 _ if this.target_os_is_unix() =>
807 shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner(
808 this, link_name, abi, args, dest,
809 ),
810 Os::Windows =>
811 shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner(
812 this, link_name, abi, args, dest,
813 ),
814 _ => interp_ok(EmulateItemResult::NotSupported),
815 };
816 }
817 };
818 interp_ok(EmulateItemResult::NeedsReturn)
821 }
822
823 fn memchr(
826 &self,
827 ptr: Pointer,
828 idxs: impl Iterator<Item = u64>,
829 needle: u8,
830 ) -> InterpResult<'tcx, Option<(u64, Pointer)>> {
831 let this = self.eval_context_ref();
832 for idx in idxs {
833 let ptr = ptr.wrapping_offset(Size::from_bytes(idx), this);
834 let place = this.ptr_to_mplace(ptr, this.machine.layouts.u8);
835 let val = this.read_scalar(&place)?.to_u8()?;
836 if val == needle {
837 return interp_ok(Some((idx, ptr)));
838 }
839 }
840 interp_ok(None)
841 }
842}