1use std::collections::hash_map::Entry;
2use std::io::Write;
3use std::path::Path;
4
5use rustc_abi::{Align, ExternAbi, Size};
6use rustc_ast::expand::allocator::NO_ALLOC_SHIM_IS_UNSTABLE;
7use rustc_attr_ir::Linkage;
8use rustc_data_structures::either::Either;
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_fn(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.clone().into(), ret, Some(unwind), |this| {
78 if let Some(body) = this.lookup_exported_fn(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 &self,
116 link_name: Symbol,
117 ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
118 let this = self.eval_context_ref();
119 let tcx = this.tcx.tcx;
120
121 let mut cache = this.machine.exported_symbols_cache.borrow_mut();
124 let entry = cache.entry(link_name);
125 let instance = *match entry {
126 Entry::Occupied(e) => e.into_mut(),
127 Entry::Vacant(e) => {
128 struct SymbolTarget<'tcx> {
131 instance: ty::Instance<'tcx>,
132 cnum: CrateNum,
133 is_weak: bool,
134 }
135 let mut symbol_target: Option<SymbolTarget<'tcx>> = None;
136 helpers::iter_exported_symbols(tcx, |cnum, def_id, _used| {
137 let attrs = tcx.codegen_fn_attrs(def_id);
138 if !(attrs.symbol_name.is_some()
140 || attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
141 || attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL))
142 {
143 return interp_ok(());
144 }
145
146 let instance = Instance::mono(tcx, def_id);
147 let symbol_name = tcx.symbol_name(instance).name;
148 let is_weak = attrs.linkage == Some(Linkage::WeakAny);
149 if symbol_name == link_name.as_str() {
150 if let Some(original) = &symbol_target {
151 match (is_weak, original.is_weak) {
154 (false, true) => {
155 symbol_target = Some(SymbolTarget {
158 instance: ty::Instance::mono(tcx, def_id),
159 cnum,
160 is_weak,
161 });
162 }
163 (true, false) => {
164 }
166 (true, true) | (false, false) => {
167 let original_span = tcx.def_span(original.instance.def_id());
174 let span = tcx.def_span(def_id);
175 if original_span.lo_hi() < span.lo_hi() {
176 throw_machine_stop!(
177 TerminationInfo::MultipleSymbolDefinitions {
178 link_name,
179 first: original_span,
180 first_crate: tcx.crate_name(original.cnum),
181 second: span,
182 second_crate: tcx.crate_name(cnum),
183 }
184 );
185 } else {
186 throw_machine_stop!(
187 TerminationInfo::MultipleSymbolDefinitions {
188 link_name,
189 first: span,
190 first_crate: tcx.crate_name(cnum),
191 second: original_span,
192 second_crate: tcx.crate_name(original.cnum),
193 }
194 );
195 }
196 }
197 }
198 } else {
199 symbol_target = Some(SymbolTarget {
200 instance: ty::Instance::mono(tcx, def_id),
201 cnum,
202 is_weak,
203 });
204 }
205 }
206 interp_ok(())
207 })?;
208
209 e.insert(symbol_target.map(|SymbolTarget { instance, .. }| instance))
210 }
211 };
212 drop(cache);
213 interp_ok(instance)
214 }
215
216 fn lookup_exported_fn(
218 &self,
219 link_name: Symbol,
220 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
221 let this = self.eval_context_ref();
222 let instance = this.lookup_exported_symbol(link_name)?;
223 if let Some(instance) = &instance {
224 if !matches!(this.tcx.def_kind(instance.def_id()), DefKind::Fn | DefKind::AssocFn) {
225 throw_ub_format!(
226 "attempt to call an exported symbol that is not defined as a function"
227 );
228 }
229 }
230 match instance {
231 None => interp_ok(None),
232 Some(instance) => interp_ok(Some((this.load_mir(instance.def, None)?, instance))),
233 }
234 }
235
236 fn lookup_exported_static(
238 &self,
239 link_name: Symbol,
240 ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
241 let this = self.eval_context_ref();
242 let instance = this.lookup_exported_symbol(link_name)?;
243 if let Some(instance) = &instance {
244 if !matches!(this.tcx.def_kind(instance.def_id()), DefKind::Static { .. }) {
245 throw_ub_format!(
246 "attempt to access an exported symbol `{link_name}` that is not defined as a static"
247 );
248 }
249 }
250 interp_ok(instance)
251 }
252}
253
254impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
255trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
256 fn emulate_foreign_item_inner(
257 &mut self,
258 link_name: Symbol,
259 abi: &FnAbi<'tcx, Ty<'tcx>>,
260 args: &[OpTy<'tcx>],
261 dest: &MPlaceTy<'tcx>,
262 ) -> InterpResult<'tcx, EmulateItemResult> {
263 let this = self.eval_context_mut();
264
265 #[cfg(all(feature = "native-lib", unix))]
267 if !this.machine.native_lib.is_empty() {
268 use crate::shims::native_lib::EvalContextExt as _;
269 if this.call_native_fn(link_name, dest, args)? {
273 return interp_ok(EmulateItemResult::NeedsReturn);
274 }
275 }
276 match link_name.as_str() {
313 name if name == this.mangle_internal_symbol(NO_ALLOC_SHIM_IS_UNSTABLE) => {
315 let [] = this
318 .check_shim_sig(shim_sig!(extern "Rust" fn() -> ()), (link_name, abi, args))?;
319 }
320
321 "miri_alloc" => {
323 let [size, align] = this.check_shim_sig(
324 shim_sig!(extern "Rust" fn(usize, usize) -> *()),
325 (link_name, abi, args),
326 )?;
327 let size = this.read_target_usize(size)?;
328 let align = this.read_target_usize(align)?;
329
330 this.check_rust_alloc_request(size, align)?;
331
332 let ptr = this.allocate_ptr(
333 Size::from_bytes(size),
334 Align::from_bytes(align).unwrap(),
335 MiriMemoryKind::Miri.into(),
336 AllocInit::Uninit,
337 )?;
338
339 this.write_pointer(ptr, dest)?;
340 }
341 "miri_dealloc" => {
342 let [ptr, old_size, align] = this.check_shim_sig(
343 shim_sig!(extern "Rust" fn(*(), usize, usize) -> ()),
344 (link_name, abi, args),
345 )?;
346 let ptr = this.read_pointer(ptr)?;
347 let old_size = this.read_target_usize(old_size)?;
348 let align = this.read_target_usize(align)?;
349
350 this.deallocate_ptr(
352 ptr,
353 Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
354 MiriMemoryKind::Miri.into(),
355 )?;
356 }
357 "miri_track_alloc" => {
358 let [ptr] = this.check_shim_sig(
359 shim_sig!(extern "Rust" fn(*()) -> ()),
360 (link_name, abi, args),
361 )?;
362 let ptr = this.read_pointer(ptr)?;
363 let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
364 err_machine_stop!(TerminationInfo::Abort(format!(
365 "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
366 )))
367 })?;
368 if this.machine.tracked_alloc_ids.insert(alloc_id) {
369 let info = this.get_alloc_info(alloc_id);
370 this.emit_diagnostic(NonHaltingDiagnostic::TrackingAlloc(
371 alloc_id, info.size, info.align,
372 ));
373 }
374 }
375 "miri_start_unwind" => {
376 let [payload] = this.check_shim_sig(
377 shim_sig!(extern "Rust" fn(*()) -> !),
378 (link_name, abi, args),
379 )?;
380 this.handle_miri_start_unwind(payload)?;
381 return interp_ok(EmulateItemResult::NeedsUnwind);
382 }
383 "miri_run_provenance_gc" => {
384 let [] = this
385 .check_shim_sig(shim_sig!(extern "Rust" fn() -> ()), (link_name, abi, args))?;
386 this.run_provenance_gc();
387 }
388 "miri_get_alloc_id" => {
389 let [ptr] = this.check_shim_sig(
390 shim_sig!(extern "Rust" fn(*()) -> u64),
391 (link_name, abi, args),
392 )?;
393 let ptr = this.read_pointer(ptr)?;
394 let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
395 err_machine_stop!(TerminationInfo::Abort(format!(
396 "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
397 )))
398 })?;
399 this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?;
400 }
401 "miri_print_borrow_state" => {
402 let [id, show_unnamed] = this.check_shim_sig(
403 shim_sig!(extern "Rust" fn(u64, bool) -> ()),
404 (link_name, abi, args),
405 )?;
406 let id = this.read_scalar(id)?.to_u64()?;
407 let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?;
408 if let Some(id) = std::num::NonZero::new(id).map(AllocId)
409 && this.get_alloc_info(id).kind == AllocKind::LiveData
410 {
411 this.print_borrow_state(id, show_unnamed)?;
412 } else {
413 eprintln!("{id} is not the ID of a live data allocation");
414 }
415 }
416 "miri_pointer_name" => {
417 let [ptr, nth_parent, name] = this.check_shim_sig(
420 shim_sig!(extern "Rust" fn(*(), u8, &[u8]) -> ()),
421 (link_name, abi, args),
422 )?;
423 let ptr = this.read_pointer(ptr)?;
424 let nth_parent = this.read_scalar(nth_parent)?.to_u8()?;
425 let name = this.read_immediate(name)?;
426
427 let name = this.read_byte_slice(&name)?;
428 let name = String::from_utf8_lossy(name).into_owned();
432 this.give_pointer_debug_name(ptr, nth_parent, &name)?;
433 }
434 "miri_static_root" => {
435 let [ptr] = this.check_shim_sig(
436 shim_sig!(extern "Rust" fn(*()) -> ()),
437 (link_name, abi, args),
438 )?;
439 let ptr = this.read_pointer(ptr)?;
440 let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
441 if offset != Size::ZERO {
442 throw_unsup_format!(
443 "pointer passed to `miri_static_root` must point to beginning of an allocated block"
444 );
445 }
446 this.machine.static_roots.push(alloc_id);
447 }
448 "miri_host_to_target_path" => {
449 let [ptr, out, out_size] = this.check_shim_sig(
450 shim_sig!(extern "Rust" fn(*(), *(), usize) -> usize),
451 (link_name, abi, args),
452 )?;
453 let ptr = this.read_pointer(ptr)?;
454 let out = this.read_pointer(out)?;
455 let out_size = this.read_scalar(out_size)?.to_target_usize(this)?;
456
457 this.check_no_isolation("`miri_host_to_target_path`")?;
459
460 let path = this.read_os_str_from_c_str(ptr)?.to_owned();
462 let (success, needed_size) =
463 this.write_path_to_c_str(Path::new(&path), out, out_size)?;
464 this.write_int(if success { 0 } else { needed_size }, dest)?;
466 }
467 "miri_thread_spawn" => {
468 let [start_routine, func_arg] = this.check_shim_sig(
469 shim_sig!(extern "Rust" fn(fn(..) -> _, *()) -> usize),
471 (link_name, abi, args),
472 )?;
473 let start_routine = this.read_pointer(start_routine)?;
474 let func_arg = this.read_immediate(func_arg)?;
475
476 this.start_regular_thread(
477 Some(dest.clone()),
478 start_routine,
479 ExternAbi::Rust,
480 func_arg,
481 this.machine.layouts.unit,
482 )?;
483 }
484 "miri_thread_join" => {
485 let [thread_id] = this.check_shim_sig(
486 shim_sig!(extern "Rust" fn(usize) -> bool),
487 (link_name, abi, args),
488 )?;
489
490 let thread = this.read_target_usize(thread_id)?;
491 use crate::concurrency::thread::ThreadLookupError;
493 let thread = match this.thread_id_try_from(thread) {
494 Ok(id) | Err(ThreadLookupError::Terminated(id)) => Some(id),
495 Err(ThreadLookupError::InvalidId) => None,
496 };
497 if let Some(thread) = thread {
498 this.join_thread_exclusive(
499 thread,
500 Scalar::from_bool(true),
501 dest,
502 )?;
503 } else {
504 this.write_scalar(Scalar::from_bool(false), dest)?;
505 }
506 }
507 "miri_spin_loop" => {
509 let [] = this
510 .check_shim_sig(shim_sig!(extern "Rust" fn() -> ()), (link_name, abi, args))?;
511
512 this.yield_active_thread();
514 }
515 "miri_backtrace_size" => {
517 this.handle_miri_backtrace_size(abi, link_name, args, dest)?;
518 }
519 "miri_get_backtrace" => {
521 this.handle_miri_get_backtrace(abi, link_name, args)?;
523 }
524 "miri_resolve_frame" => {
526 this.handle_miri_resolve_frame(abi, link_name, args, dest)?;
528 }
529 "miri_resolve_frame_names" => {
531 this.handle_miri_resolve_frame_names(abi, link_name, args)?;
532 }
533 "miri_write_to_stdout" | "miri_write_to_stderr" => {
535 let [msg] = this.check_shim_sig(
536 shim_sig!(extern "Rust" fn(&[u8]) -> ()),
537 (link_name, abi, args),
538 )?;
539 let msg = this.read_immediate(msg)?;
540 let msg = this.read_byte_slice(&msg)?;
541 let _ignore = match link_name.as_str() {
543 "miri_write_to_stdout" => std::io::stdout().write_all(msg),
544 "miri_write_to_stderr" => std::io::stderr().write_all(msg),
545 _ => unreachable!(),
546 };
547 }
548 "miri_promise_symbolic_alignment" => {
550 use rustc_abi::AlignFromBytesError;
551
552 let [ptr, align] = this.check_shim_sig(
553 shim_sig!(extern "Rust" fn(*(), usize) -> ()),
554 (link_name, abi, args),
555 )?;
556
557 let ptr = this.read_pointer(ptr)?;
558 let align = this.read_target_usize(align)?;
559 if !align.is_power_of_two() {
560 throw_unsup_format!(
561 "`miri_promise_symbolic_alignment`: alignment must be a power of 2, got {align}"
562 );
563 }
564 let align = Align::from_bytes(align).unwrap_or_else(|err| {
565 match err {
566 AlignFromBytesError::NotPowerOfTwo(_) => unreachable!(),
567 AlignFromBytesError::TooLarge(_) => Align::MAX,
569 }
570 });
571 let addr = ptr.addr();
572 if addr.bytes().strict_rem(align.bytes()) != 0 {
574 throw_unsup_format!(
575 "`miri_promise_symbolic_alignment`: pointer is not actually aligned"
576 );
577 }
578 if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr, 0) {
579 let alloc_align = this.get_alloc_info(alloc_id).align;
580 if align > alloc_align
583 && this
584 .machine
585 .symbolic_alignment
586 .get_mut()
587 .get(&alloc_id)
588 .is_none_or(|&(_, old_align)| align > old_align)
589 {
590 this.machine.symbolic_alignment.get_mut().insert(alloc_id, (offset, align));
591 }
592 }
593 }
594 "miri_genmc_assume" => {
596 let [condition] = this.check_shim_sig(
597 shim_sig!(extern "Rust" fn(bool) -> ()),
598 (link_name, abi, args),
599 )?;
600
601 if this.machine.data_race.as_genmc_ref().is_some() {
602 this.handle_genmc_verifier_assume(condition)?;
603 } else {
604 throw_unsup_format!("miri_genmc_assume is only supported in GenMC mode")
605 }
606 }
607
608 "exit" => {
610 let [code] = this
612 .check_shim_sig(shim_sig!(extern "C" fn(i32) -> ()), (link_name, abi, args))?;
613 let code = this.read_scalar(code)?.to_i32()?;
614 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
615 genmc_ctx.handle_exit(
617 this.machine.threads.active_thread(),
618 code,
619 crate::concurrency::ExitType::ExitCalled,
620 )?;
621 return interp_ok(EmulateItemResult::AlreadyJumped);
622 }
623 throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false });
624 }
625 "abort" => {
626 let [] =
628 this.check_shim_sig(shim_sig!(extern "C" fn() -> ()), (link_name, abi, args))?;
629 throw_machine_stop!(TerminationInfo::Abort(
630 "the program aborted execution".to_owned()
631 ));
632 }
633
634 "malloc" => {
636 let [size] = this.check_shim_sig(
637 shim_sig!(extern "C" fn(usize) -> *_),
638 (link_name, abi, args),
639 )?;
640 let size = this.read_target_usize(size)?;
641 if size <= this.max_size_of_val().bytes() {
642 let res = this.malloc(size, AllocInit::Uninit)?;
643 this.write_pointer(res, dest)?;
644 } else {
645 if this.target_os_is_unix() {
647 this.set_last_error(LibcError("ENOMEM"))?;
648 }
649 this.write_null(dest)?;
650 }
651 }
652 "calloc" => {
653 let [items, elem_size] = this.check_shim_sig(
654 shim_sig!(extern "C" fn(usize, usize) -> *_),
655 (link_name, abi, args),
656 )?;
657 let items = this.read_target_usize(items)?;
658 let elem_size = this.read_target_usize(elem_size)?;
659 if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
660 let res = this.malloc(size.bytes(), AllocInit::Zero)?;
661 this.write_pointer(res, dest)?;
662 } else {
663 if this.target_os_is_unix() {
665 this.set_last_error(LibcError("ENOMEM"))?;
666 }
667 this.write_null(dest)?;
668 }
669 }
670 "free" => {
671 let [ptr] = this
672 .check_shim_sig(shim_sig!(extern "C" fn(*_) -> ()), (link_name, abi, args))?;
673 let ptr = this.read_pointer(ptr)?;
674 this.free(ptr)?;
675 }
676 "realloc" => {
677 let [old_ptr, new_size] = this.check_shim_sig(
678 shim_sig!(extern "C" fn(*_, usize) -> *_),
679 (link_name, abi, args),
680 )?;
681 let old_ptr = this.read_pointer(old_ptr)?;
682 let new_size = this.read_target_usize(new_size)?;
683 if new_size <= this.max_size_of_val().bytes() {
684 let res = this.realloc(old_ptr, new_size)?;
685 this.write_pointer(res, dest)?;
686 } else {
687 if this.target_os_is_unix() {
689 this.set_last_error(LibcError("ENOMEM"))?;
690 }
691 this.write_null(dest)?;
692 }
693 }
694 "malloc_usable_size" => {
695 this.check_target_os(&[Os::Linux, Os::FreeBsd, Os::Android], link_name)?;
696
697 let [ptr] = this.check_shim_sig(
698 shim_sig!(extern "C" fn(*_) -> usize),
699 (link_name, abi, args),
700 )?;
701 let ptr = this.read_pointer(ptr)?;
702 let size = if this.ptr_is_null(ptr)? {
703 0
704 } else {
705 let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
706 if offset.bytes() != 0 {
707 throw_ub_format!(
708 "`malloc_usable_size` was called on a pointer that does not point to the beginning of its allocation"
709 );
710 }
711 let Some((alloc_kind, _)) = this.memory.alloc_map().get(alloc_id) else {
712 throw_ub_format!(
713 "`malloc_usable_size` was called on a pointer to memory not managed by the C allocator"
714 );
715 };
716 if *alloc_kind != MiriMemoryKind::C.into() {
717 throw_ub_format!(
718 "`malloc_usable_size` was called on a pointer to {alloc_kind} memory, which is not managed by the C allocator"
719 );
720 }
721 this.get_alloc_info(alloc_id).size.bytes()
722 };
723 this.write_scalar(Scalar::from_target_usize(size, this), dest)?;
724 }
725
726 "memcmp" => {
728 let [left, right, n] = this.check_shim_sig(
729 shim_sig!(extern "C" fn(*_, *_, usize) -> i32),
730 (link_name, abi, args),
731 )?;
732 let left = this.read_pointer(left)?;
733 let right = this.read_pointer(right)?;
734 let n = Size::from_bytes(this.read_target_usize(n)?);
735
736 this.ptr_get_alloc_id(left, 0)?;
738 this.ptr_get_alloc_id(right, 0)?;
739
740 let result = {
745 let left_bytes = this.read_bytes_ptr_strip_provenance(left, n)?;
746 let right_bytes = this.read_bytes_ptr_strip_provenance(right, n)?;
747
748 use std::cmp::Ordering::*;
749 match left_bytes.cmp(right_bytes) {
750 Less => -1i32,
751 Equal => 0,
752 Greater => 1,
753 }
754 };
755
756 this.write_scalar(Scalar::from_i32(result), dest)?;
757 }
758 "memchr" => {
759 let [ptr, val, num] = this.check_shim_sig(
760 shim_sig!(extern "C" fn(*_, i32, usize) -> *_),
761 (link_name, abi, args),
762 )?;
763 let ptr = this.read_pointer(ptr)?;
764 let val = this.read_scalar(val)?.to_i32()?;
765 let num = this.read_target_usize(num)?;
766 #[expect(clippy::as_conversions)]
768 let val = val as u8;
769
770 this.ptr_get_alloc_id(ptr, 0)?;
772
773 let needle_ptr = this.memchr(ptr, 0..num, val)?.map(|(_idx, ptr)| ptr);
776
777 if let Some(needle_ptr) = needle_ptr {
778 this.write_pointer(needle_ptr, dest)?;
779 } else {
780 this.write_null(dest)?;
781 }
782 }
783 "memrchr" => {
784 this.check_target_os(&[Os::Linux, Os::Android, Os::FreeBsd], link_name)?;
785
786 let [ptr, val, num] = this.check_shim_sig(
787 shim_sig!(extern "C" fn(*_, i32, usize) -> *_),
788 (link_name, abi, args),
789 )?;
790 let ptr = this.read_pointer(ptr)?;
791 let val = this.read_scalar(val)?.to_i32()?;
792 let num = this.read_target_usize(num)?;
793 #[expect(clippy::as_conversions)]
795 let val = val as u8;
796
797 this.ptr_get_alloc_id(ptr, 0)?;
799
800 let needle_ptr = this.memchr(ptr, (0..num).rev(), val)?.map(|(_idx, ptr)| ptr);
802
803 if let Some(needle_ptr) = needle_ptr {
804 this.write_pointer(needle_ptr, dest)?;
805 } else {
806 this.write_null(dest)?;
807 }
808 }
809 "strlen" => {
810 let [ptr] = this.check_shim_sig(
811 shim_sig!(extern "C" fn(*_) -> usize),
812 (link_name, abi, args),
813 )?;
814 let ptr = this.read_pointer(ptr)?;
815 let n = this.read_c_str(ptr)?.len();
817 this.write_scalar(
818 Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
819 dest,
820 )?;
821 }
822 "strnlen" => {
823 let [ptr, num] = this.check_shim_sig(
824 shim_sig!(extern "C" fn(*_, usize) -> usize),
825 (link_name, abi, args),
826 )?;
827 let ptr = this.read_pointer(ptr)?;
828 let num = this.read_target_usize(num)?;
829
830 this.ptr_get_alloc_id(ptr, 0)?;
832
833 let idx = this.memchr(ptr, 0..num, 0)?.map(|(idx, _ptr)| idx).unwrap_or(num);
836 this.write_scalar(Scalar::from_target_usize(idx, this), dest)?;
837 }
838 "wcslen" => {
839 let [ptr] = this.check_shim_sig(
840 shim_sig!(extern "C" fn(*_) -> usize),
841 (link_name, abi, args),
842 )?;
843 let ptr = this.read_pointer(ptr)?;
844 let n = this.read_wchar_t_str(ptr)?.len();
846 this.write_scalar(
847 Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
848 dest,
849 )?;
850 }
851 "memcpy" => {
852 let [ptr_dest, ptr_src, n] = this.check_shim_sig(
853 shim_sig!(extern "C" fn(*_, *_, usize) -> *_),
854 (link_name, abi, args),
855 )?;
856 let ptr_dest = this.read_pointer(ptr_dest)?;
857 let ptr_src = this.read_pointer(ptr_src)?;
858 let n = this.read_target_usize(n)?;
859
860 this.ptr_get_alloc_id(ptr_dest, 0)?;
863 this.ptr_get_alloc_id(ptr_src, 0)?;
864
865 this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
866 this.write_pointer(ptr_dest, dest)?;
867 }
868 "strcpy" => {
869 let [ptr_dest, ptr_src] = this.check_shim_sig(
870 shim_sig!(extern "C" fn(*_, *_) -> *_),
871 (link_name, abi, args),
872 )?;
873 let ptr_dest = this.read_pointer(ptr_dest)?;
874 let ptr_src = this.read_pointer(ptr_src)?;
875
876 let n = this.read_c_str(ptr_src)?.len().strict_add(1);
883 this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
884 this.write_pointer(ptr_dest, dest)?;
885 }
886 "memset" => {
887 let [ptr_dest, val, n] = this.check_shim_sig(
888 shim_sig!(extern "C" fn(*_, i32, usize) -> *_),
889 (link_name, abi, args),
890 )?;
891 let ptr_dest = this.read_pointer(ptr_dest)?;
892 let val = this.read_scalar(val)?.to_i32()?;
893 let n = this.read_target_usize(n)?;
894 #[expect(clippy::as_conversions)]
896 let val = val as u8;
897
898 this.ptr_get_alloc_id(ptr_dest, 0)?;
900
901 let bytes = std::iter::repeat_n(val, n.try_into().unwrap());
902 this.write_bytes_ptr(ptr_dest, bytes)?;
903 this.write_pointer(ptr_dest, dest)?;
904 }
905
906 _ => {
908 if let res = shims::math::EvalContextExt::emulate_foreign_item_inner(
910 this, link_name, abi, args, dest,
911 )? && !matches!(res, EmulateItemResult::NotSupported)
912 {
913 return interp_ok(res);
914 }
915
916 return match &this.tcx.sess.target.os {
918 _ if this.target_os_is_unix() =>
919 shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner(
920 this, link_name, abi, args, dest,
921 ),
922 Os::Windows =>
923 shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner(
924 this, 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 this = self.eval_context_ref();
944 for idx in idxs {
945 let ptr = ptr.wrapping_offset(Size::from_bytes(idx), this);
946 let place = this.ptr_to_mplace(ptr, this.machine.layouts.u8);
947 let val = this.read_scalar(&place)?.to_u8()?;
948 if val == needle {
949 return interp_ok(Some((idx, ptr)));
950 }
951 }
952 interp_ok(None)
953 }
954}