miri/shims/
foreign_items.rs

1use std::collections::hash_map::Entry;
2use std::io::Write;
3use std::path::Path;
4
5use rustc_abi::{Align, AlignFromBytesError, CanonAbi, Size};
6use rustc_apfloat::Float;
7use rustc_ast::expand::allocator::alloc_error_handler_name;
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;
17
18use self::helpers::{ToHost, ToSoft};
19use super::alloc::EvalContextExt as _;
20use super::backtrace::EvalContextExt as _;
21use crate::helpers::EvalContextExt as _;
22use crate::*;
23
24/// Type of dynamic symbols (for `dlsym` et al)
25#[derive(Debug, Copy, Clone)]
26pub struct DynSym(Symbol);
27
28#[expect(clippy::should_implement_trait)]
29impl DynSym {
30    pub fn from_str(name: &str) -> Self {
31        DynSym(Symbol::intern(name))
32    }
33}
34
35impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
36pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
37    /// Emulates calling a foreign item, failing if the item is not supported.
38    /// This function will handle `goto_block` if needed.
39    /// Returns Ok(None) if the foreign item was completely handled
40    /// by this function.
41    /// Returns Ok(Some(body)) if processing the foreign item
42    /// is delegated to another function.
43    fn emulate_foreign_item(
44        &mut self,
45        link_name: Symbol,
46        abi: &FnAbi<'tcx, Ty<'tcx>>,
47        args: &[OpTy<'tcx>],
48        dest: &PlaceTy<'tcx>,
49        ret: Option<mir::BasicBlock>,
50        unwind: mir::UnwindAction,
51    ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
52        let this = self.eval_context_mut();
53
54        // Some shims forward to other MIR bodies.
55        match link_name.as_str() {
56            name if name == this.mangle_internal_symbol("__rust_alloc_error_handler") => {
57                // Forward to the right symbol that implements this function.
58                let Some(handler_kind) = this.tcx.alloc_error_handler_kind(()) else {
59                    // in real code, this symbol does not exist without an allocator
60                    throw_unsup_format!(
61                        "`__rust_alloc_error_handler` cannot be called when no alloc error handler is set"
62                    );
63                };
64                let name = Symbol::intern(
65                    this.mangle_internal_symbol(alloc_error_handler_name(handler_kind)),
66                );
67                let handler =
68                    this.lookup_exported_symbol(name)?.expect("missing alloc error handler symbol");
69                return interp_ok(Some(handler));
70            }
71            _ => {}
72        }
73
74        // FIXME: avoid allocating memory
75        let dest = this.force_allocation(dest)?;
76
77        // The rest either implements the logic, or falls back to `lookup_exported_symbol`.
78        match this.emulate_foreign_item_inner(link_name, abi, args, &dest)? {
79            EmulateItemResult::NeedsReturn => {
80                trace!("{:?}", this.dump_place(&dest.clone().into()));
81                this.return_to_block(ret)?;
82            }
83            EmulateItemResult::NeedsUnwind => {
84                // Jump to the unwind block to begin unwinding.
85                this.unwind_to_block(unwind)?;
86            }
87            EmulateItemResult::AlreadyJumped => (),
88            EmulateItemResult::NotSupported => {
89                if let Some(body) = this.lookup_exported_symbol(link_name)? {
90                    return interp_ok(Some(body));
91                }
92
93                throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(format!(
94                    "can't call foreign function `{link_name}` on OS `{os}`",
95                    os = this.tcx.sess.target.os,
96                )));
97            }
98        }
99
100        interp_ok(None)
101    }
102
103    fn is_dyn_sym(&self, name: &str) -> bool {
104        let this = self.eval_context_ref();
105        match this.tcx.sess.target.os.as_ref() {
106            os if this.target_os_is_unix() => shims::unix::foreign_items::is_dyn_sym(name, os),
107            "wasi" => shims::wasi::foreign_items::is_dyn_sym(name),
108            "windows" => shims::windows::foreign_items::is_dyn_sym(name),
109            _ => false,
110        }
111    }
112
113    /// Emulates a call to a `DynSym`.
114    fn emulate_dyn_sym(
115        &mut self,
116        sym: DynSym,
117        abi: &FnAbi<'tcx, Ty<'tcx>>,
118        args: &[OpTy<'tcx>],
119        dest: &PlaceTy<'tcx>,
120        ret: Option<mir::BasicBlock>,
121        unwind: mir::UnwindAction,
122    ) -> InterpResult<'tcx> {
123        let res = self.emulate_foreign_item(sym.0, abi, args, dest, ret, unwind)?;
124        assert!(res.is_none(), "DynSyms that delegate are not supported");
125        interp_ok(())
126    }
127
128    /// Lookup the body of a function that has `link_name` as the symbol name.
129    fn lookup_exported_symbol(
130        &mut self,
131        link_name: Symbol,
132    ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
133        let this = self.eval_context_mut();
134        let tcx = this.tcx.tcx;
135
136        // If the result was cached, just return it.
137        // (Cannot use `or_insert` since the code below might have to throw an error.)
138        let entry = this.machine.exported_symbols_cache.entry(link_name);
139        let instance = *match entry {
140            Entry::Occupied(e) => e.into_mut(),
141            Entry::Vacant(e) => {
142                // Find it if it was not cached.
143
144                struct SymbolTarget<'tcx> {
145                    instance: ty::Instance<'tcx>,
146                    cnum: CrateNum,
147                    is_weak: bool,
148                }
149                let mut symbol_target: Option<SymbolTarget<'tcx>> = None;
150                helpers::iter_exported_symbols(tcx, |cnum, def_id| {
151                    let attrs = tcx.codegen_fn_attrs(def_id);
152                    // Skip over imports of items.
153                    if tcx.is_foreign_item(def_id) {
154                        return interp_ok(());
155                    }
156                    // Skip over items without an explicitly defined symbol name.
157                    if !(attrs.symbol_name.is_some()
158                        || attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
159                        || attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL))
160                    {
161                        return interp_ok(());
162                    }
163
164                    let instance = Instance::mono(tcx, def_id);
165                    let symbol_name = tcx.symbol_name(instance).name;
166                    let is_weak = attrs.linkage == Some(Linkage::WeakAny);
167                    if symbol_name == link_name.as_str() {
168                        if let Some(original) = &symbol_target {
169                            // There is more than one definition with this name. What we do now
170                            // depends on whether one or both definitions are weak.
171                            match (is_weak, original.is_weak) {
172                                (false, true) => {
173                                    // Original definition is a weak definition. Override it.
174
175                                    symbol_target = Some(SymbolTarget {
176                                        instance: ty::Instance::mono(tcx, def_id),
177                                        cnum,
178                                        is_weak,
179                                    });
180                                }
181                                (true, false) => {
182                                    // Current definition is a weak definition. Keep the original one.
183                                }
184                                (true, true) | (false, false) => {
185                                    // Either both definitions are non-weak or both are weak. In
186                                    // either case return an error. For weak definitions we error
187                                    // because it is unspecified which definition would have been
188                                    // picked by the linker.
189
190                                    // Make sure we are consistent wrt what is 'first' and 'second'.
191                                    let original_span =
192                                        tcx.def_span(original.instance.def_id()).data();
193                                    let span = tcx.def_span(def_id).data();
194                                    if original_span < span {
195                                        throw_machine_stop!(
196                                            TerminationInfo::MultipleSymbolDefinitions {
197                                                link_name,
198                                                first: original_span,
199                                                first_crate: tcx.crate_name(original.cnum),
200                                                second: span,
201                                                second_crate: tcx.crate_name(cnum),
202                                            }
203                                        );
204                                    } else {
205                                        throw_machine_stop!(
206                                            TerminationInfo::MultipleSymbolDefinitions {
207                                                link_name,
208                                                first: span,
209                                                first_crate: tcx.crate_name(cnum),
210                                                second: original_span,
211                                                second_crate: tcx.crate_name(original.cnum),
212                                            }
213                                        );
214                                    }
215                                }
216                            }
217                        } else {
218                            symbol_target = Some(SymbolTarget {
219                                instance: ty::Instance::mono(tcx, def_id),
220                                cnum,
221                                is_weak,
222                            });
223                        }
224                    }
225                    interp_ok(())
226                })?;
227
228                // Once we identified the instance corresponding to the symbol, ensure
229                // it is a function. It is okay to encounter non-functions in the search above
230                // as long as the final instance we arrive at is a function.
231                if let Some(SymbolTarget { instance, .. }) = symbol_target {
232                    if !matches!(tcx.def_kind(instance.def_id()), DefKind::Fn | DefKind::AssocFn) {
233                        throw_ub_format!(
234                            "attempt to call an exported symbol that is not defined as a function"
235                        );
236                    }
237                }
238
239                e.insert(symbol_target.map(|SymbolTarget { instance, .. }| instance))
240            }
241        };
242        match instance {
243            None => interp_ok(None), // no symbol with this name
244            Some(instance) => interp_ok(Some((this.load_mir(instance.def, None)?, instance))),
245        }
246    }
247}
248
249impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
250trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
251    /// Check some basic requirements for this allocation request:
252    /// non-zero size, power-of-two alignment.
253    fn check_rustc_alloc_request(&self, size: u64, align: u64) -> InterpResult<'tcx> {
254        let this = self.eval_context_ref();
255        if size == 0 {
256            throw_ub_format!("creating allocation with size 0");
257        }
258        if size > this.max_size_of_val().bytes() {
259            throw_ub_format!("creating an allocation larger than half the address space");
260        }
261        if let Err(e) = Align::from_bytes(align) {
262            match e {
263                AlignFromBytesError::TooLarge(_) => {
264                    throw_unsup_format!(
265                        "creating allocation with alignment {align} exceeding rustc's maximum \
266                         supported value"
267                    );
268                }
269                AlignFromBytesError::NotPowerOfTwo(_) => {
270                    throw_ub_format!("creating allocation with non-power-of-two alignment {align}");
271                }
272            }
273        }
274
275        interp_ok(())
276    }
277
278    fn emulate_foreign_item_inner(
279        &mut self,
280        link_name: Symbol,
281        abi: &FnAbi<'tcx, Ty<'tcx>>,
282        args: &[OpTy<'tcx>],
283        dest: &MPlaceTy<'tcx>,
284    ) -> InterpResult<'tcx, EmulateItemResult> {
285        let this = self.eval_context_mut();
286
287        // First deal with any external C functions in linked .so file.
288        #[cfg(all(unix, feature = "native-lib"))]
289        if !this.machine.native_lib.is_empty() {
290            use crate::shims::native_lib::EvalContextExt as _;
291            // An Ok(false) here means that the function being called was not exported
292            // by the specified `.so` file; we should continue and check if it corresponds to
293            // a provided shim.
294            if this.call_native_fn(link_name, dest, args)? {
295                return interp_ok(EmulateItemResult::NeedsReturn);
296            }
297        }
298        // When adding a new shim, you should follow the following pattern:
299        // ```
300        // "shim_name" => {
301        //     let [arg1, arg2, arg3] = this.check_shim(abi, CanonAbi::C , link_name, args)?;
302        //     let result = this.shim_name(arg1, arg2, arg3)?;
303        //     this.write_scalar(result, dest)?;
304        // }
305        // ```
306        // and then define `shim_name` as a helper function in an extension trait in a suitable file
307        // (see e.g. `unix/fs.rs`):
308        // ```
309        // fn shim_name(
310        //     &mut self,
311        //     arg1: &OpTy<'tcx>,
312        //     arg2: &OpTy<'tcx>,
313        //     arg3: &OpTy<'tcx>,
314        //     arg4: &OpTy<'tcx>)
315        // -> InterpResult<'tcx, Scalar> {
316        //     let this = self.eval_context_mut();
317        //
318        //     // First thing: load all the arguments. Details depend on the shim.
319        //     let arg1 = this.read_scalar(arg1)?.to_u32()?;
320        //     let arg2 = this.read_pointer(arg2)?; // when you need to work with the pointer directly
321        //     let arg3 = this.deref_pointer_as(arg3, this.libc_ty_layout("some_libc_struct"))?; // when you want to load/store
322        //         // through the pointer and supply the type information yourself
323        //     let arg4 = this.deref_pointer(arg4)?; // when you want to load/store through the pointer and trust
324        //         // the user-given type (which you shouldn't usually do)
325        //
326        //     // ...
327        //
328        //     interp_ok(Scalar::from_u32(42))
329        // }
330        // ```
331        // You might find existing shims not following this pattern, most
332        // likely because they predate it or because for some reason they cannot be made to fit.
333
334        // Here we dispatch all the shims for foreign functions. If you have a platform specific
335        // shim, add it to the corresponding submodule.
336        match link_name.as_str() {
337            // Miri-specific extern functions
338            "miri_start_unwind" => {
339                let [payload] =
340                    this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
341                this.handle_miri_start_unwind(payload)?;
342                return interp_ok(EmulateItemResult::NeedsUnwind);
343            }
344            "miri_run_provenance_gc" => {
345                let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
346                this.run_provenance_gc();
347            }
348            "miri_get_alloc_id" => {
349                let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
350                let ptr = this.read_pointer(ptr)?;
351                let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
352                    err_machine_stop!(TerminationInfo::Abort(format!(
353                        "pointer passed to `miri_get_alloc_id` must not be dangling, got {ptr:?}"
354                    )))
355                })?;
356                this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?;
357            }
358            "miri_print_borrow_state" => {
359                let [id, show_unnamed] =
360                    this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
361                let id = this.read_scalar(id)?.to_u64()?;
362                let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?;
363                if let Some(id) = std::num::NonZero::new(id).map(AllocId)
364                    && this.get_alloc_info(id).kind == AllocKind::LiveData
365                {
366                    this.print_borrow_state(id, show_unnamed)?;
367                } else {
368                    eprintln!("{id} is not the ID of a live data allocation");
369                }
370            }
371            "miri_pointer_name" => {
372                // This associates a name to a tag. Very useful for debugging, and also makes
373                // tests more strict.
374                let [ptr, nth_parent, name] =
375                    this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
376                let ptr = this.read_pointer(ptr)?;
377                let nth_parent = this.read_scalar(nth_parent)?.to_u8()?;
378                let name = this.read_immediate(name)?;
379
380                let name = this.read_byte_slice(&name)?;
381                // We must make `name` owned because we need to
382                // end the shared borrow from `read_byte_slice` before we can
383                // start the mutable borrow for `give_pointer_debug_name`.
384                let name = String::from_utf8_lossy(name).into_owned();
385                this.give_pointer_debug_name(ptr, nth_parent, &name)?;
386            }
387            "miri_static_root" => {
388                let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
389                let ptr = this.read_pointer(ptr)?;
390                let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
391                if offset != Size::ZERO {
392                    throw_unsup_format!(
393                        "pointer passed to `miri_static_root` must point to beginning of an allocated block"
394                    );
395                }
396                this.machine.static_roots.push(alloc_id);
397            }
398            "miri_host_to_target_path" => {
399                let [ptr, out, out_size] =
400                    this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
401                let ptr = this.read_pointer(ptr)?;
402                let out = this.read_pointer(out)?;
403                let out_size = this.read_scalar(out_size)?.to_target_usize(this)?;
404
405                // The host affects program behavior here, so this requires isolation to be disabled.
406                this.check_no_isolation("`miri_host_to_target_path`")?;
407
408                // We read this as a plain OsStr and write it as a path, which will convert it to the target.
409                let path = this.read_os_str_from_c_str(ptr)?.to_owned();
410                let (success, needed_size) =
411                    this.write_path_to_c_str(Path::new(&path), out, out_size)?;
412                // Return value: 0 on success, otherwise the size it would have needed.
413                this.write_int(if success { 0 } else { needed_size }, dest)?;
414            }
415            // Obtains the size of a Miri backtrace. See the README for details.
416            "miri_backtrace_size" => {
417                this.handle_miri_backtrace_size(abi, link_name, args, dest)?;
418            }
419            // Obtains a Miri backtrace. See the README for details.
420            "miri_get_backtrace" => {
421                // `check_shim` happens inside `handle_miri_get_backtrace`.
422                this.handle_miri_get_backtrace(abi, link_name, args)?;
423            }
424            // Resolves a Miri backtrace frame. See the README for details.
425            "miri_resolve_frame" => {
426                // `check_shim` happens inside `handle_miri_resolve_frame`.
427                this.handle_miri_resolve_frame(abi, link_name, args, dest)?;
428            }
429            // Writes the function and file names of a Miri backtrace frame into a user provided buffer. See the README for details.
430            "miri_resolve_frame_names" => {
431                this.handle_miri_resolve_frame_names(abi, link_name, args)?;
432            }
433            // Writes some bytes to the interpreter's stdout/stderr. See the
434            // README for details.
435            "miri_write_to_stdout" | "miri_write_to_stderr" => {
436                let [msg] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
437                let msg = this.read_immediate(msg)?;
438                let msg = this.read_byte_slice(&msg)?;
439                // Note: we're ignoring errors writing to host stdout/stderr.
440                let _ignore = match link_name.as_str() {
441                    "miri_write_to_stdout" => std::io::stdout().write_all(msg),
442                    "miri_write_to_stderr" => std::io::stderr().write_all(msg),
443                    _ => unreachable!(),
444                };
445            }
446            // Promises that a pointer has a given symbolic alignment.
447            "miri_promise_symbolic_alignment" => {
448                use rustc_abi::AlignFromBytesError;
449
450                let [ptr, align] =
451                    this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
452                let ptr = this.read_pointer(ptr)?;
453                let align = this.read_target_usize(align)?;
454                if !align.is_power_of_two() {
455                    throw_unsup_format!(
456                        "`miri_promise_symbolic_alignment`: alignment must be a power of 2, got {align}"
457                    );
458                }
459                let align = Align::from_bytes(align).unwrap_or_else(|err| {
460                    match err {
461                        AlignFromBytesError::NotPowerOfTwo(_) => unreachable!(),
462                        // When the alignment is a power of 2 but too big, clamp it to MAX.
463                        AlignFromBytesError::TooLarge(_) => Align::MAX,
464                    }
465                });
466                let addr = ptr.addr();
467                // Cannot panic since `align` is a power of 2 and hence non-zero.
468                if addr.bytes().strict_rem(align.bytes()) != 0 {
469                    throw_unsup_format!(
470                        "`miri_promise_symbolic_alignment`: pointer is not actually aligned"
471                    );
472                }
473                if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr, 0) {
474                    let alloc_align = this.get_alloc_info(alloc_id).align;
475                    // If the newly promised alignment is bigger than the native alignment of this
476                    // allocation, and bigger than the previously promised alignment, then set it.
477                    if align > alloc_align
478                        && this
479                            .machine
480                            .symbolic_alignment
481                            .get_mut()
482                            .get(&alloc_id)
483                            .is_none_or(|&(_, old_align)| align > old_align)
484                    {
485                        this.machine.symbolic_alignment.get_mut().insert(alloc_id, (offset, align));
486                    }
487                }
488            }
489
490            // Aborting the process.
491            "exit" => {
492                let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
493                let code = this.read_scalar(code)?.to_i32()?;
494                throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false });
495            }
496            "abort" => {
497                let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
498                throw_machine_stop!(TerminationInfo::Abort(
499                    "the program aborted execution".to_owned()
500                ))
501            }
502
503            // Standard C allocation
504            "malloc" => {
505                let [size] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
506                let size = this.read_target_usize(size)?;
507                if size <= this.max_size_of_val().bytes() {
508                    let res = this.malloc(size, AllocInit::Uninit)?;
509                    this.write_pointer(res, dest)?;
510                } else {
511                    // If this does not fit in an isize, return null and, on Unix, set errno.
512                    if this.target_os_is_unix() {
513                        this.set_last_error(LibcError("ENOMEM"))?;
514                    }
515                    this.write_null(dest)?;
516                }
517            }
518            "calloc" => {
519                let [items, elem_size] =
520                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
521                let items = this.read_target_usize(items)?;
522                let elem_size = this.read_target_usize(elem_size)?;
523                if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
524                    let res = this.malloc(size.bytes(), AllocInit::Zero)?;
525                    this.write_pointer(res, dest)?;
526                } else {
527                    // On size overflow, return null and, on Unix, set errno.
528                    if this.target_os_is_unix() {
529                        this.set_last_error(LibcError("ENOMEM"))?;
530                    }
531                    this.write_null(dest)?;
532                }
533            }
534            "free" => {
535                let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
536                let ptr = this.read_pointer(ptr)?;
537                this.free(ptr)?;
538            }
539            "realloc" => {
540                let [old_ptr, new_size] =
541                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
542                let old_ptr = this.read_pointer(old_ptr)?;
543                let new_size = this.read_target_usize(new_size)?;
544                if new_size <= this.max_size_of_val().bytes() {
545                    let res = this.realloc(old_ptr, new_size)?;
546                    this.write_pointer(res, dest)?;
547                } else {
548                    // If this does not fit in an isize, return null and, on Unix, set errno.
549                    if this.target_os_is_unix() {
550                        this.set_last_error(LibcError("ENOMEM"))?;
551                    }
552                    this.write_null(dest)?;
553                }
554            }
555
556            // Rust allocation
557            name if name == this.mangle_internal_symbol("__rust_alloc") || name == "miri_alloc" => {
558                let default = |ecx: &mut MiriInterpCx<'tcx>| {
559                    // Only call `check_shim` when `#[global_allocator]` isn't used. When that
560                    // macro is used, we act like no shim exists, so that the exported function can run.
561                    let [size, align] =
562                        ecx.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
563                    let size = ecx.read_target_usize(size)?;
564                    let align = ecx.read_target_usize(align)?;
565
566                    ecx.check_rustc_alloc_request(size, align)?;
567
568                    let memory_kind = match link_name.as_str() {
569                        "miri_alloc" => MiriMemoryKind::Miri,
570                        _ => MiriMemoryKind::Rust,
571                    };
572
573                    let ptr = ecx.allocate_ptr(
574                        Size::from_bytes(size),
575                        Align::from_bytes(align).unwrap(),
576                        memory_kind.into(),
577                        AllocInit::Uninit,
578                    )?;
579
580                    ecx.write_pointer(ptr, dest)
581                };
582
583                match link_name.as_str() {
584                    "miri_alloc" => {
585                        default(this)?;
586                        return interp_ok(EmulateItemResult::NeedsReturn);
587                    }
588                    _ => return this.emulate_allocator(default),
589                }
590            }
591            name if name == this.mangle_internal_symbol("__rust_alloc_zeroed") => {
592                return this.emulate_allocator(|this| {
593                    // See the comment for `__rust_alloc` why `check_shim` is only called in the
594                    // default case.
595                    let [size, align] =
596                        this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
597                    let size = this.read_target_usize(size)?;
598                    let align = this.read_target_usize(align)?;
599
600                    this.check_rustc_alloc_request(size, align)?;
601
602                    let ptr = this.allocate_ptr(
603                        Size::from_bytes(size),
604                        Align::from_bytes(align).unwrap(),
605                        MiriMemoryKind::Rust.into(),
606                        AllocInit::Zero,
607                    )?;
608                    this.write_pointer(ptr, dest)
609                });
610            }
611            name if name == this.mangle_internal_symbol("__rust_dealloc")
612                || name == "miri_dealloc" =>
613            {
614                let default = |ecx: &mut MiriInterpCx<'tcx>| {
615                    // See the comment for `__rust_alloc` why `check_shim` is only called in the
616                    // default case.
617                    let [ptr, old_size, align] =
618                        ecx.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
619                    let ptr = ecx.read_pointer(ptr)?;
620                    let old_size = ecx.read_target_usize(old_size)?;
621                    let align = ecx.read_target_usize(align)?;
622
623                    let memory_kind = match link_name.as_str() {
624                        "miri_dealloc" => MiriMemoryKind::Miri,
625                        _ => MiriMemoryKind::Rust,
626                    };
627
628                    // No need to check old_size/align; we anyway check that they match the allocation.
629                    ecx.deallocate_ptr(
630                        ptr,
631                        Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
632                        memory_kind.into(),
633                    )
634                };
635
636                match link_name.as_str() {
637                    "miri_dealloc" => {
638                        default(this)?;
639                        return interp_ok(EmulateItemResult::NeedsReturn);
640                    }
641                    _ => return this.emulate_allocator(default),
642                }
643            }
644            name if name == this.mangle_internal_symbol("__rust_realloc") => {
645                return this.emulate_allocator(|this| {
646                    // See the comment for `__rust_alloc` why `check_shim` is only called in the
647                    // default case.
648                    let [ptr, old_size, align, new_size] =
649                        this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
650                    let ptr = this.read_pointer(ptr)?;
651                    let old_size = this.read_target_usize(old_size)?;
652                    let align = this.read_target_usize(align)?;
653                    let new_size = this.read_target_usize(new_size)?;
654                    // No need to check old_size; we anyway check that they match the allocation.
655
656                    this.check_rustc_alloc_request(new_size, align)?;
657
658                    let align = Align::from_bytes(align).unwrap();
659                    let new_ptr = this.reallocate_ptr(
660                        ptr,
661                        Some((Size::from_bytes(old_size), align)),
662                        Size::from_bytes(new_size),
663                        align,
664                        MiriMemoryKind::Rust.into(),
665                        AllocInit::Uninit,
666                    )?;
667                    this.write_pointer(new_ptr, dest)
668                });
669            }
670            name if name == this.mangle_internal_symbol("__rust_no_alloc_shim_is_unstable_v2") => {
671                // This is a no-op shim that only exists to prevent making the allocator shims instantly stable.
672                let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
673            }
674            name if name
675                == this.mangle_internal_symbol("__rust_alloc_error_handler_should_panic_v2") =>
676            {
677                // Gets the value of the `oom` option.
678                let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
679                let val = this.tcx.sess.opts.unstable_opts.oom.should_panic();
680                this.write_int(val, dest)?;
681            }
682
683            // C memory handling functions
684            "memcmp" => {
685                let [left, right, n] =
686                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
687                let left = this.read_pointer(left)?;
688                let right = this.read_pointer(right)?;
689                let n = Size::from_bytes(this.read_target_usize(n)?);
690
691                // C requires that this must always be a valid pointer (C18 §7.1.4).
692                this.ptr_get_alloc_id(left, 0)?;
693                this.ptr_get_alloc_id(right, 0)?;
694
695                let result = {
696                    let left_bytes = this.read_bytes_ptr_strip_provenance(left, n)?;
697                    let right_bytes = this.read_bytes_ptr_strip_provenance(right, n)?;
698
699                    use std::cmp::Ordering::*;
700                    match left_bytes.cmp(right_bytes) {
701                        Less => -1i32,
702                        Equal => 0,
703                        Greater => 1,
704                    }
705                };
706
707                this.write_scalar(Scalar::from_i32(result), dest)?;
708            }
709            "memrchr" => {
710                let [ptr, val, num] =
711                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
712                let ptr = this.read_pointer(ptr)?;
713                let val = this.read_scalar(val)?.to_i32()?;
714                let num = this.read_target_usize(num)?;
715                // The docs say val is "interpreted as unsigned char".
716                #[expect(clippy::as_conversions)]
717                let val = val as u8;
718
719                // C requires that this must always be a valid pointer (C18 §7.1.4).
720                this.ptr_get_alloc_id(ptr, 0)?;
721
722                if let Some(idx) = this
723                    .read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))?
724                    .iter()
725                    .rev()
726                    .position(|&c| c == val)
727                {
728                    let idx = u64::try_from(idx).unwrap();
729                    #[expect(clippy::arithmetic_side_effects)] // idx < num, so this never wraps
730                    let new_ptr = ptr.wrapping_offset(Size::from_bytes(num - idx - 1), this);
731                    this.write_pointer(new_ptr, dest)?;
732                } else {
733                    this.write_null(dest)?;
734                }
735            }
736            "memchr" => {
737                let [ptr, val, num] =
738                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
739                let ptr = this.read_pointer(ptr)?;
740                let val = this.read_scalar(val)?.to_i32()?;
741                let num = this.read_target_usize(num)?;
742                // The docs say val is "interpreted as unsigned char".
743                #[expect(clippy::as_conversions)]
744                let val = val as u8;
745
746                // C requires that this must always be a valid pointer (C18 §7.1.4).
747                this.ptr_get_alloc_id(ptr, 0)?;
748
749                let idx = this
750                    .read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(num))?
751                    .iter()
752                    .position(|&c| c == val);
753                if let Some(idx) = idx {
754                    let new_ptr = ptr.wrapping_offset(Size::from_bytes(idx), this);
755                    this.write_pointer(new_ptr, dest)?;
756                } else {
757                    this.write_null(dest)?;
758                }
759            }
760            "strlen" => {
761                let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
762                let ptr = this.read_pointer(ptr)?;
763                // This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
764                let n = this.read_c_str(ptr)?.len();
765                this.write_scalar(
766                    Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
767                    dest,
768                )?;
769            }
770            "wcslen" => {
771                let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
772                let ptr = this.read_pointer(ptr)?;
773                // This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
774                let n = this.read_wchar_t_str(ptr)?.len();
775                this.write_scalar(
776                    Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
777                    dest,
778                )?;
779            }
780            "memcpy" => {
781                let [ptr_dest, ptr_src, n] =
782                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
783                let ptr_dest = this.read_pointer(ptr_dest)?;
784                let ptr_src = this.read_pointer(ptr_src)?;
785                let n = this.read_target_usize(n)?;
786
787                // C requires that this must always be a valid pointer, even if `n` is zero, so we better check that.
788                // (This is more than Rust requires, so `mem_copy` is not sufficient.)
789                this.ptr_get_alloc_id(ptr_dest, 0)?;
790                this.ptr_get_alloc_id(ptr_src, 0)?;
791
792                this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
793                this.write_pointer(ptr_dest, dest)?;
794            }
795            "strcpy" => {
796                let [ptr_dest, ptr_src] =
797                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
798                let ptr_dest = this.read_pointer(ptr_dest)?;
799                let ptr_src = this.read_pointer(ptr_src)?;
800
801                // We use `read_c_str` to determine the amount of data to copy,
802                // and then use `mem_copy` for the actual copy. This means
803                // pointer provenance is preserved by this implementation of `strcpy`.
804                // That is probably overly cautious, but there also is no fundamental
805                // reason to have `strcpy` destroy pointer provenance.
806                // This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
807                let n = this.read_c_str(ptr_src)?.len().strict_add(1);
808                this.mem_copy(ptr_src, ptr_dest, Size::from_bytes(n), true)?;
809                this.write_pointer(ptr_dest, dest)?;
810            }
811
812            // math functions (note that there are also intrinsics for some other functions)
813            #[rustfmt::skip]
814            | "cbrtf"
815            | "coshf"
816            | "sinhf"
817            | "tanf"
818            | "tanhf"
819            | "acosf"
820            | "asinf"
821            | "atanf"
822            | "log1pf"
823            | "expm1f"
824            | "tgammaf"
825            | "erff"
826            | "erfcf"
827            => {
828                let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
829                let f = this.read_scalar(f)?.to_f32()?;
830
831                let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| {
832                    // Using host floats (but it's fine, these operations do not have
833                    // guaranteed precision).
834                    let f_host = f.to_host();
835                    let res = match link_name.as_str() {
836                        "cbrtf" => f_host.cbrt(),
837                        "coshf" => f_host.cosh(),
838                        "sinhf" => f_host.sinh(),
839                        "tanf" => f_host.tan(),
840                        "tanhf" => f_host.tanh(),
841                        "acosf" => f_host.acos(),
842                        "asinf" => f_host.asin(),
843                        "atanf" => f_host.atan(),
844                        "log1pf" => f_host.ln_1p(),
845                        "expm1f" => f_host.exp_m1(),
846                        "tgammaf" => f_host.gamma(),
847                        "erff" => f_host.erf(),
848                        "erfcf" => f_host.erfc(),
849                        _ => bug!(),
850                    };
851                    let res = res.to_soft();
852                    // Apply a relative error of 4ULP to introduce some non-determinism
853                    // simulating imprecise implementations and optimizations.
854                    let res = math::apply_random_float_error_ulp(this, res, 4);
855
856                    // Clamp the result to the guaranteed range of this function according to the C standard,
857                    // if any.
858                    math::clamp_float_value(link_name.as_str(), res)
859                });
860                let res = this.adjust_nan(res, &[f]);
861                this.write_scalar(res, dest)?;
862            }
863            #[rustfmt::skip]
864            | "_hypotf"
865            | "hypotf"
866            | "atan2f"
867            | "fdimf"
868            => {
869                let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
870                let f1 = this.read_scalar(f1)?.to_f32()?;
871                let f2 = this.read_scalar(f2)?.to_f32()?;
872
873                let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2])
874                    .unwrap_or_else(|| {
875                        let res = match link_name.as_str() {
876                            // underscore case for windows, here and below
877                            // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019)
878                            // Using host floats (but it's fine, these operations do not have guaranteed precision).
879                            "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(),
880                            "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(),
881                            #[allow(deprecated)]
882                            "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
883                            _ => bug!(),
884                        };
885                        // Apply a relative error of 4ULP to introduce some non-determinism
886                        // simulating imprecise implementations and optimizations.
887                        let res = math::apply_random_float_error_ulp(this, res, 4);
888
889                        // Clamp the result to the guaranteed range of this function according to the C standard,
890                        // if any.
891                        math::clamp_float_value(link_name.as_str(), res)
892                    });
893                let res = this.adjust_nan(res, &[f1, f2]);
894                this.write_scalar(res, dest)?;
895            }
896            #[rustfmt::skip]
897            | "cbrt"
898            | "cosh"
899            | "sinh"
900            | "tan"
901            | "tanh"
902            | "acos"
903            | "asin"
904            | "atan"
905            | "log1p"
906            | "expm1"
907            | "tgamma"
908            | "erf"
909            | "erfc"
910            => {
911                let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
912                let f = this.read_scalar(f)?.to_f64()?;
913
914                let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| {
915                    // Using host floats (but it's fine, these operations do not have
916                    // guaranteed precision).
917                    let f_host = f.to_host();
918                    let res = match link_name.as_str() {
919                        "cbrt" => f_host.cbrt(),
920                        "cosh" => f_host.cosh(),
921                        "sinh" => f_host.sinh(),
922                        "tan" => f_host.tan(),
923                        "tanh" => f_host.tanh(),
924                        "acos" => f_host.acos(),
925                        "asin" => f_host.asin(),
926                        "atan" => f_host.atan(),
927                        "log1p" => f_host.ln_1p(),
928                        "expm1" => f_host.exp_m1(),
929                        "tgamma" => f_host.gamma(),
930                        "erf" => f_host.erf(),
931                        "erfc" => f_host.erfc(),
932                        _ => bug!(),
933                    };
934                    let res = res.to_soft();
935                    // Apply a relative error of 4ULP to introduce some non-determinism
936                    // simulating imprecise implementations and optimizations.
937                    let res = math::apply_random_float_error_ulp(this, res, 4);
938
939                    // Clamp the result to the guaranteed range of this function according to the C standard,
940                    // if any.
941                    math::clamp_float_value(link_name.as_str(), res)
942                });
943                let res = this.adjust_nan(res, &[f]);
944                this.write_scalar(res, dest)?;
945            }
946            #[rustfmt::skip]
947            | "_hypot"
948            | "hypot"
949            | "atan2"
950            | "fdim"
951            => {
952                let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
953                let f1 = this.read_scalar(f1)?.to_f64()?;
954                let f2 = this.read_scalar(f2)?.to_f64()?;
955
956                let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]).unwrap_or_else(|| {
957                    let res = match link_name.as_str() {
958                        // underscore case for windows, here and below
959                        // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019)
960                        // Using host floats (but it's fine, these operations do not have guaranteed precision).
961                        "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(),
962                        "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(),
963                        #[allow(deprecated)]
964                        "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
965                        _ => bug!(),
966                    };
967                    // Apply a relative error of 4ULP to introduce some non-determinism
968                    // simulating imprecise implementations and optimizations.
969                    let res = math::apply_random_float_error_ulp(this, res, 4);
970
971                    // Clamp the result to the guaranteed range of this function according to the C standard,
972                    // if any.
973                    math::clamp_float_value(link_name.as_str(), res)
974                });
975                let res = this.adjust_nan(res, &[f1, f2]);
976                this.write_scalar(res, dest)?;
977            }
978            #[rustfmt::skip]
979            | "_ldexp"
980            | "ldexp"
981            | "scalbn"
982            => {
983                let [x, exp] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
984                // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same.
985                let x = this.read_scalar(x)?.to_f64()?;
986                let exp = this.read_scalar(exp)?.to_i32()?;
987
988                let res = x.scalbn(exp);
989                let res = this.adjust_nan(res, &[x]);
990                this.write_scalar(res, dest)?;
991            }
992            "lgammaf_r" => {
993                let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
994                let x = this.read_scalar(x)?.to_f32()?;
995                let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?;
996
997                // Using host floats (but it's fine, these operations do not have guaranteed precision).
998                let (res, sign) = x.to_host().ln_gamma();
999                this.write_int(sign, &signp)?;
1000
1001                let res = res.to_soft();
1002                // Apply a relative error of 4ULP to introduce some non-determinism
1003                // simulating imprecise implementations and optimizations.
1004                let res = math::apply_random_float_error_ulp(this, res, 4);
1005                // Clamp the result to the guaranteed range of this function according to the C standard,
1006                // if any.
1007                let res = math::clamp_float_value(link_name.as_str(), res);
1008                let res = this.adjust_nan(res, &[x]);
1009                this.write_scalar(res, dest)?;
1010            }
1011            "lgamma_r" => {
1012                let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
1013                let x = this.read_scalar(x)?.to_f64()?;
1014                let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?;
1015
1016                // Using host floats (but it's fine, these operations do not have guaranteed precision).
1017                let (res, sign) = x.to_host().ln_gamma();
1018                this.write_int(sign, &signp)?;
1019
1020                let res = res.to_soft();
1021                // Apply a relative error of 4ULP to introduce some non-determinism
1022                // simulating imprecise implementations and optimizations.
1023                let res = math::apply_random_float_error_ulp(this, res, 4);
1024                // Clamp the result to the guaranteed range of this function according to the C standard,
1025                // if any.
1026                let res = math::clamp_float_value(link_name.as_str(), res);
1027                let res = this.adjust_nan(res, &[x]);
1028                this.write_scalar(res, dest)?;
1029            }
1030
1031            // LLVM intrinsics
1032            "llvm.prefetch" => {
1033                let [p, rw, loc, ty] =
1034                    this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
1035
1036                let _ = this.read_pointer(p)?;
1037                let rw = this.read_scalar(rw)?.to_i32()?;
1038                let loc = this.read_scalar(loc)?.to_i32()?;
1039                let ty = this.read_scalar(ty)?.to_i32()?;
1040
1041                if ty == 1 {
1042                    // Data cache prefetch.
1043                    // Notably, we do not have to check the pointer, this operation is never UB!
1044
1045                    if !matches!(rw, 0 | 1) {
1046                        throw_unsup_format!("invalid `rw` value passed to `llvm.prefetch`: {}", rw);
1047                    }
1048                    if !matches!(loc, 0..=3) {
1049                        throw_unsup_format!(
1050                            "invalid `loc` value passed to `llvm.prefetch`: {}",
1051                            loc
1052                        );
1053                    }
1054                } else {
1055                    throw_unsup_format!("unsupported `llvm.prefetch` type argument: {}", ty);
1056                }
1057            }
1058            // Used to implement the x86 `_mm{,256,512}_popcnt_epi{8,16,32,64}` and wasm
1059            // `{i,u}8x16_popcnt` functions.
1060            name if name.starts_with("llvm.ctpop.v") => {
1061                let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
1062
1063                let (op, op_len) = this.project_to_simd(op)?;
1064                let (dest, dest_len) = this.project_to_simd(dest)?;
1065
1066                assert_eq!(dest_len, op_len);
1067
1068                for i in 0..dest_len {
1069                    let op = this.read_immediate(&this.project_index(&op, i)?)?;
1070                    // Use `to_uint` to get a zero-extended `u128`. Those
1071                    // extra zeros will not affect `count_ones`.
1072                    let res = op.to_scalar().to_uint(op.layout.size)?.count_ones();
1073
1074                    this.write_scalar(
1075                        Scalar::from_uint(res, op.layout.size),
1076                        &this.project_index(&dest, i)?,
1077                    )?;
1078                }
1079            }
1080
1081            // Target-specific shims
1082            name if name.starts_with("llvm.x86.")
1083                && (this.tcx.sess.target.arch == "x86"
1084                    || this.tcx.sess.target.arch == "x86_64") =>
1085            {
1086                return shims::x86::EvalContextExt::emulate_x86_intrinsic(
1087                    this, link_name, abi, args, dest,
1088                );
1089            }
1090            name if name.starts_with("llvm.aarch64.") && this.tcx.sess.target.arch == "aarch64" => {
1091                return shims::aarch64::EvalContextExt::emulate_aarch64_intrinsic(
1092                    this, link_name, abi, args, dest,
1093                );
1094            }
1095            // FIXME: Move this to an `arm` submodule.
1096            "llvm.arm.hint" if this.tcx.sess.target.arch == "arm" => {
1097                let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
1098                let arg = this.read_scalar(arg)?.to_i32()?;
1099                // Note that different arguments might have different target feature requirements.
1100                match arg {
1101                    // YIELD
1102                    1 => {
1103                        this.expect_target_feature_for_intrinsic(link_name, "v6")?;
1104                        this.yield_active_thread();
1105                    }
1106                    _ => {
1107                        throw_unsup_format!("unsupported llvm.arm.hint argument {}", arg);
1108                    }
1109                }
1110            }
1111
1112            // Platform-specific shims
1113            _ =>
1114                return match this.tcx.sess.target.os.as_ref() {
1115                    _ if this.target_os_is_unix() =>
1116                        shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner(
1117                            this, link_name, abi, args, dest,
1118                        ),
1119                    "wasi" =>
1120                        shims::wasi::foreign_items::EvalContextExt::emulate_foreign_item_inner(
1121                            this, link_name, abi, args, dest,
1122                        ),
1123                    "windows" =>
1124                        shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner(
1125                            this, link_name, abi, args, dest,
1126                        ),
1127                    _ => interp_ok(EmulateItemResult::NotSupported),
1128                },
1129        };
1130        // We only fall through to here if we did *not* hit the `_` arm above,
1131        // i.e., if we actually emulated the function with one of the shims.
1132        interp_ok(EmulateItemResult::NeedsReturn)
1133    }
1134}