1use std::collections::hash_map::Entry;
2use std::io::Write;
3use std::path::Path;
4
5use rustc_abi::{Align, CanonAbi, Endian, 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::{Arch, 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 match this.emulate_foreign_item_inner(link_name, abi, args, &dest)? {
77 EmulateItemResult::NeedsReturn => {
78 trace!("{:?}", this.dump_place(&dest.clone().into()));
79 this.return_to_block(ret)?;
80 }
81 EmulateItemResult::NeedsUnwind => {
82 this.unwind_to_block(unwind)?;
84 }
85 EmulateItemResult::AlreadyJumped => (),
86 EmulateItemResult::NotSupported => {
87 if let Some(body) = this.lookup_exported_symbol(link_name)? {
88 return interp_ok(Some(body));
89 }
90
91 throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(format!(
92 "can't call foreign function `{link_name}` on OS `{os}`",
93 os = this.tcx.sess.target.os,
94 )));
95 }
96 }
97
98 interp_ok(None)
99 }
100
101 fn is_dyn_sym(&self, name: &str) -> bool {
102 let this = self.eval_context_ref();
103 match &this.tcx.sess.target.os {
104 os if this.target_os_is_unix() => shims::unix::foreign_items::is_dyn_sym(name, os),
105 Os::Windows => shims::windows::foreign_items::is_dyn_sym(name),
106 _ => false,
107 }
108 }
109
110 fn emulate_dyn_sym(
112 &mut self,
113 sym: DynSym,
114 abi: &FnAbi<'tcx, Ty<'tcx>>,
115 args: &[OpTy<'tcx>],
116 dest: &PlaceTy<'tcx>,
117 ret: Option<mir::BasicBlock>,
118 unwind: mir::UnwindAction,
119 ) -> InterpResult<'tcx> {
120 let res = self.emulate_foreign_item(sym.0, abi, args, dest, ret, unwind)?;
121 assert!(res.is_none(), "DynSyms that delegate are not supported");
122 interp_ok(())
123 }
124
125 fn lookup_exported_symbol(
127 &mut self,
128 link_name: Symbol,
129 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
130 let this = self.eval_context_mut();
131 let tcx = this.tcx.tcx;
132
133 let entry = this.machine.exported_symbols_cache.entry(link_name);
136 let instance = *match entry {
137 Entry::Occupied(e) => e.into_mut(),
138 Entry::Vacant(e) => {
139 struct SymbolTarget<'tcx> {
142 instance: ty::Instance<'tcx>,
143 cnum: CrateNum,
144 is_weak: bool,
145 }
146 let mut symbol_target: Option<SymbolTarget<'tcx>> = None;
147 helpers::iter_exported_symbols(tcx, |cnum, def_id| {
148 let attrs = tcx.codegen_fn_attrs(def_id);
149 if tcx.is_foreign_item(def_id) {
151 return interp_ok(());
152 }
153 if !(attrs.symbol_name.is_some()
155 || attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
156 || attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL))
157 {
158 return interp_ok(());
159 }
160
161 let instance = Instance::mono(tcx, def_id);
162 let symbol_name = tcx.symbol_name(instance).name;
163 let is_weak = attrs.linkage == Some(Linkage::WeakAny);
164 if symbol_name == link_name.as_str() {
165 if let Some(original) = &symbol_target {
166 match (is_weak, original.is_weak) {
169 (false, true) => {
170 symbol_target = Some(SymbolTarget {
173 instance: ty::Instance::mono(tcx, def_id),
174 cnum,
175 is_weak,
176 });
177 }
178 (true, false) => {
179 }
181 (true, true) | (false, false) => {
182 let original_span =
189 tcx.def_span(original.instance.def_id()).data();
190 let span = tcx.def_span(def_id).data();
191 if original_span < span {
192 throw_machine_stop!(
193 TerminationInfo::MultipleSymbolDefinitions {
194 link_name,
195 first: original_span,
196 first_crate: tcx.crate_name(original.cnum),
197 second: span,
198 second_crate: tcx.crate_name(cnum),
199 }
200 );
201 } else {
202 throw_machine_stop!(
203 TerminationInfo::MultipleSymbolDefinitions {
204 link_name,
205 first: span,
206 first_crate: tcx.crate_name(cnum),
207 second: original_span,
208 second_crate: tcx.crate_name(original.cnum),
209 }
210 );
211 }
212 }
213 }
214 } else {
215 symbol_target = Some(SymbolTarget {
216 instance: ty::Instance::mono(tcx, def_id),
217 cnum,
218 is_weak,
219 });
220 }
221 }
222 interp_ok(())
223 })?;
224
225 if let Some(SymbolTarget { instance, .. }) = symbol_target {
229 if !matches!(tcx.def_kind(instance.def_id()), DefKind::Fn | DefKind::AssocFn) {
230 throw_ub_format!(
231 "attempt to call an exported symbol that is not defined as a function"
232 );
233 }
234 }
235
236 e.insert(symbol_target.map(|SymbolTarget { instance, .. }| instance))
237 }
238 };
239 match instance {
240 None => interp_ok(None), Some(instance) => interp_ok(Some((this.load_mir(instance.def, None)?, instance))),
242 }
243 }
244
245 fn call_llvm_intrinsic(
247 &mut self,
248 instance: ty::Instance<'tcx>,
249 args: &[OpTy<'tcx>],
250 dest: &PlaceTy<'tcx>,
251 ret: Option<mir::BasicBlock>,
252 ) -> InterpResult<'tcx> {
253 let this = self.eval_context_mut();
254
255 let link_name = this.tcx.codegen_fn_attrs(instance.def_id()).symbol_name.unwrap();
256
257 let dest = this.force_allocation(dest)?;
259
260 let handled = match link_name.as_str() {
261 "llvm.prefetch.p0" => {
263 let [p, rw, loc, ty] = this.check_shim_sig_unadjusted(link_name, args)?;
264
265 let _ = this.read_pointer(p)?;
266 let rw = this.read_scalar(rw)?.to_i32()?;
267 let loc = this.read_scalar(loc)?.to_i32()?;
268 let ty = this.read_scalar(ty)?.to_i32()?;
269
270 if ty == 1 {
271 if !matches!(rw, 0 | 1) {
275 throw_unsup_format!("invalid `rw` value passed to `llvm.prefetch`: {}", rw);
276 }
277 if !matches!(loc, 0..=3) {
278 throw_unsup_format!(
279 "invalid `loc` value passed to `llvm.prefetch`: {}",
280 loc
281 );
282 }
283 } else {
284 throw_unsup_format!("unsupported `llvm.prefetch` type argument: {}", ty);
285 }
286
287 true
288 }
289 name if name.starts_with("llvm.ctpop.v")
292 && this.tcx.sess.target.endian == Endian::Little =>
293 {
294 let [op] = this.check_shim_sig_unadjusted(link_name, args)?;
295
296 let (op, op_len) = this.project_to_simd(op)?;
297 let (dest, dest_len) = this.project_to_simd(&dest)?;
298
299 assert_eq!(dest_len, op_len);
300
301 for i in 0..dest_len {
302 let op = this.read_immediate(&this.project_index(&op, i)?)?;
303 let res = op.to_scalar().to_uint(op.layout.size)?.count_ones();
306
307 this.write_scalar(
308 Scalar::from_uint(res, op.layout.size),
309 &this.project_index(&dest, i)?,
310 )?;
311 }
312
313 true
314 }
315
316 name if name.starts_with("llvm.x86.")
318 && matches!(this.tcx.sess.target.arch, Arch::X86 | Arch::X86_64)
319 && this.tcx.sess.target.endian == Endian::Little =>
320 shims::x86::EvalContextExt::emulate_x86_intrinsic(this, link_name, args, &dest)?,
321 name if name.starts_with("llvm.aarch64.")
322 && this.tcx.sess.target.arch == Arch::AArch64
323 && this.tcx.sess.target.endian == Endian::Little =>
324 shims::aarch64::EvalContextExt::emulate_aarch64_intrinsic(
325 this, link_name, args, &dest,
326 )?,
327 name if name.starts_with("llvm.loongarch.")
328 && matches!(this.tcx.sess.target.arch, Arch::LoongArch32 | Arch::LoongArch64)
329 && this.tcx.sess.target.endian == Endian::Little =>
330 shims::loongarch::EvalContextExt::emulate_loongarch_intrinsic(
331 this, link_name, args, &dest,
332 )?,
333 _ => false,
334 };
335
336 if handled {
338 trace!("{:?}", this.dump_place(&dest.clone().into()));
339 this.return_to_block(ret)
340 } else {
341 throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(format!(
342 "can't call LLVM intrinsic `{link_name}` on architecture `{arch}`",
343 arch = this.tcx.sess.target.arch,
344 )));
345 }
346 }
347}
348
349impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
350trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
351 fn emulate_foreign_item_inner(
352 &mut self,
353 link_name: Symbol,
354 abi: &FnAbi<'tcx, Ty<'tcx>>,
355 args: &[OpTy<'tcx>],
356 dest: &MPlaceTy<'tcx>,
357 ) -> InterpResult<'tcx, EmulateItemResult> {
358 let this = self.eval_context_mut();
359
360 #[cfg(all(feature = "native-lib", unix))]
362 if !this.machine.native_lib.is_empty() {
363 use crate::shims::native_lib::EvalContextExt as _;
364 if this.call_native_fn(link_name, dest, args)? {
368 return interp_ok(EmulateItemResult::NeedsReturn);
369 }
370 }
371 match link_name.as_str() {
410 name if name == this.mangle_internal_symbol(NO_ALLOC_SHIM_IS_UNSTABLE) => {
412 let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
415 }
416
417 "miri_alloc" => {
419 let [size, align] =
420 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
421 let size = this.read_target_usize(size)?;
422 let align = this.read_target_usize(align)?;
423
424 this.check_rust_alloc_request(size, align)?;
425
426 let ptr = this.allocate_ptr(
427 Size::from_bytes(size),
428 Align::from_bytes(align).unwrap(),
429 MiriMemoryKind::Miri.into(),
430 AllocInit::Uninit,
431 )?;
432
433 this.write_pointer(ptr, dest)?;
434 }
435 "miri_dealloc" => {
436 let [ptr, old_size, align] =
437 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
438 let ptr = this.read_pointer(ptr)?;
439 let old_size = this.read_target_usize(old_size)?;
440 let align = this.read_target_usize(align)?;
441
442 this.deallocate_ptr(
444 ptr,
445 Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
446 MiriMemoryKind::Miri.into(),
447 )?;
448 }
449 "miri_track_alloc" => {
450 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
451 let ptr = this.read_pointer(ptr)?;
452 let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
453 err_machine_stop!(TerminationInfo::Abort(format!(
454 "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
455 )))
456 })?;
457 if this.machine.tracked_alloc_ids.insert(alloc_id) {
458 let info = this.get_alloc_info(alloc_id);
459 this.emit_diagnostic(NonHaltingDiagnostic::TrackingAlloc(
460 alloc_id, info.size, info.align,
461 ));
462 }
463 }
464 "miri_start_unwind" => {
465 let [payload] =
466 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
467 this.handle_miri_start_unwind(payload)?;
468 return interp_ok(EmulateItemResult::NeedsUnwind);
469 }
470 "miri_run_provenance_gc" => {
471 let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
472 this.run_provenance_gc();
473 }
474 "miri_get_alloc_id" => {
475 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
476 let ptr = this.read_pointer(ptr)?;
477 let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
478 err_machine_stop!(TerminationInfo::Abort(format!(
479 "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
480 )))
481 })?;
482 this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?;
483 }
484 "miri_print_borrow_state" => {
485 let [id, show_unnamed] =
486 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
487 let id = this.read_scalar(id)?.to_u64()?;
488 let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?;
489 if let Some(id) = std::num::NonZero::new(id).map(AllocId)
490 && this.get_alloc_info(id).kind == AllocKind::LiveData
491 {
492 this.print_borrow_state(id, show_unnamed)?;
493 } else {
494 eprintln!("{id} is not the ID of a live data allocation");
495 }
496 }
497 "miri_pointer_name" => {
498 let [ptr, nth_parent, name] =
501 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
502 let ptr = this.read_pointer(ptr)?;
503 let nth_parent = this.read_scalar(nth_parent)?.to_u8()?;
504 let name = this.read_immediate(name)?;
505
506 let name = this.read_byte_slice(&name)?;
507 let name = String::from_utf8_lossy(name).into_owned();
511 this.give_pointer_debug_name(ptr, nth_parent, &name)?;
512 }
513 "miri_static_root" => {
514 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
515 let ptr = this.read_pointer(ptr)?;
516 let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
517 if offset != Size::ZERO {
518 throw_unsup_format!(
519 "pointer passed to `miri_static_root` must point to beginning of an allocated block"
520 );
521 }
522 this.machine.static_roots.push(alloc_id);
523 }
524 "miri_host_to_target_path" => {
525 let [ptr, out, out_size] =
526 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
527 let ptr = this.read_pointer(ptr)?;
528 let out = this.read_pointer(out)?;
529 let out_size = this.read_scalar(out_size)?.to_target_usize(this)?;
530
531 this.check_no_isolation("`miri_host_to_target_path`")?;
533
534 let path = this.read_os_str_from_c_str(ptr)?.to_owned();
536 let (success, needed_size) =
537 this.write_path_to_c_str(Path::new(&path), out, out_size)?;
538 this.write_int(if success { 0 } else { needed_size }, dest)?;
540 }
541 "miri_thread_spawn" => {
542 let [start_routine, func_arg] =
544 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
545 let start_routine = this.read_pointer(start_routine)?;
546 let func_arg = this.read_immediate(func_arg)?;
547
548 this.start_regular_thread(
549 Some(dest.clone()),
550 start_routine,
551 ExternAbi::Rust,
552 func_arg,
553 this.machine.layouts.unit,
554 )?;
555 }
556 "miri_thread_join" => {
557 let [thread_id] = this.check_shim_sig(
558 shim_sig!(extern "Rust" fn(usize) -> bool),
559 link_name,
560 abi,
561 args,
562 )?;
563
564 let thread = this.read_target_usize(thread_id)?;
565 use crate::concurrency::thread::ThreadLookupError;
567 let thread = match this.thread_id_try_from(thread) {
568 Ok(id) | Err(ThreadLookupError::Terminated(id)) => Some(id),
569 Err(ThreadLookupError::InvalidId) => None,
570 };
571 if let Some(thread) = thread {
572 this.join_thread_exclusive(
573 thread,
574 Scalar::from_bool(true),
575 dest,
576 )?;
577 } else {
578 this.write_scalar(Scalar::from_bool(false), dest)?;
579 }
580 }
581 "miri_spin_loop" => {
583 let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
584
585 this.yield_active_thread();
587 }
588 "miri_backtrace_size" => {
590 this.handle_miri_backtrace_size(abi, link_name, args, dest)?;
591 }
592 "miri_get_backtrace" => {
594 this.handle_miri_get_backtrace(abi, link_name, args)?;
596 }
597 "miri_resolve_frame" => {
599 this.handle_miri_resolve_frame(abi, link_name, args, dest)?;
601 }
602 "miri_resolve_frame_names" => {
604 this.handle_miri_resolve_frame_names(abi, link_name, args)?;
605 }
606 "miri_write_to_stdout" | "miri_write_to_stderr" => {
609 let [msg] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
610 let msg = this.read_immediate(msg)?;
611 let msg = this.read_byte_slice(&msg)?;
612 let _ignore = match link_name.as_str() {
614 "miri_write_to_stdout" => std::io::stdout().write_all(msg),
615 "miri_write_to_stderr" => std::io::stderr().write_all(msg),
616 _ => unreachable!(),
617 };
618 }
619 "miri_promise_symbolic_alignment" => {
621 use rustc_abi::AlignFromBytesError;
622
623 let [ptr, align] =
624 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
625 let ptr = this.read_pointer(ptr)?;
626 let align = this.read_target_usize(align)?;
627 if !align.is_power_of_two() {
628 throw_unsup_format!(
629 "`miri_promise_symbolic_alignment`: alignment must be a power of 2, got {align}"
630 );
631 }
632 let align = Align::from_bytes(align).unwrap_or_else(|err| {
633 match err {
634 AlignFromBytesError::NotPowerOfTwo(_) => unreachable!(),
635 AlignFromBytesError::TooLarge(_) => Align::MAX,
637 }
638 });
639 let addr = ptr.addr();
640 if addr.bytes().strict_rem(align.bytes()) != 0 {
642 throw_unsup_format!(
643 "`miri_promise_symbolic_alignment`: pointer is not actually aligned"
644 );
645 }
646 if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr, 0) {
647 let alloc_align = this.get_alloc_info(alloc_id).align;
648 if align > alloc_align
651 && this
652 .machine
653 .symbolic_alignment
654 .get_mut()
655 .get(&alloc_id)
656 .is_none_or(|&(_, old_align)| align > old_align)
657 {
658 this.machine.symbolic_alignment.get_mut().insert(alloc_id, (offset, align));
659 }
660 }
661 }
662 "miri_genmc_assume" => {
664 let [condition] =
665 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
666 if this.machine.data_race.as_genmc_ref().is_some() {
667 this.handle_genmc_verifier_assume(condition)?;
668 } else {
669 throw_unsup_format!("miri_genmc_assume is only supported in GenMC mode")
670 }
671 }
672
673 "exit" => {
675 let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
677 let code = this.read_scalar(code)?.to_i32()?;
678 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
679 genmc_ctx.handle_exit(
681 this.machine.threads.active_thread(),
682 code,
683 crate::concurrency::ExitType::ExitCalled,
684 )?;
685 return interp_ok(EmulateItemResult::AlreadyJumped);
686 }
687 throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false });
688 }
689 "abort" => {
690 let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
692 throw_machine_stop!(TerminationInfo::Abort(
693 "the program aborted execution".to_owned()
694 ));
695 }
696
697 "malloc" => {
699 let [size] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
700 let size = this.read_target_usize(size)?;
701 if size <= this.max_size_of_val().bytes() {
702 let res = this.malloc(size, AllocInit::Uninit)?;
703 this.write_pointer(res, dest)?;
704 } else {
705 if this.target_os_is_unix() {
707 this.set_last_error(LibcError("ENOMEM"))?;
708 }
709 this.write_null(dest)?;
710 }
711 }
712 "calloc" => {
713 let [items, elem_size] =
714 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
715 let items = this.read_target_usize(items)?;
716 let elem_size = this.read_target_usize(elem_size)?;
717 if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
718 let res = this.malloc(size.bytes(), AllocInit::Zero)?;
719 this.write_pointer(res, dest)?;
720 } else {
721 if this.target_os_is_unix() {
723 this.set_last_error(LibcError("ENOMEM"))?;
724 }
725 this.write_null(dest)?;
726 }
727 }
728 "free" => {
729 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
730 let ptr = this.read_pointer(ptr)?;
731 this.free(ptr)?;
732 }
733 "realloc" => {
734 let [old_ptr, new_size] =
735 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
736 let old_ptr = this.read_pointer(old_ptr)?;
737 let new_size = this.read_target_usize(new_size)?;
738 if new_size <= this.max_size_of_val().bytes() {
739 let res = this.realloc(old_ptr, new_size)?;
740 this.write_pointer(res, dest)?;
741 } else {
742 if this.target_os_is_unix() {
744 this.set_last_error(LibcError("ENOMEM"))?;
745 }
746 this.write_null(dest)?;
747 }
748 }
749
750 "memcmp" => {
752 let [left, right, n] =
754 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
755 let left = this.read_pointer(left)?;
756 let right = this.read_pointer(right)?;
757 let n = Size::from_bytes(this.read_target_usize(n)?);
758
759 this.ptr_get_alloc_id(left, 0)?;
761 this.ptr_get_alloc_id(right, 0)?;
762
763 let result = {
764 let left_bytes = this.read_bytes_ptr_strip_provenance(left, n)?;
767 let right_bytes = this.read_bytes_ptr_strip_provenance(right, n)?;
768
769 use std::cmp::Ordering::*;
770 match left_bytes.cmp(right_bytes) {
771 Less => -1i32,
772 Equal => 0,
773 Greater => 1,
774 }
775 };
776
777 this.write_scalar(Scalar::from_i32(result), dest)?;
778 }
779 "memchr" => {
780 let [ptr, val, num] =
781 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
782 let ptr = this.read_pointer(ptr)?;
783 let val = this.read_scalar(val)?.to_i32()?;
784 let num = this.read_target_usize(num)?;
785 #[expect(clippy::as_conversions)]
787 let val = val as u8;
788
789 this.ptr_get_alloc_id(ptr, 0)?;
791
792 let needle_ptr = this.memchr(ptr, 0..num, val)?.map(|(_idx, ptr)| ptr);
796
797 if let Some(needle_ptr) = needle_ptr {
798 this.write_pointer(needle_ptr, dest)?;
799 } else {
800 this.write_null(dest)?;
801 }
802 }
803 "memrchr" => {
804 this.check_target_os(&[Os::Linux, Os::Android, Os::FreeBsd], link_name)?;
805
806 let [ptr, val, num] =
807 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
808 let ptr = this.read_pointer(ptr)?;
809 let val = this.read_scalar(val)?.to_i32()?;
810 let num = this.read_target_usize(num)?;
811 #[expect(clippy::as_conversions)]
813 let val = val as u8;
814
815 this.ptr_get_alloc_id(ptr, 0)?;
817
818 let needle_ptr = this.memchr(ptr, (0..num).rev(), val)?.map(|(_idx, ptr)| ptr);
820
821 if let Some(needle_ptr) = needle_ptr {
822 this.write_pointer(needle_ptr, dest)?;
823 } else {
824 this.write_null(dest)?;
825 }
826 }
827 "strlen" => {
828 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
829 let ptr = this.read_pointer(ptr)?;
830 let n = this.read_c_str(ptr)?.len();
832 this.write_scalar(
833 Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
834 dest,
835 )?;
836 }
837 "strnlen" => {
838 let [ptr, num] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
839 let ptr = this.read_pointer(ptr)?;
840 let num = this.read_target_usize(num)?;
841
842 this.ptr_get_alloc_id(ptr, 0)?;
844
845 let idx = this.memchr(ptr, 0..num, 0)?.map(|(idx, _ptr)| idx).unwrap_or(num);
848 this.write_scalar(Scalar::from_target_usize(idx, this), dest)?;
849 }
850 "wcslen" => {
851 let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
852 let ptr = this.read_pointer(ptr)?;
853 let n = this.read_wchar_t_str(ptr)?.len();
855 this.write_scalar(
856 Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
857 dest,
858 )?;
859 }
860 "memcpy" => {
861 let [ptr_dest, ptr_src, n] =
862 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
863 let ptr_dest = this.read_pointer(ptr_dest)?;
864 let ptr_src = this.read_pointer(ptr_src)?;
865 let n = this.read_target_usize(n)?;
866
867 this.ptr_get_alloc_id(ptr_dest, 0)?;
870 this.ptr_get_alloc_id(ptr_src, 0)?;
871
872 this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
873 this.write_pointer(ptr_dest, dest)?;
874 }
875 "strcpy" => {
876 let [ptr_dest, ptr_src] =
877 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
878 let ptr_dest = this.read_pointer(ptr_dest)?;
879 let ptr_src = this.read_pointer(ptr_src)?;
880
881 let n = this.read_c_str(ptr_src)?.len().strict_add(1);
888 this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
889 this.write_pointer(ptr_dest, dest)?;
890 }
891 "memset" => {
892 let [ptr_dest, val, n] =
893 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
894 let ptr_dest = this.read_pointer(ptr_dest)?;
895 let val = this.read_scalar(val)?.to_i32()?;
896 let n = this.read_target_usize(n)?;
897 #[expect(clippy::as_conversions)]
899 let val = val as u8;
900
901 this.ptr_get_alloc_id(ptr_dest, 0)?;
903
904 let bytes = std::iter::repeat_n(val, n.try_into().unwrap());
905 this.write_bytes_ptr(ptr_dest, bytes)?;
906 this.write_pointer(ptr_dest, dest)?;
907 }
908
909 _ => {
911 if let res = shims::math::EvalContextExt::emulate_foreign_item_inner(
913 this, link_name, abi, args, dest,
914 )? && !matches!(res, EmulateItemResult::NotSupported)
915 {
916 return interp_ok(res);
917 }
918
919 return match &this.tcx.sess.target.os {
921 _ if this.target_os_is_unix() =>
922 shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner(
923 this, link_name, abi, args, dest,
924 ),
925 Os::Windows =>
926 shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner(
927 this, link_name, abi, args, dest,
928 ),
929 _ => interp_ok(EmulateItemResult::NotSupported),
930 };
931 }
932 };
933 interp_ok(EmulateItemResult::NeedsReturn)
936 }
937
938 fn memchr(
941 &self,
942 ptr: Pointer,
943 idxs: impl Iterator<Item = u64>,
944 needle: u8,
945 ) -> InterpResult<'tcx, Option<(u64, Pointer)>> {
946 let this = self.eval_context_ref();
947 for idx in idxs {
948 let ptr = ptr.wrapping_offset(Size::from_bytes(idx), this);
949 let place = this.ptr_to_mplace(ptr, this.machine.layouts.u8);
950 let val = this.read_scalar(&place)?.to_u8()?;
951 if val == needle {
952 return interp_ok(Some((idx, ptr)));
953 }
954 }
955 interp_ok(None)
956 }
957}