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_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_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 =
174 tcx.def_span(original.instance.def_id()).data();
175 let span = tcx.def_span(def_id).data();
176 if original_span < span {
177 throw_machine_stop!(
178 TerminationInfo::MultipleSymbolDefinitions {
179 link_name,
180 first: original_span,
181 first_crate: tcx.crate_name(original.cnum),
182 second: span,
183 second_crate: tcx.crate_name(cnum),
184 }
185 );
186 } else {
187 throw_machine_stop!(
188 TerminationInfo::MultipleSymbolDefinitions {
189 link_name,
190 first: span,
191 first_crate: tcx.crate_name(cnum),
192 second: original_span,
193 second_crate: tcx.crate_name(original.cnum),
194 }
195 );
196 }
197 }
198 }
199 } else {
200 symbol_target = Some(SymbolTarget {
201 instance: ty::Instance::mono(tcx, def_id),
202 cnum,
203 is_weak,
204 });
205 }
206 }
207 interp_ok(())
208 })?;
209
210 e.insert(symbol_target.map(|SymbolTarget { instance, .. }| instance))
211 }
212 };
213 drop(cache);
214 interp_ok(instance)
215 }
216
217 fn lookup_exported_fn(
219 &self,
220 link_name: Symbol,
221 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
222 let this = self.eval_context_ref();
223 let instance = this.lookup_exported_symbol(link_name)?;
224 if let Some(instance) = &instance {
225 if !matches!(this.tcx.def_kind(instance.def_id()), DefKind::Fn | DefKind::AssocFn) {
226 throw_ub_format!(
227 "attempt to call an exported symbol that is not defined as a function"
228 );
229 }
230 }
231 match instance {
232 None => interp_ok(None),
233 Some(instance) => interp_ok(Some((this.load_mir(instance.def, None)?, instance))),
234 }
235 }
236
237 fn lookup_exported_static(
239 &self,
240 link_name: Symbol,
241 ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
242 let this = self.eval_context_ref();
243 let instance = this.lookup_exported_symbol(link_name)?;
244 if let Some(instance) = &instance {
245 if !matches!(this.tcx.def_kind(instance.def_id()), DefKind::Static { .. }) {
246 throw_ub_format!(
247 "attempt to access an exported symbol `{link_name}` that is not defined as a static"
248 );
249 }
250 }
251 interp_ok(instance)
252 }
253}
254
255impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
256trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
257 fn emulate_foreign_item_inner(
258 &mut self,
259 link_name: Symbol,
260 abi: &FnAbi<'tcx, Ty<'tcx>>,
261 args: &[OpTy<'tcx>],
262 dest: &MPlaceTy<'tcx>,
263 ) -> InterpResult<'tcx, EmulateItemResult> {
264 let this = self.eval_context_mut();
265
266 #[cfg(all(feature = "native-lib", unix))]
268 if !this.machine.native_lib.is_empty() {
269 use crate::shims::native_lib::EvalContextExt as _;
270 if this.call_native_fn(link_name, dest, args)? {
274 return interp_ok(EmulateItemResult::NeedsReturn);
275 }
276 }
277 match link_name.as_str() {
316 name if name == this.mangle_internal_symbol(NO_ALLOC_SHIM_IS_UNSTABLE) => {
318 let [] = this.check_shim_sig(
321 shim_sig!(extern "Rust" fn() -> ()),
322 (link_name, abi, args),
323 )?;
324 }
325
326 "miri_alloc" => {
328 let [size, align] = this.check_shim_sig(
329 shim_sig!(extern "Rust" fn(usize, usize) -> *_),
330 (link_name, abi, args),
331 )?;
332 let size = this.read_target_usize(size)?;
333 let align = this.read_target_usize(align)?;
334
335 this.check_rust_alloc_request(size, align)?;
336
337 let ptr = this.allocate_ptr(
338 Size::from_bytes(size),
339 Align::from_bytes(align).unwrap(),
340 MiriMemoryKind::Miri.into(),
341 AllocInit::Uninit,
342 )?;
343
344 this.write_pointer(ptr, dest)?;
345 }
346 "miri_dealloc" => {
347 let [ptr, old_size, align] = this.check_shim_sig(
348 shim_sig!(extern "Rust" fn(*_, usize, usize) -> ()),
349 (link_name, abi, args),
350 )?;
351 let ptr = this.read_pointer(ptr)?;
352 let old_size = this.read_target_usize(old_size)?;
353 let align = this.read_target_usize(align)?;
354
355 this.deallocate_ptr(
357 ptr,
358 Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
359 MiriMemoryKind::Miri.into(),
360 )?;
361 }
362 "miri_track_alloc" => {
363 let [ptr] = this.check_shim_sig(
364 shim_sig!(extern "Rust" fn(*_) -> ()),
365 (link_name, abi, args),
366 )?;
367 let ptr = this.read_pointer(ptr)?;
368 let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
369 err_machine_stop!(TerminationInfo::Abort(format!(
370 "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
371 )))
372 })?;
373 if this.machine.tracked_alloc_ids.insert(alloc_id) {
374 let info = this.get_alloc_info(alloc_id);
375 this.emit_diagnostic(NonHaltingDiagnostic::TrackingAlloc(
376 alloc_id, info.size, info.align,
377 ));
378 }
379 }
380 "miri_start_unwind" => {
381 let [payload] = this
382 .check_shim_sig(shim_sig!(extern "Rust" fn(*_) -> !), (link_name, abi, args))?;
383 this.handle_miri_start_unwind(payload)?;
384 return interp_ok(EmulateItemResult::NeedsUnwind);
385 }
386 "miri_run_provenance_gc" => {
387 let [] = this
388 .check_shim_sig(shim_sig!(extern "Rust" fn() -> ()), (link_name, abi, args))?;
389 this.run_provenance_gc();
390 }
391 "miri_get_alloc_id" => {
392 let [ptr] = this.check_shim_sig(
393 shim_sig!(extern "Rust" fn(*_) -> u64),
394 (link_name, abi, args),
395 )?;
396 let ptr = this.read_pointer(ptr)?;
397 let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
398 err_machine_stop!(TerminationInfo::Abort(format!(
399 "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
400 )))
401 })?;
402 this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?;
403 }
404 "miri_print_borrow_state" => {
405 let [id, show_unnamed] = this.check_shim_sig(
406 shim_sig!(extern "Rust" fn(u64, bool) -> ()),
407 (link_name, abi, args),
408 )?;
409 let id = this.read_scalar(id)?.to_u64()?;
410 let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?;
411 if let Some(id) = std::num::NonZero::new(id).map(AllocId)
412 && this.get_alloc_info(id).kind == AllocKind::LiveData
413 {
414 this.print_borrow_state(id, show_unnamed)?;
415 } else {
416 eprintln!("{id} is not the ID of a live data allocation");
417 }
418 }
419 "miri_pointer_name" => {
420 let [ptr, nth_parent, name] = this.check_shim_sig(
423 shim_sig!(extern "Rust" fn(*_, u8, &[u8]) -> ()),
424 (link_name, abi, args),
425 )?;
426 let ptr = this.read_pointer(ptr)?;
427 let nth_parent = this.read_scalar(nth_parent)?.to_u8()?;
428 let name = this.read_immediate(name)?;
429
430 let name = this.read_byte_slice(&name)?;
431 let name = String::from_utf8_lossy(name).into_owned();
435 this.give_pointer_debug_name(ptr, nth_parent, &name)?;
436 }
437 "miri_static_root" => {
438 let [ptr] = this.check_shim_sig(
439 shim_sig!(extern "Rust" fn(*_) -> ()),
440 (link_name, abi, args),
441 )?;
442 let ptr = this.read_pointer(ptr)?;
443 let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
444 if offset != Size::ZERO {
445 throw_unsup_format!(
446 "pointer passed to `miri_static_root` must point to beginning of an allocated block"
447 );
448 }
449 this.machine.static_roots.push(alloc_id);
450 }
451 "miri_host_to_target_path" => {
452 let [ptr, out, out_size] = this.check_shim_sig(
453 shim_sig!(extern "Rust" fn(*_, *_, usize) -> usize),
454 (link_name, abi, args),
455 )?;
456 let ptr = this.read_pointer(ptr)?;
457 let out = this.read_pointer(out)?;
458 let out_size = this.read_scalar(out_size)?.to_target_usize(this)?;
459
460 this.check_no_isolation("`miri_host_to_target_path`")?;
462
463 let path = this.read_os_str_from_c_str(ptr)?.to_owned();
465 let (success, needed_size) =
466 this.write_path_to_c_str(Path::new(&path), out, out_size)?;
467 this.write_int(if success { 0 } else { needed_size }, dest)?;
469 }
470 "miri_thread_spawn" => {
471 let [start_routine, func_arg] = this.check_shim_sig(
472 shim_sig!(extern "Rust" fn(fn(..) -> _, *_) -> usize),
474 (link_name, abi, args),
475 )?;
476 let start_routine = this.read_pointer(start_routine)?;
477 let func_arg = this.read_immediate(func_arg)?;
478
479 this.start_regular_thread(
480 Some(dest.clone()),
481 start_routine,
482 ExternAbi::Rust,
483 func_arg,
484 this.machine.layouts.unit,
485 )?;
486 }
487 "miri_thread_join" => {
488 let [thread_id] = this.check_shim_sig(
489 shim_sig!(extern "Rust" fn(usize) -> bool),
490 (link_name, abi, args),
491 )?;
492
493 let thread = this.read_target_usize(thread_id)?;
494 use crate::concurrency::thread::ThreadLookupError;
496 let thread = match this.thread_id_try_from(thread) {
497 Ok(id) | Err(ThreadLookupError::Terminated(id)) => Some(id),
498 Err(ThreadLookupError::InvalidId) => None,
499 };
500 if let Some(thread) = thread {
501 this.join_thread_exclusive(
502 thread,
503 Scalar::from_bool(true),
504 dest,
505 )?;
506 } else {
507 this.write_scalar(Scalar::from_bool(false), dest)?;
508 }
509 }
510 "miri_spin_loop" => {
512 let [] = this
513 .check_shim_sig(shim_sig!(extern "Rust" fn() -> ()), (link_name, abi, args))?;
514
515 this.yield_active_thread();
517 }
518 "miri_backtrace_size" => {
520 this.handle_miri_backtrace_size(abi, link_name, args, dest)?;
521 }
522 "miri_get_backtrace" => {
524 this.handle_miri_get_backtrace(abi, link_name, args)?;
526 }
527 "miri_resolve_frame" => {
529 this.handle_miri_resolve_frame(abi, link_name, args, dest)?;
531 }
532 "miri_resolve_frame_names" => {
534 this.handle_miri_resolve_frame_names(abi, link_name, args)?;
535 }
536 "miri_write_to_stdout" | "miri_write_to_stderr" => {
538 let [msg] = this.check_shim_sig(
539 shim_sig!(extern "Rust" fn(&[u8]) -> ()),
540 (link_name, abi, args),
541 )?;
542 let msg = this.read_immediate(msg)?;
543 let msg = this.read_byte_slice(&msg)?;
544 let _ignore = match link_name.as_str() {
546 "miri_write_to_stdout" => std::io::stdout().write_all(msg),
547 "miri_write_to_stderr" => std::io::stderr().write_all(msg),
548 _ => unreachable!(),
549 };
550 }
551 "miri_promise_symbolic_alignment" => {
553 use rustc_abi::AlignFromBytesError;
554
555 let [ptr, align] = this.check_shim_sig(
556 shim_sig!(extern "Rust" fn(*_, usize) -> ()),
557 (link_name, abi, args),
558 )?;
559
560 let ptr = this.read_pointer(ptr)?;
561 let align = this.read_target_usize(align)?;
562 if !align.is_power_of_two() {
563 throw_unsup_format!(
564 "`miri_promise_symbolic_alignment`: alignment must be a power of 2, got {align}"
565 );
566 }
567 let align = Align::from_bytes(align).unwrap_or_else(|err| {
568 match err {
569 AlignFromBytesError::NotPowerOfTwo(_) => unreachable!(),
570 AlignFromBytesError::TooLarge(_) => Align::MAX,
572 }
573 });
574 let addr = ptr.addr();
575 if addr.bytes().strict_rem(align.bytes()) != 0 {
577 throw_unsup_format!(
578 "`miri_promise_symbolic_alignment`: pointer is not actually aligned"
579 );
580 }
581 if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr, 0) {
582 let alloc_align = this.get_alloc_info(alloc_id).align;
583 if align > alloc_align
586 && this
587 .machine
588 .symbolic_alignment
589 .get_mut()
590 .get(&alloc_id)
591 .is_none_or(|&(_, old_align)| align > old_align)
592 {
593 this.machine.symbolic_alignment.get_mut().insert(alloc_id, (offset, align));
594 }
595 }
596 }
597 "miri_genmc_assume" => {
599 let [condition] = this.check_shim_sig(
600 shim_sig!(extern "Rust" fn(bool) -> ()),
601 (link_name, abi, args),
602 )?;
603
604 if this.machine.data_race.as_genmc_ref().is_some() {
605 this.handle_genmc_verifier_assume(condition)?;
606 } else {
607 throw_unsup_format!("miri_genmc_assume is only supported in GenMC mode")
608 }
609 }
610
611 "exit" => {
613 let [code] = this
615 .check_shim_sig(shim_sig!(extern "C" fn(i32) -> ()), (link_name, abi, args))?;
616 let code = this.read_scalar(code)?.to_i32()?;
617 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
618 genmc_ctx.handle_exit(
620 this.machine.threads.active_thread(),
621 code,
622 crate::concurrency::ExitType::ExitCalled,
623 )?;
624 return interp_ok(EmulateItemResult::AlreadyJumped);
625 }
626 throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false });
627 }
628 "abort" => {
629 let [] =
631 this.check_shim_sig(shim_sig!(extern "C" fn() -> ()), (link_name, abi, args))?;
632 throw_machine_stop!(TerminationInfo::Abort(
633 "the program aborted execution".to_owned()
634 ));
635 }
636
637 "malloc" => {
639 let [size] = this.check_shim_sig(
640 shim_sig!(extern "C" fn(usize) -> *_),
641 (link_name, abi, args),
642 )?;
643 let size = this.read_target_usize(size)?;
644 if size <= this.max_size_of_val().bytes() {
645 let res = this.malloc(size, AllocInit::Uninit)?;
646 this.write_pointer(res, dest)?;
647 } else {
648 if this.target_os_is_unix() {
650 this.set_last_error(LibcError("ENOMEM"))?;
651 }
652 this.write_null(dest)?;
653 }
654 }
655 "calloc" => {
656 let [items, elem_size] = this.check_shim_sig(
657 shim_sig!(extern "C" fn(usize, usize) -> *_),
658 (link_name, abi, args),
659 )?;
660 let items = this.read_target_usize(items)?;
661 let elem_size = this.read_target_usize(elem_size)?;
662 if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
663 let res = this.malloc(size.bytes(), AllocInit::Zero)?;
664 this.write_pointer(res, dest)?;
665 } else {
666 if this.target_os_is_unix() {
668 this.set_last_error(LibcError("ENOMEM"))?;
669 }
670 this.write_null(dest)?;
671 }
672 }
673 "free" => {
674 let [ptr] = this
675 .check_shim_sig(shim_sig!(extern "C" fn(*_) -> ()), (link_name, abi, args))?;
676 let ptr = this.read_pointer(ptr)?;
677 this.free(ptr)?;
678 }
679 "realloc" => {
680 let [old_ptr, new_size] = this.check_shim_sig(
681 shim_sig!(extern "C" fn(*_, usize) -> *_),
682 (link_name, abi, args),
683 )?;
684 let old_ptr = this.read_pointer(old_ptr)?;
685 let new_size = this.read_target_usize(new_size)?;
686 if new_size <= this.max_size_of_val().bytes() {
687 let res = this.realloc(old_ptr, new_size)?;
688 this.write_pointer(res, dest)?;
689 } else {
690 if this.target_os_is_unix() {
692 this.set_last_error(LibcError("ENOMEM"))?;
693 }
694 this.write_null(dest)?;
695 }
696 }
697 "malloc_usable_size" => {
698 this.check_target_os(&[Os::Linux, Os::FreeBsd, Os::Android], link_name)?;
699
700 let [ptr] = this.check_shim_sig(
701 shim_sig!(extern "C" fn(*_) -> usize),
702 (link_name, abi, args),
703 )?;
704 let ptr = this.read_pointer(ptr)?;
705 let size = if this.ptr_is_null(ptr)? {
706 0
707 } else {
708 let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
709 if offset.bytes() != 0 {
710 throw_ub_format!(
711 "`malloc_usable_size` was called on a pointer that does not point to the beginning of its allocation"
712 );
713 }
714 let Some((alloc_kind, _)) = this.memory.alloc_map().get(alloc_id) else {
715 throw_ub_format!(
716 "`malloc_usable_size` was called on a pointer to memory not managed by the C allocator"
717 );
718 };
719 if *alloc_kind != MiriMemoryKind::C.into() {
720 throw_ub_format!(
721 "`malloc_usable_size` was called on a pointer to {alloc_kind} memory, which is not managed by the C allocator"
722 );
723 }
724 this.get_alloc_info(alloc_id).size.bytes()
725 };
726 this.write_scalar(Scalar::from_target_usize(size, this), dest)?;
727 }
728
729 "memcmp" => {
731 let [left, right, n] = this.check_shim_sig(
732 shim_sig!(extern "C" fn(*_, *_, usize) -> i32),
733 (link_name, abi, args),
734 )?;
735 let left = this.read_pointer(left)?;
736 let right = this.read_pointer(right)?;
737 let n = Size::from_bytes(this.read_target_usize(n)?);
738
739 this.ptr_get_alloc_id(left, 0)?;
741 this.ptr_get_alloc_id(right, 0)?;
742
743 let result = {
748 let left_bytes = this.read_bytes_ptr_strip_provenance(left, n)?;
749 let right_bytes = this.read_bytes_ptr_strip_provenance(right, n)?;
750
751 use std::cmp::Ordering::*;
752 match left_bytes.cmp(right_bytes) {
753 Less => -1i32,
754 Equal => 0,
755 Greater => 1,
756 }
757 };
758
759 this.write_scalar(Scalar::from_i32(result), dest)?;
760 }
761 "memchr" => {
762 let [ptr, val, num] = this.check_shim_sig(
763 shim_sig!(extern "C" fn(*_, i32, usize) -> *_),
764 (link_name, abi, args),
765 )?;
766 let ptr = this.read_pointer(ptr)?;
767 let val = this.read_scalar(val)?.to_i32()?;
768 let num = this.read_target_usize(num)?;
769 #[expect(clippy::as_conversions)]
771 let val = val as u8;
772
773 this.ptr_get_alloc_id(ptr, 0)?;
775
776 let needle_ptr = this.memchr(ptr, 0..num, val)?.map(|(_idx, ptr)| ptr);
779
780 if let Some(needle_ptr) = needle_ptr {
781 this.write_pointer(needle_ptr, dest)?;
782 } else {
783 this.write_null(dest)?;
784 }
785 }
786 "memrchr" => {
787 this.check_target_os(&[Os::Linux, Os::Android, Os::FreeBsd], link_name)?;
788
789 let [ptr, val, num] = this.check_shim_sig(
790 shim_sig!(extern "C" fn(*_, i32, usize) -> *_),
791 (link_name, abi, args),
792 )?;
793 let ptr = this.read_pointer(ptr)?;
794 let val = this.read_scalar(val)?.to_i32()?;
795 let num = this.read_target_usize(num)?;
796 #[expect(clippy::as_conversions)]
798 let val = val as u8;
799
800 this.ptr_get_alloc_id(ptr, 0)?;
802
803 let needle_ptr = this.memchr(ptr, (0..num).rev(), val)?.map(|(_idx, ptr)| ptr);
805
806 if let Some(needle_ptr) = needle_ptr {
807 this.write_pointer(needle_ptr, dest)?;
808 } else {
809 this.write_null(dest)?;
810 }
811 }
812 "strlen" => {
813 let [ptr] = this.check_shim_sig(
814 shim_sig!(extern "C" fn(*_) -> usize),
815 (link_name, abi, args),
816 )?;
817 let ptr = this.read_pointer(ptr)?;
818 let n = this.read_c_str(ptr)?.len();
820 this.write_scalar(
821 Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
822 dest,
823 )?;
824 }
825 "strnlen" => {
826 let [ptr, num] = this.check_shim_sig(
827 shim_sig!(extern "C" fn(*_, usize) -> usize),
828 (link_name, abi, args),
829 )?;
830 let ptr = this.read_pointer(ptr)?;
831 let num = this.read_target_usize(num)?;
832
833 this.ptr_get_alloc_id(ptr, 0)?;
835
836 let idx = this.memchr(ptr, 0..num, 0)?.map(|(idx, _ptr)| idx).unwrap_or(num);
839 this.write_scalar(Scalar::from_target_usize(idx, this), dest)?;
840 }
841 "wcslen" => {
842 let [ptr] = this.check_shim_sig(
843 shim_sig!(extern "C" fn(*_) -> usize),
844 (link_name, abi, args),
845 )?;
846 let ptr = this.read_pointer(ptr)?;
847 let n = this.read_wchar_t_str(ptr)?.len();
849 this.write_scalar(
850 Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
851 dest,
852 )?;
853 }
854 "memcpy" => {
855 let [ptr_dest, ptr_src, n] = this.check_shim_sig(
856 shim_sig!(extern "C" fn(*_, *_, usize) -> *_),
857 (link_name, abi, args),
858 )?;
859 let ptr_dest = this.read_pointer(ptr_dest)?;
860 let ptr_src = this.read_pointer(ptr_src)?;
861 let n = this.read_target_usize(n)?;
862
863 this.ptr_get_alloc_id(ptr_dest, 0)?;
866 this.ptr_get_alloc_id(ptr_src, 0)?;
867
868 this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
869 this.write_pointer(ptr_dest, dest)?;
870 }
871 "strcpy" => {
872 let [ptr_dest, ptr_src] = this.check_shim_sig(
873 shim_sig!(extern "C" fn(*_, *_) -> *_),
874 (link_name, abi, args),
875 )?;
876 let ptr_dest = this.read_pointer(ptr_dest)?;
877 let ptr_src = this.read_pointer(ptr_src)?;
878
879 let n = this.read_c_str(ptr_src)?.len().strict_add(1);
886 this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
887 this.write_pointer(ptr_dest, dest)?;
888 }
889 "memset" => {
890 let [ptr_dest, val, n] = this.check_shim_sig(
891 shim_sig!(extern "C" fn(*_, i32, usize) -> *_),
892 (link_name, abi, args),
893 )?;
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}