1use std::collections::hash_map::Entry;
2use std::io::Write;
3use std::path::Path;
4
5use rustc_abi::{Align, AlignFromBytesError, Size};
6use rustc_apfloat::Float;
7use rustc_ast::expand::allocator::alloc_error_handler_name;
8use rustc_hir::def::DefKind;
9use rustc_hir::def_id::CrateNum;
10use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
11use rustc_middle::mir::interpret::AllocInit;
12use rustc_middle::ty::Ty;
13use rustc_middle::{mir, ty};
14use rustc_span::Symbol;
15use rustc_symbol_mangling::mangle_internal_symbol;
16use rustc_target::callconv::{Conv, FnAbi};
17
18use self::helpers::{ToHost, ToSoft};
19use super::alloc::EvalContextExt as _;
20use super::backtrace::EvalContextExt as _;
21use crate::*;
22
23#[derive(Debug, Copy, Clone)]
25pub struct DynSym(Symbol);
26
27#[expect(clippy::should_implement_trait)]
28impl DynSym {
29 pub fn from_str(name: &str) -> Self {
30 DynSym(Symbol::intern(name))
31 }
32}
33
34impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
35pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
36 fn emulate_foreign_item(
43 &mut self,
44 link_name: Symbol,
45 abi: &FnAbi<'tcx, Ty<'tcx>>,
46 args: &[OpTy<'tcx>],
47 dest: &MPlaceTy<'tcx>,
48 ret: Option<mir::BasicBlock>,
49 unwind: mir::UnwindAction,
50 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
51 let this = self.eval_context_mut();
52
53 match link_name.as_str() {
55 name if name == mangle_internal_symbol(*this.tcx, "__rust_alloc_error_handler") => {
56 let Some(handler_kind) = this.tcx.alloc_error_handler_kind(()) else {
58 throw_unsup_format!(
60 "`__rust_alloc_error_handler` cannot be called when no alloc error handler is set"
61 );
62 };
63 let name =
64 mangle_internal_symbol(*this.tcx, alloc_error_handler_name(handler_kind));
65 let handler = this
66 .lookup_exported_symbol(Symbol::intern(&name))?
67 .expect("missing alloc error handler symbol");
68 return interp_ok(Some(handler));
69 }
70 _ => {}
71 }
72
73 match this.emulate_foreign_item_inner(link_name, abi, args, dest)? {
75 EmulateItemResult::NeedsReturn => {
76 trace!("{:?}", this.dump_place(&dest.clone().into()));
77 this.return_to_block(ret)?;
78 }
79 EmulateItemResult::NeedsUnwind => {
80 this.unwind_to_block(unwind)?;
82 }
83 EmulateItemResult::AlreadyJumped => (),
84 EmulateItemResult::NotSupported => {
85 if let Some(body) = this.lookup_exported_symbol(link_name)? {
86 return interp_ok(Some(body));
87 }
88
89 throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(format!(
90 "can't call foreign function `{link_name}` on OS `{os}`",
91 os = this.tcx.sess.target.os,
92 )));
93 }
94 }
95
96 interp_ok(None)
97 }
98
99 fn is_dyn_sym(&self, name: &str) -> bool {
100 let this = self.eval_context_ref();
101 match this.tcx.sess.target.os.as_ref() {
102 os if this.target_os_is_unix() => shims::unix::foreign_items::is_dyn_sym(name, os),
103 "wasi" => shims::wasi::foreign_items::is_dyn_sym(name),
104 "windows" => shims::windows::foreign_items::is_dyn_sym(name),
105 _ => false,
106 }
107 }
108
109 fn emulate_dyn_sym(
111 &mut self,
112 sym: DynSym,
113 abi: &FnAbi<'tcx, Ty<'tcx>>,
114 args: &[OpTy<'tcx>],
115 dest: &MPlaceTy<'tcx>,
116 ret: Option<mir::BasicBlock>,
117 unwind: mir::UnwindAction,
118 ) -> InterpResult<'tcx> {
119 let res = self.emulate_foreign_item(sym.0, abi, args, dest, ret, unwind)?;
120 assert!(res.is_none(), "DynSyms that delegate are not supported");
121 interp_ok(())
122 }
123
124 fn lookup_exported_symbol(
126 &mut self,
127 link_name: Symbol,
128 ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
129 let this = self.eval_context_mut();
130 let tcx = this.tcx.tcx;
131
132 let entry = this.machine.exported_symbols_cache.entry(link_name);
135 let instance = *match entry {
136 Entry::Occupied(e) => e.into_mut(),
137 Entry::Vacant(e) => {
138 let mut instance_and_crate: Option<(ty::Instance<'_>, CrateNum)> = None;
140 helpers::iter_exported_symbols(tcx, |cnum, def_id| {
141 if tcx.is_foreign_item(def_id) {
142 return interp_ok(());
144 }
145
146 let attrs = tcx.codegen_fn_attrs(def_id);
147 let symbol_name = if let Some(export_name) = attrs.export_name {
149 export_name
150 } else if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
151 || attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
152 {
153 tcx.item_name(def_id)
154 } else {
155 return interp_ok(());
157 };
158 let symbol_name =
159 if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
160 Symbol::intern(&mangle_internal_symbol(tcx, symbol_name.as_str()))
161 } else {
162 symbol_name
163 };
164 if symbol_name == link_name {
165 if let Some((original_instance, original_cnum)) = instance_and_crate {
166 let original_span = tcx.def_span(original_instance.def_id()).data();
168 let span = tcx.def_span(def_id).data();
169 if original_span < span {
170 throw_machine_stop!(TerminationInfo::MultipleSymbolDefinitions {
171 link_name,
172 first: original_span,
173 first_crate: tcx.crate_name(original_cnum),
174 second: span,
175 second_crate: tcx.crate_name(cnum),
176 });
177 } else {
178 throw_machine_stop!(TerminationInfo::MultipleSymbolDefinitions {
179 link_name,
180 first: span,
181 first_crate: tcx.crate_name(cnum),
182 second: original_span,
183 second_crate: tcx.crate_name(original_cnum),
184 });
185 }
186 }
187 if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
188 throw_ub_format!(
189 "attempt to call an exported symbol that is not defined as a function"
190 );
191 }
192 instance_and_crate = Some((ty::Instance::mono(tcx, def_id), cnum));
193 }
194 interp_ok(())
195 })?;
196
197 e.insert(instance_and_crate.map(|ic| ic.0))
198 }
199 };
200 match instance {
201 None => interp_ok(None), Some(instance) => interp_ok(Some((this.load_mir(instance.def, None)?, instance))),
203 }
204 }
205}
206
207impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
208trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
209 fn check_rustc_alloc_request(&self, size: u64, align: u64) -> InterpResult<'tcx> {
212 let this = self.eval_context_ref();
213 if size == 0 {
214 throw_ub_format!("creating allocation with size 0");
215 }
216 if size > this.max_size_of_val().bytes() {
217 throw_ub_format!("creating an allocation larger than half the address space");
218 }
219 if let Err(e) = Align::from_bytes(align) {
220 match e {
221 AlignFromBytesError::TooLarge(_) => {
222 throw_unsup_format!(
223 "creating allocation with alignment {align} exceeding rustc's maximum \
224 supported value"
225 );
226 }
227 AlignFromBytesError::NotPowerOfTwo(_) => {
228 throw_ub_format!("creating allocation with non-power-of-two alignment {align}");
229 }
230 }
231 }
232
233 interp_ok(())
234 }
235
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(unix)]
247 if this.machine.native_lib.as_ref().is_some() {
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 "miri_start_unwind" => {
297 let [payload] = this.check_shim(abi, Conv::Rust, link_name, args)?;
298 this.handle_miri_start_unwind(payload)?;
299 return interp_ok(EmulateItemResult::NeedsUnwind);
300 }
301 "miri_run_provenance_gc" => {
302 let [] = this.check_shim(abi, Conv::Rust, link_name, args)?;
303 this.run_provenance_gc();
304 }
305 "miri_get_alloc_id" => {
306 let [ptr] = this.check_shim(abi, Conv::Rust, link_name, args)?;
307 let ptr = this.read_pointer(ptr)?;
308 let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
309 err_machine_stop!(TerminationInfo::Abort(format!(
310 "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
311 )))
312 })?;
313 this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?;
314 }
315 "miri_print_borrow_state" => {
316 let [id, show_unnamed] = this.check_shim(abi, Conv::Rust, link_name, args)?;
317 let id = this.read_scalar(id)?.to_u64()?;
318 let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?;
319 if let Some(id) = std::num::NonZero::new(id).map(AllocId)
320 && this.get_alloc_info(id).kind == AllocKind::LiveData
321 {
322 this.print_borrow_state(id, show_unnamed)?;
323 } else {
324 eprintln!("{id} is not the ID of a live data allocation");
325 }
326 }
327 "miri_pointer_name" => {
328 let [ptr, nth_parent, name] = this.check_shim(abi, Conv::Rust, link_name, args)?;
331 let ptr = this.read_pointer(ptr)?;
332 let nth_parent = this.read_scalar(nth_parent)?.to_u8()?;
333 let name = this.read_immediate(name)?;
334
335 let name = this.read_byte_slice(&name)?;
336 let name = String::from_utf8_lossy(name).into_owned();
340 this.give_pointer_debug_name(ptr, nth_parent, &name)?;
341 }
342 "miri_static_root" => {
343 let [ptr] = this.check_shim(abi, Conv::Rust, link_name, args)?;
344 let ptr = this.read_pointer(ptr)?;
345 let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
346 if offset != Size::ZERO {
347 throw_unsup_format!(
348 "pointer passed to `miri_static_root` must point to beginning of an allocated block"
349 );
350 }
351 this.machine.static_roots.push(alloc_id);
352 }
353 "miri_host_to_target_path" => {
354 let [ptr, out, out_size] = this.check_shim(abi, Conv::Rust, link_name, args)?;
355 let ptr = this.read_pointer(ptr)?;
356 let out = this.read_pointer(out)?;
357 let out_size = this.read_scalar(out_size)?.to_target_usize(this)?;
358
359 this.check_no_isolation("`miri_host_to_target_path`")?;
361
362 let path = this.read_os_str_from_c_str(ptr)?.to_owned();
364 let (success, needed_size) =
365 this.write_path_to_c_str(Path::new(&path), out, out_size)?;
366 this.write_int(if success { 0 } else { needed_size }, dest)?;
368 }
369 "miri_backtrace_size" => {
371 this.handle_miri_backtrace_size(abi, link_name, args, dest)?;
372 }
373 "miri_get_backtrace" => {
375 this.handle_miri_get_backtrace(abi, link_name, args)?;
377 }
378 "miri_resolve_frame" => {
380 this.handle_miri_resolve_frame(abi, link_name, args, dest)?;
382 }
383 "miri_resolve_frame_names" => {
385 this.handle_miri_resolve_frame_names(abi, link_name, args)?;
386 }
387 "miri_write_to_stdout" | "miri_write_to_stderr" => {
390 let [msg] = this.check_shim(abi, Conv::Rust, link_name, args)?;
391 let msg = this.read_immediate(msg)?;
392 let msg = this.read_byte_slice(&msg)?;
393 let _ignore = match link_name.as_str() {
395 "miri_write_to_stdout" => std::io::stdout().write_all(msg),
396 "miri_write_to_stderr" => std::io::stderr().write_all(msg),
397 _ => unreachable!(),
398 };
399 }
400 "miri_promise_symbolic_alignment" => {
402 use rustc_abi::AlignFromBytesError;
403
404 let [ptr, align] = this.check_shim(abi, Conv::Rust, link_name, args)?;
405 let ptr = this.read_pointer(ptr)?;
406 let align = this.read_target_usize(align)?;
407 if !align.is_power_of_two() {
408 throw_unsup_format!(
409 "`miri_promise_symbolic_alignment`: alignment must be a power of 2, got {align}"
410 );
411 }
412 let align = Align::from_bytes(align).unwrap_or_else(|err| {
413 match err {
414 AlignFromBytesError::NotPowerOfTwo(_) => unreachable!(),
415 AlignFromBytesError::TooLarge(_) => Align::MAX,
417 }
418 });
419 let (_, addr) = ptr.into_parts(); if addr.bytes().strict_rem(align.bytes()) != 0 {
422 throw_unsup_format!(
423 "`miri_promise_symbolic_alignment`: pointer is not actually aligned"
424 );
425 }
426 if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr, 0) {
427 let alloc_align = this.get_alloc_info(alloc_id).align;
428 if align > alloc_align
431 && this
432 .machine
433 .symbolic_alignment
434 .get_mut()
435 .get(&alloc_id)
436 .is_none_or(|&(_, old_align)| align > old_align)
437 {
438 this.machine.symbolic_alignment.get_mut().insert(alloc_id, (offset, align));
439 }
440 }
441 }
442
443 "exit" => {
445 let [code] = this.check_shim(abi, Conv::C, link_name, args)?;
446 let code = this.read_scalar(code)?.to_i32()?;
447 throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false });
448 }
449 "abort" => {
450 let [] = this.check_shim(abi, Conv::C, link_name, args)?;
451 throw_machine_stop!(TerminationInfo::Abort(
452 "the program aborted execution".to_owned()
453 ))
454 }
455
456 "malloc" => {
458 let [size] = this.check_shim(abi, Conv::C, link_name, args)?;
459 let size = this.read_target_usize(size)?;
460 if size <= this.max_size_of_val().bytes() {
461 let res = this.malloc(size, AllocInit::Uninit)?;
462 this.write_pointer(res, dest)?;
463 } else {
464 if this.target_os_is_unix() {
466 this.set_last_error(LibcError("ENOMEM"))?;
467 }
468 this.write_null(dest)?;
469 }
470 }
471 "calloc" => {
472 let [items, elem_size] = this.check_shim(abi, Conv::C, link_name, args)?;
473 let items = this.read_target_usize(items)?;
474 let elem_size = this.read_target_usize(elem_size)?;
475 if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
476 let res = this.malloc(size.bytes(), AllocInit::Zero)?;
477 this.write_pointer(res, dest)?;
478 } else {
479 if this.target_os_is_unix() {
481 this.set_last_error(LibcError("ENOMEM"))?;
482 }
483 this.write_null(dest)?;
484 }
485 }
486 "free" => {
487 let [ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
488 let ptr = this.read_pointer(ptr)?;
489 this.free(ptr)?;
490 }
491 "realloc" => {
492 let [old_ptr, new_size] = this.check_shim(abi, Conv::C, link_name, args)?;
493 let old_ptr = this.read_pointer(old_ptr)?;
494 let new_size = this.read_target_usize(new_size)?;
495 if new_size <= this.max_size_of_val().bytes() {
496 let res = this.realloc(old_ptr, new_size)?;
497 this.write_pointer(res, dest)?;
498 } else {
499 if this.target_os_is_unix() {
501 this.set_last_error(LibcError("ENOMEM"))?;
502 }
503 this.write_null(dest)?;
504 }
505 }
506
507 name if name == mangle_internal_symbol(*this.tcx, "__rust_alloc")
509 || name == "miri_alloc" =>
510 {
511 let default = |ecx: &mut MiriInterpCx<'tcx>| {
512 let [size, align] = ecx.check_shim(abi, Conv::Rust, link_name, args)?;
515 let size = ecx.read_target_usize(size)?;
516 let align = ecx.read_target_usize(align)?;
517
518 ecx.check_rustc_alloc_request(size, align)?;
519
520 let memory_kind = match link_name.as_str() {
521 "miri_alloc" => MiriMemoryKind::Miri,
522 _ => MiriMemoryKind::Rust,
523 };
524
525 let ptr = ecx.allocate_ptr(
526 Size::from_bytes(size),
527 Align::from_bytes(align).unwrap(),
528 memory_kind.into(),
529 AllocInit::Uninit,
530 )?;
531
532 ecx.write_pointer(ptr, dest)
533 };
534
535 match link_name.as_str() {
536 "miri_alloc" => {
537 default(this)?;
538 return interp_ok(EmulateItemResult::NeedsReturn);
539 }
540 _ => return this.emulate_allocator(default),
541 }
542 }
543 name if name == mangle_internal_symbol(*this.tcx, "__rust_alloc_zeroed") => {
544 return this.emulate_allocator(|this| {
545 let [size, align] = this.check_shim(abi, Conv::Rust, link_name, args)?;
548 let size = this.read_target_usize(size)?;
549 let align = this.read_target_usize(align)?;
550
551 this.check_rustc_alloc_request(size, align)?;
552
553 let ptr = this.allocate_ptr(
554 Size::from_bytes(size),
555 Align::from_bytes(align).unwrap(),
556 MiriMemoryKind::Rust.into(),
557 AllocInit::Zero,
558 )?;
559 this.write_pointer(ptr, dest)
560 });
561 }
562 name if name == mangle_internal_symbol(*this.tcx, "__rust_dealloc")
563 || name == "miri_dealloc" =>
564 {
565 let default = |ecx: &mut MiriInterpCx<'tcx>| {
566 let [ptr, old_size, align] =
569 ecx.check_shim(abi, Conv::Rust, link_name, args)?;
570 let ptr = ecx.read_pointer(ptr)?;
571 let old_size = ecx.read_target_usize(old_size)?;
572 let align = ecx.read_target_usize(align)?;
573
574 let memory_kind = match link_name.as_str() {
575 "miri_dealloc" => MiriMemoryKind::Miri,
576 _ => MiriMemoryKind::Rust,
577 };
578
579 ecx.deallocate_ptr(
581 ptr,
582 Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
583 memory_kind.into(),
584 )
585 };
586
587 match link_name.as_str() {
588 "miri_dealloc" => {
589 default(this)?;
590 return interp_ok(EmulateItemResult::NeedsReturn);
591 }
592 _ => return this.emulate_allocator(default),
593 }
594 }
595 name if name == mangle_internal_symbol(*this.tcx, "__rust_realloc") => {
596 return this.emulate_allocator(|this| {
597 let [ptr, old_size, align, new_size] =
600 this.check_shim(abi, Conv::Rust, link_name, args)?;
601 let ptr = this.read_pointer(ptr)?;
602 let old_size = this.read_target_usize(old_size)?;
603 let align = this.read_target_usize(align)?;
604 let new_size = this.read_target_usize(new_size)?;
605 this.check_rustc_alloc_request(new_size, align)?;
608
609 let align = Align::from_bytes(align).unwrap();
610 let new_ptr = this.reallocate_ptr(
611 ptr,
612 Some((Size::from_bytes(old_size), align)),
613 Size::from_bytes(new_size),
614 align,
615 MiriMemoryKind::Rust.into(),
616 AllocInit::Uninit,
617 )?;
618 this.write_pointer(new_ptr, dest)
619 });
620 }
621
622 "memcmp" => {
624 let [left, right, n] = this.check_shim(abi, Conv::C, link_name, args)?;
625 let left = this.read_pointer(left)?;
626 let right = this.read_pointer(right)?;
627 let n = Size::from_bytes(this.read_target_usize(n)?);
628
629 this.ptr_get_alloc_id(left, 0)?;
631 this.ptr_get_alloc_id(right, 0)?;
632
633 let result = {
634 let left_bytes = this.read_bytes_ptr_strip_provenance(left, n)?;
635 let right_bytes = this.read_bytes_ptr_strip_provenance(right, n)?;
636
637 use std::cmp::Ordering::*;
638 match left_bytes.cmp(right_bytes) {
639 Less => -1i32,
640 Equal => 0,
641 Greater => 1,
642 }
643 };
644
645 this.write_scalar(Scalar::from_i32(result), dest)?;
646 }
647 "memrchr" => {
648 let [ptr, val, num] = this.check_shim(abi, Conv::C, link_name, args)?;
649 let ptr = this.read_pointer(ptr)?;
650 let val = this.read_scalar(val)?.to_i32()?;
651 let num = this.read_target_usize(num)?;
652 #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
654 let val = val as u8;
655
656 this.ptr_get_alloc_id(ptr, 0)?;
658
659 if let Some(idx) = this
660 .read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))?
661 .iter()
662 .rev()
663 .position(|&c| c == val)
664 {
665 let idx = u64::try_from(idx).unwrap();
666 #[expect(clippy::arithmetic_side_effects)] let new_ptr = ptr.wrapping_offset(Size::from_bytes(num - idx - 1), this);
668 this.write_pointer(new_ptr, dest)?;
669 } else {
670 this.write_null(dest)?;
671 }
672 }
673 "memchr" => {
674 let [ptr, val, num] = this.check_shim(abi, Conv::C, link_name, args)?;
675 let ptr = this.read_pointer(ptr)?;
676 let val = this.read_scalar(val)?.to_i32()?;
677 let num = this.read_target_usize(num)?;
678 #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
680 let val = val as u8;
681
682 this.ptr_get_alloc_id(ptr, 0)?;
684
685 let idx = this
686 .read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))?
687 .iter()
688 .position(|&c| c == val);
689 if let Some(idx) = idx {
690 let new_ptr = ptr.wrapping_offset(Size::from_bytes(idx as u64), this);
691 this.write_pointer(new_ptr, dest)?;
692 } else {
693 this.write_null(dest)?;
694 }
695 }
696 "strlen" => {
697 let [ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
698 let ptr = this.read_pointer(ptr)?;
699 let n = this.read_c_str(ptr)?.len();
701 this.write_scalar(
702 Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
703 dest,
704 )?;
705 }
706 "wcslen" => {
707 let [ptr] = this.check_shim(abi, Conv::C, link_name, args)?;
708 let ptr = this.read_pointer(ptr)?;
709 let n = this.read_wchar_t_str(ptr)?.len();
711 this.write_scalar(
712 Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
713 dest,
714 )?;
715 }
716 "memcpy" => {
717 let [ptr_dest, ptr_src, n] = this.check_shim(abi, Conv::C, link_name, args)?;
718 let ptr_dest = this.read_pointer(ptr_dest)?;
719 let ptr_src = this.read_pointer(ptr_src)?;
720 let n = this.read_target_usize(n)?;
721
722 this.ptr_get_alloc_id(ptr_dest, 0)?;
725 this.ptr_get_alloc_id(ptr_src, 0)?;
726
727 this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
728 this.write_pointer(ptr_dest, dest)?;
729 }
730 "strcpy" => {
731 let [ptr_dest, ptr_src] = this.check_shim(abi, Conv::C, link_name, args)?;
732 let ptr_dest = this.read_pointer(ptr_dest)?;
733 let ptr_src = this.read_pointer(ptr_src)?;
734
735 let n = this.read_c_str(ptr_src)?.len().strict_add(1);
742 this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
743 this.write_pointer(ptr_dest, dest)?;
744 }
745
746 #[rustfmt::skip]
748 | "cbrtf"
749 | "coshf"
750 | "sinhf"
751 | "tanf"
752 | "tanhf"
753 | "acosf"
754 | "asinf"
755 | "atanf"
756 | "log1pf"
757 | "expm1f"
758 | "tgammaf"
759 | "erff"
760 | "erfcf"
761 => {
762 let [f] = this.check_shim(abi, Conv::C , link_name, args)?;
763 let f = this.read_scalar(f)?.to_f32()?;
764 let f_host = f.to_host();
766 let res = match link_name.as_str() {
767 "cbrtf" => f_host.cbrt(),
768 "coshf" => f_host.cosh(),
769 "sinhf" => f_host.sinh(),
770 "tanf" => f_host.tan(),
771 "tanhf" => f_host.tanh(),
772 "acosf" => f_host.acos(),
773 "asinf" => f_host.asin(),
774 "atanf" => f_host.atan(),
775 "log1pf" => f_host.ln_1p(),
776 "expm1f" => f_host.exp_m1(),
777 "tgammaf" => f_host.gamma(),
778 "erff" => f_host.erf(),
779 "erfcf" => f_host.erfc(),
780 _ => bug!(),
781 };
782 let res = res.to_soft();
783 let res = this.adjust_nan(res, &[f]);
792 this.write_scalar(res, dest)?;
793 }
794 #[rustfmt::skip]
795 | "_hypotf"
796 | "hypotf"
797 | "atan2f"
798 | "fdimf"
799 => {
800 let [f1, f2] = this.check_shim(abi, Conv::C , link_name, args)?;
801 let f1 = this.read_scalar(f1)?.to_f32()?;
802 let f2 = this.read_scalar(f2)?.to_f32()?;
803 let res = match link_name.as_str() {
807 "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(),
808 "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(),
809 #[allow(deprecated)]
810 "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
811 _ => bug!(),
812 };
813 let res = this.adjust_nan(res, &[f1, f2]);
822 this.write_scalar(res, dest)?;
823 }
824 #[rustfmt::skip]
825 | "cbrt"
826 | "cosh"
827 | "sinh"
828 | "tan"
829 | "tanh"
830 | "acos"
831 | "asin"
832 | "atan"
833 | "log1p"
834 | "expm1"
835 | "tgamma"
836 | "erf"
837 | "erfc"
838 => {
839 let [f] = this.check_shim(abi, Conv::C , link_name, args)?;
840 let f = this.read_scalar(f)?.to_f64()?;
841 let f_host = f.to_host();
843 let res = match link_name.as_str() {
844 "cbrt" => f_host.cbrt(),
845 "cosh" => f_host.cosh(),
846 "sinh" => f_host.sinh(),
847 "tan" => f_host.tan(),
848 "tanh" => f_host.tanh(),
849 "acos" => f_host.acos(),
850 "asin" => f_host.asin(),
851 "atan" => f_host.atan(),
852 "log1p" => f_host.ln_1p(),
853 "expm1" => f_host.exp_m1(),
854 "tgamma" => f_host.gamma(),
855 "erf" => f_host.erf(),
856 "erfc" => f_host.erfc(),
857 _ => bug!(),
858 };
859 let res = res.to_soft();
860 let res = this.adjust_nan(res, &[f]);
869 this.write_scalar(res, dest)?;
870 }
871 #[rustfmt::skip]
872 | "_hypot"
873 | "hypot"
874 | "atan2"
875 | "fdim"
876 => {
877 let [f1, f2] = this.check_shim(abi, Conv::C , link_name, args)?;
878 let f1 = this.read_scalar(f1)?.to_f64()?;
879 let f2 = this.read_scalar(f2)?.to_f64()?;
880 let res = match link_name.as_str() {
884 "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(),
885 "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(),
886 #[allow(deprecated)]
887 "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
888 _ => bug!(),
889 };
890 let res = this.adjust_nan(res, &[f1, f2]);
899 this.write_scalar(res, dest)?;
900 }
901 #[rustfmt::skip]
902 | "_ldexp"
903 | "ldexp"
904 | "scalbn"
905 => {
906 let [x, exp] = this.check_shim(abi, Conv::C , link_name, args)?;
907 let x = this.read_scalar(x)?.to_f64()?;
909 let exp = this.read_scalar(exp)?.to_i32()?;
910
911 let res = x.scalbn(exp);
912 let res = this.adjust_nan(res, &[x]);
913 this.write_scalar(res, dest)?;
914 }
915 "lgammaf_r" => {
916 let [x, signp] = this.check_shim(abi, Conv::C, link_name, args)?;
917 let x = this.read_scalar(x)?.to_f32()?;
918 let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?;
919
920 let (res, sign) = x.to_host().ln_gamma();
922 this.write_int(sign, &signp)?;
923 let res = res.to_soft();
924 let res = this.adjust_nan(res, &[x]);
929 this.write_scalar(res, dest)?;
930 }
931 "lgamma_r" => {
932 let [x, signp] = this.check_shim(abi, Conv::C, link_name, args)?;
933 let x = this.read_scalar(x)?.to_f64()?;
934 let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?;
935
936 let (res, sign) = x.to_host().ln_gamma();
938 this.write_int(sign, &signp)?;
939 let res = res.to_soft();
940 let res = this.adjust_nan(res, &[x]);
945 this.write_scalar(res, dest)?;
946 }
947
948 "llvm.prefetch" => {
950 let [p, rw, loc, ty] = this.check_shim(abi, Conv::C, link_name, args)?;
951
952 let _ = this.read_pointer(p)?;
953 let rw = this.read_scalar(rw)?.to_i32()?;
954 let loc = this.read_scalar(loc)?.to_i32()?;
955 let ty = this.read_scalar(ty)?.to_i32()?;
956
957 if ty == 1 {
958 if !matches!(rw, 0 | 1) {
962 throw_unsup_format!("invalid `rw` value passed to `llvm.prefetch`: {}", rw);
963 }
964 if !matches!(loc, 0..=3) {
965 throw_unsup_format!(
966 "invalid `loc` value passed to `llvm.prefetch`: {}",
967 loc
968 );
969 }
970 } else {
971 throw_unsup_format!("unsupported `llvm.prefetch` type argument: {}", ty);
972 }
973 }
974 name if name.starts_with("llvm.ctpop.v") => {
977 let [op] = this.check_shim(abi, Conv::C, link_name, args)?;
978
979 let (op, op_len) = this.project_to_simd(op)?;
980 let (dest, dest_len) = this.project_to_simd(dest)?;
981
982 assert_eq!(dest_len, op_len);
983
984 for i in 0..dest_len {
985 let op = this.read_immediate(&this.project_index(&op, i)?)?;
986 let res = op.to_scalar().to_uint(op.layout.size)?.count_ones();
989
990 this.write_scalar(
991 Scalar::from_uint(res, op.layout.size),
992 &this.project_index(&dest, i)?,
993 )?;
994 }
995 }
996
997 name if name.starts_with("llvm.x86.")
999 && (this.tcx.sess.target.arch == "x86"
1000 || this.tcx.sess.target.arch == "x86_64") =>
1001 {
1002 return shims::x86::EvalContextExt::emulate_x86_intrinsic(
1003 this, link_name, abi, args, dest,
1004 );
1005 }
1006 name if name.starts_with("llvm.aarch64.") && this.tcx.sess.target.arch == "aarch64" => {
1007 return shims::aarch64::EvalContextExt::emulate_aarch64_intrinsic(
1008 this, link_name, abi, args, dest,
1009 );
1010 }
1011 "llvm.arm.hint" if this.tcx.sess.target.arch == "arm" => {
1013 let [arg] = this.check_shim(abi, Conv::C, link_name, args)?;
1014 let arg = this.read_scalar(arg)?.to_i32()?;
1015 match arg {
1017 1 => {
1019 this.expect_target_feature_for_intrinsic(link_name, "v6")?;
1020 this.yield_active_thread();
1021 }
1022 _ => {
1023 throw_unsup_format!("unsupported llvm.arm.hint argument {}", arg);
1024 }
1025 }
1026 }
1027
1028 _ =>
1030 return match this.tcx.sess.target.os.as_ref() {
1031 _ if this.target_os_is_unix() =>
1032 shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner(
1033 this, link_name, abi, args, dest,
1034 ),
1035 "wasi" =>
1036 shims::wasi::foreign_items::EvalContextExt::emulate_foreign_item_inner(
1037 this, link_name, abi, args, dest,
1038 ),
1039 "windows" =>
1040 shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner(
1041 this, link_name, abi, args, dest,
1042 ),
1043 _ => interp_ok(EmulateItemResult::NotSupported),
1044 },
1045 };
1046 interp_ok(EmulateItemResult::NeedsReturn)
1049 }
1050}