]> git.lizzy.rs Git - rust.git/blobdiff - src/shims/foreign_items.rs
Auto merge of #2183 - RalfJung:better-provenance-control, r=RalfJung
[rust.git] / src / shims / foreign_items.rs
index d588e2962a1501bf48a49172f5f8541c6d8e37b0..12b5b40e69e88b38c86e15d04b8e226238363a59 100644 (file)
@@ -1,25 +1,50 @@
-use std::{convert::{TryInto, TryFrom}, iter};
+use std::{collections::hash_map::Entry, iter};
 
 use log::trace;
 
-use rustc_hir::def_id::DefId;
+use rustc_apfloat::Float;
+use rustc_ast::expand::allocator::AllocatorKind;
+use rustc_hir::{
+    def::DefKind,
+    def_id::{CrateNum, DefId, LOCAL_CRATE},
+};
+use rustc_middle::middle::{
+    codegen_fn_attrs::CodegenFnAttrFlags, dependency_format::Linkage,
+    exported_symbols::ExportedSymbol,
+};
 use rustc_middle::mir;
-use rustc_target::{abi::{Align, Size}, spec::PanicStrategy};
 use rustc_middle::ty;
-use rustc_apfloat::Float;
-use rustc_span::symbol::sym;
+use rustc_session::config::CrateType;
+use rustc_span::Symbol;
+use rustc_target::{
+    abi::{Align, Size},
+    spec::abi::Abi,
+};
 
-use crate::*;
 use super::backtrace::EvalContextExt as _;
-use helpers::check_arg_count;
+use crate::helpers::convert::Truncate;
+use crate::*;
+
+/// Returned by `emulate_foreign_item_by_name`.
+pub enum EmulateByNameResult<'mir, 'tcx> {
+    /// The caller is expected to jump to the return block.
+    NeedsJumping,
+    /// Jumping has already been taken care of.
+    AlreadyJumped,
+    /// A MIR body has been found for the function
+    MirBody(&'mir mir::Body<'tcx>, ty::Instance<'tcx>),
+    /// The item is not supported.
+    NotSupported,
+}
 
 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
     /// Returns the minimum alignment for the target architecture for allocations of the given size.
     fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align {
         let this = self.eval_context_ref();
-        // List taken from `libstd/sys_common/alloc.rs`.
-        let min_align = match this.tcx.sess.target.arch.as_str() {
+        // List taken from `library/std/src/sys/common/alloc.rs`.
+        // This list should be kept in sync with the one from libstd.
+        let min_align = match this.tcx.sess.target.arch.as_ref() {
             "x86" | "arm" | "mips" | "powerpc" | "powerpc64" | "asmjs" | "wasm32" => 8,
             "x86_64" | "aarch64" | "mips64" | "s390x" | "sparc64" => 16,
             arch => bug!("Unsupported target architecture: {}", arch),
@@ -44,286 +69,446 @@ fn prev_power_of_two(x: u64) -> u64 {
         Align::from_bytes(prev_power_of_two(size)).unwrap()
     }
 
-    fn malloc(&mut self, size: u64, zero_init: bool, kind: MiriMemoryKind) -> Scalar<Tag> {
+    fn malloc(
+        &mut self,
+        size: u64,
+        zero_init: bool,
+        kind: MiriMemoryKind,
+    ) -> InterpResult<'tcx, Pointer<Option<Tag>>> {
         let this = self.eval_context_mut();
         if size == 0 {
-            Scalar::null_ptr(this)
+            Ok(Pointer::null())
         } else {
             let align = this.min_align(size, kind);
-            let ptr = this.memory.allocate(Size::from_bytes(size), align, kind.into());
+            let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?;
             if zero_init {
                 // We just allocated this, the access is definitely in-bounds.
-                this.memory.write_bytes(ptr.into(), iter::repeat(0u8).take(size as usize)).unwrap();
+                this.write_bytes_ptr(ptr.into(), iter::repeat(0u8).take(size as usize)).unwrap();
             }
-            Scalar::Ptr(ptr)
+            Ok(ptr.into())
         }
     }
 
-    fn free(&mut self, ptr: Scalar<Tag>, kind: MiriMemoryKind) -> InterpResult<'tcx> {
+    fn free(&mut self, ptr: Pointer<Option<Tag>>, kind: MiriMemoryKind) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
-        if !this.is_null(ptr)? {
-            let ptr = this.force_ptr(ptr)?;
-            this.memory.deallocate(ptr, None, kind.into())?;
+        if !this.ptr_is_null(ptr)? {
+            this.deallocate_ptr(ptr, None, kind.into())?;
         }
         Ok(())
     }
 
     fn realloc(
         &mut self,
-        old_ptr: Scalar<Tag>,
+        old_ptr: Pointer<Option<Tag>>,
         new_size: u64,
         kind: MiriMemoryKind,
-    ) -> InterpResult<'tcx, Scalar<Tag>> {
+    ) -> InterpResult<'tcx, Pointer<Option<Tag>>> {
         let this = self.eval_context_mut();
         let new_align = this.min_align(new_size, kind);
-        if this.is_null(old_ptr)? {
+        if this.ptr_is_null(old_ptr)? {
             if new_size == 0 {
-                Ok(Scalar::null_ptr(this))
+                Ok(Pointer::null())
             } else {
                 let new_ptr =
-                    this.memory.allocate(Size::from_bytes(new_size), new_align, kind.into());
-                Ok(Scalar::Ptr(new_ptr))
+                    this.allocate_ptr(Size::from_bytes(new_size), new_align, kind.into())?;
+                Ok(new_ptr.into())
             }
         } else {
-            let old_ptr = this.force_ptr(old_ptr)?;
             if new_size == 0 {
-                this.memory.deallocate(old_ptr, None, kind.into())?;
-                Ok(Scalar::null_ptr(this))
+                this.deallocate_ptr(old_ptr, None, kind.into())?;
+                Ok(Pointer::null())
             } else {
-                let new_ptr = this.memory.reallocate(
+                let new_ptr = this.reallocate_ptr(
                     old_ptr,
                     None,
                     Size::from_bytes(new_size),
                     new_align,
                     kind.into(),
                 )?;
-                Ok(Scalar::Ptr(new_ptr))
+                Ok(new_ptr.into())
             }
         }
     }
 
+    /// Lookup the body of a function that has `link_name` as the symbol name.
+    fn lookup_exported_symbol(
+        &mut self,
+        link_name: Symbol,
+    ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
+        let this = self.eval_context_mut();
+        let tcx = this.tcx.tcx;
+
+        // If the result was cached, just return it.
+        // (Cannot use `or_insert` since the code below might have to throw an error.)
+        let entry = this.machine.exported_symbols_cache.entry(link_name);
+        let instance = *match entry {
+            Entry::Occupied(e) => e.into_mut(),
+            Entry::Vacant(e) => {
+                // Find it if it was not cached.
+                let mut instance_and_crate: Option<(ty::Instance<'_>, CrateNum)> = None;
+                // `dependency_formats` includes all the transitive informations needed to link a crate,
+                // which is what we need here since we need to dig out `exported_symbols` from all transitive
+                // dependencies.
+                let dependency_formats = tcx.dependency_formats(());
+                let dependency_format = dependency_formats
+                    .iter()
+                    .find(|(crate_type, _)| *crate_type == CrateType::Executable)
+                    .expect("interpreting a non-executable crate");
+                for cnum in iter::once(LOCAL_CRATE).chain(
+                    dependency_format.1.iter().enumerate().filter_map(|(num, &linkage)| {
+                        (linkage != Linkage::NotLinked).then_some(CrateNum::new(num + 1))
+                    }),
+                ) {
+                    // We can ignore `_export_info` here: we are a Rust crate, and everything is exported
+                    // from a Rust crate.
+                    for &(symbol, _export_info) in tcx.exported_symbols(cnum) {
+                        if let ExportedSymbol::NonGeneric(def_id) = symbol {
+                            let attrs = tcx.codegen_fn_attrs(def_id);
+                            let symbol_name = if let Some(export_name) = attrs.export_name {
+                                export_name
+                            } else if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) {
+                                tcx.item_name(def_id)
+                            } else {
+                                // Skip over items without an explicitly defined symbol name.
+                                continue;
+                            };
+                            if symbol_name == link_name {
+                                if let Some((original_instance, original_cnum)) = instance_and_crate
+                                {
+                                    // Make sure we are consistent wrt what is 'first' and 'second'.
+                                    let original_span =
+                                        tcx.def_span(original_instance.def_id()).data();
+                                    let span = tcx.def_span(def_id).data();
+                                    if original_span < span {
+                                        throw_machine_stop!(
+                                            TerminationInfo::MultipleSymbolDefinitions {
+                                                link_name,
+                                                first: original_span,
+                                                first_crate: tcx.crate_name(original_cnum),
+                                                second: span,
+                                                second_crate: tcx.crate_name(cnum),
+                                            }
+                                        );
+                                    } else {
+                                        throw_machine_stop!(
+                                            TerminationInfo::MultipleSymbolDefinitions {
+                                                link_name,
+                                                first: span,
+                                                first_crate: tcx.crate_name(cnum),
+                                                second: original_span,
+                                                second_crate: tcx.crate_name(original_cnum),
+                                            }
+                                        );
+                                    }
+                                }
+                                if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
+                                    throw_ub_format!(
+                                        "attempt to call an exported symbol that is not defined as a function"
+                                    );
+                                }
+                                instance_and_crate = Some((ty::Instance::mono(tcx, def_id), cnum));
+                            }
+                        }
+                    }
+                }
+
+                e.insert(instance_and_crate.map(|ic| ic.0))
+            }
+        };
+        match instance {
+            None => Ok(None), // no symbol with this name
+            Some(instance) => Ok(Some((this.load_mir(instance.def, None)?, instance))),
+        }
+    }
+
     /// Emulates calling a foreign item, failing if the item is not supported.
     /// This function will handle `goto_block` if needed.
     /// Returns Ok(None) if the foreign item was completely handled
     /// by this function.
     /// Returns Ok(Some(body)) if processing the foreign item
     /// is delegated to another function.
-    #[rustfmt::skip]
     fn emulate_foreign_item(
         &mut self,
         def_id: DefId,
+        abi: Abi,
         args: &[OpTy<'tcx, Tag>],
-        ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
-        unwind: Option<mir::BasicBlock>,
-    ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
+        dest: &PlaceTy<'tcx, Tag>,
+        ret: Option<mir::BasicBlock>,
+        unwind: StackPopUnwind,
+    ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
         let this = self.eval_context_mut();
-        let attrs = this.tcx.get_attrs(def_id);
-        let link_name = match this.tcx.sess.first_attr_value_str_by_name(&attrs, sym::link_name) {
-            Some(name) => name.as_str(),
-            None => this.tcx.item_name(def_id).as_str(),
-        };
-        // Strip linker suffixes (seen on 32-bit macOS).
-        let link_name = link_name.trim_end_matches("$UNIX2003");
+        let link_name = this.item_link_name(def_id);
         let tcx = this.tcx.tcx;
 
         // First: functions that diverge.
-        let (dest, ret) = match ret {
-            None => match link_name {
-                "miri_start_panic" => {
-                    this.handle_miri_start_panic(args, unwind)?;
-                    return Ok(None);
-                }
-                // This matches calls to the foreign item `panic_impl`.
-                // The implementation is provided by the function with the `#[panic_handler]` attribute.
-                "panic_impl" => {
-                    let panic_impl_id = tcx.lang_items().panic_impl().unwrap();
-                    let panic_impl_instance = ty::Instance::mono(tcx, panic_impl_id);
-                    return Ok(Some(&*this.load_mir(panic_impl_instance.def, None)?));
-                }
-                | "exit"
-                | "ExitProcess"
-                => {
-                    let &[code] = check_arg_count(args)?;
-                    // it's really u32 for ExitProcess, but we have to put it into the `Exit` variant anyway
-                    let code = this.read_scalar(code)?.to_i32()?;
-                    throw_machine_stop!(TerminationInfo::Exit(code.into()));
-                }
-                "abort" => {
-                    throw_machine_stop!(TerminationInfo::Abort("the program aborted execution".to_owned()))
-                }
-                _ => throw_unsup_format!("can't call (diverging) foreign function: {}", link_name),
-            },
+        let ret = match ret {
+            None =>
+                match &*link_name.as_str() {
+                    "miri_start_panic" => {
+                        // `check_shim` happens inside `handle_miri_start_panic`.
+                        this.handle_miri_start_panic(abi, link_name, args, unwind)?;
+                        return Ok(None);
+                    }
+                    // This matches calls to the foreign item `panic_impl`.
+                    // The implementation is provided by the function with the `#[panic_handler]` attribute.
+                    "panic_impl" => {
+                        // We don't use `check_shim` here because we are just forwarding to the lang
+                        // item. Argument count checking will be performed when the returned `Body` is
+                        // called.
+                        this.check_abi_and_shim_symbol_clash(abi, Abi::Rust, link_name)?;
+                        let panic_impl_id = tcx.lang_items().panic_impl().unwrap();
+                        let panic_impl_instance = ty::Instance::mono(tcx, panic_impl_id);
+                        return Ok(Some((
+                            &*this.load_mir(panic_impl_instance.def, None)?,
+                            panic_impl_instance,
+                        )));
+                    }
+                    #[rustfmt::skip]
+                    | "exit"
+                    | "ExitProcess"
+                    => {
+                        let exp_abi = if link_name.as_str() == "exit" {
+                            Abi::C { unwind: false }
+                        } else {
+                            Abi::System { unwind: false }
+                        };
+                        let [code] = this.check_shim(abi, exp_abi, link_name, args)?;
+                        // it's really u32 for ExitProcess, but we have to put it into the `Exit` variant anyway
+                        let code = this.read_scalar(code)?.to_i32()?;
+                        throw_machine_stop!(TerminationInfo::Exit(code.into()));
+                    }
+                    "abort" => {
+                        let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                        throw_machine_stop!(TerminationInfo::Abort(
+                            "the program aborted execution".to_owned()
+                        ))
+                    }
+                    _ => {
+                        if let Some(body) = this.lookup_exported_symbol(link_name)? {
+                            return Ok(Some(body));
+                        }
+                        this.handle_unsupported(format!(
+                            "can't call (diverging) foreign function: {}",
+                            link_name
+                        ))?;
+                        return Ok(None);
+                    }
+                },
             Some(p) => p,
         };
 
-        // Second: some functions that we forward to MIR implementations.
-        match link_name {
-            // This matches calls to the foreign item `__rust_start_panic`, that is,
-            // calls to `extern "Rust" { fn __rust_start_panic(...) }`
-            // (and `__rust_panic_cleanup`, respectively).
-            // We forward this to the underlying *implementation* in the panic runtime crate.
-            // Normally, this will be either `libpanic_unwind` or `libpanic_abort`, but it could
-            // also be a custom user-provided implementation via `#![feature(panic_runtime)]`
-            "__rust_start_panic" | "__rust_panic_cleanup" => {
-                // This replicates some of the logic in `inject_panic_runtime`.
-                // FIXME: is there a way to reuse that logic?
-                let panic_runtime = match this.tcx.sess.panic_strategy() {
-                    PanicStrategy::Unwind => sym::panic_unwind,
-                    PanicStrategy::Abort => sym::panic_abort,
-                };
-                let start_panic_instance =
-                    this.resolve_path(&[&*panic_runtime.as_str(), link_name]);
-                return Ok(Some(&*this.load_mir(start_panic_instance.def, None)?));
+        // Second: functions that return.
+        match this.emulate_foreign_item_by_name(link_name, abi, args, dest, ret)? {
+            EmulateByNameResult::NeedsJumping => {
+                trace!("{:?}", this.dump_place(**dest));
+                this.go_to_block(ret);
             }
-            _ => {}
-        }
+            EmulateByNameResult::AlreadyJumped => (),
+            EmulateByNameResult::MirBody(mir, instance) => return Ok(Some((mir, instance))),
+            EmulateByNameResult::NotSupported => {
+                if let Some(body) = this.lookup_exported_symbol(link_name)? {
+                    return Ok(Some(body));
+                }
 
-        // Third: functions that return.
-        if this.emulate_foreign_item_by_name(link_name, args, dest, ret)? {
-            trace!("{:?}", this.dump_place(*dest));
-            this.go_to_block(ret);
+                this.handle_unsupported(format!("can't call foreign function: {}", link_name))?;
+                return Ok(None);
+            }
         }
 
         Ok(None)
     }
 
-    /// Emulates calling a foreign item using its name, failing if the item is not supported.
-    /// Returns `true` if the caller is expected to jump to the return block, and `false` if
-    /// jumping has already been taken care of.
+    /// Emulates calling the internal __rust_* allocator functions
+    fn emulate_allocator(
+        &mut self,
+        symbol: Symbol,
+        default: impl FnOnce(&mut MiriEvalContext<'mir, 'tcx>) -> InterpResult<'tcx>,
+    ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
+        let this = self.eval_context_mut();
+
+        let allocator_kind = if let Some(allocator_kind) = this.tcx.allocator_kind(()) {
+            allocator_kind
+        } else {
+            // in real code, this symbol does not exist without an allocator
+            return Ok(EmulateByNameResult::NotSupported);
+        };
+
+        match allocator_kind {
+            AllocatorKind::Global => {
+                let (body, instance) = this
+                    .lookup_exported_symbol(symbol)?
+                    .expect("symbol should be present if there is a global allocator");
+
+                Ok(EmulateByNameResult::MirBody(body, instance))
+            }
+            AllocatorKind::Default => {
+                default(this)?;
+                Ok(EmulateByNameResult::NeedsJumping)
+            }
+        }
+    }
+
+    /// Emulates calling a foreign item using its name.
     fn emulate_foreign_item_by_name(
         &mut self,
-        link_name: &str,
+        link_name: Symbol,
+        abi: Abi,
         args: &[OpTy<'tcx, Tag>],
-        dest: PlaceTy<'tcx, Tag>,
+        dest: &PlaceTy<'tcx, Tag>,
         ret: mir::BasicBlock,
-    ) -> InterpResult<'tcx, bool> {
+    ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
         let this = self.eval_context_mut();
 
         // Here we dispatch all the shims for foreign functions. If you have a platform specific
         // shim, add it to the corresponding submodule.
-        match link_name {
+        match &*link_name.as_str() {
             // Miri-specific extern functions
             "miri_static_root" => {
-                let &[ptr] = check_arg_count(args)?;
-                let ptr = this.read_scalar(ptr)?.check_init()?;
-                let ptr = this.force_ptr(ptr)?;
-                if ptr.offset != Size::ZERO {
+                let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let ptr = this.read_pointer(ptr)?;
+                let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr)?;
+                if offset != Size::ZERO {
                     throw_unsup_format!("pointer passed to miri_static_root must point to beginning of an allocated block");
                 }
-                this.machine.static_roots.push(ptr.alloc_id);
+                this.machine.static_roots.push(alloc_id);
+            }
+
+            // Obtains the size of a Miri backtrace. See the README for details.
+            "miri_backtrace_size" => {
+                this.handle_miri_backtrace_size(abi, link_name, args, dest)?;
             }
 
             // Obtains a Miri backtrace. See the README for details.
             "miri_get_backtrace" => {
-                this.handle_miri_get_backtrace(args, dest)?;
+                // `check_shim` happens inside `handle_miri_get_backtrace`.
+                this.handle_miri_get_backtrace(abi, link_name, args, dest)?;
             }
 
             // Resolves a Miri backtrace frame. See the README for details.
             "miri_resolve_frame" => {
-                this.handle_miri_resolve_frame(args, dest)?;
+                // `check_shim` happens inside `handle_miri_resolve_frame`.
+                this.handle_miri_resolve_frame(abi, link_name, args, dest)?;
             }
 
+            // Writes the function and file names of a Miri backtrace frame into a user provided buffer. See the README for details.
+            "miri_resolve_frame_names" => {
+                this.handle_miri_resolve_frame_names(abi, link_name, args)?;
+            }
 
             // Standard C allocation
             "malloc" => {
-                let &[size] = check_arg_count(args)?;
+                let [size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
-                let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C);
-                this.write_scalar(res, dest)?;
+                let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C)?;
+                this.write_pointer(res, dest)?;
             }
             "calloc" => {
-                let &[items, len] = check_arg_count(args)?;
+                let [items, len] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 let items = this.read_scalar(items)?.to_machine_usize(this)?;
                 let len = this.read_scalar(len)?.to_machine_usize(this)?;
                 let size =
                     items.checked_mul(len).ok_or_else(|| err_ub_format!("overflow during calloc size computation"))?;
-                let res = this.malloc(size, /*zero_init:*/ true, MiriMemoryKind::C);
-                this.write_scalar(res, dest)?;
+                let res = this.malloc(size, /*zero_init:*/ true, MiriMemoryKind::C)?;
+                this.write_pointer(res, dest)?;
             }
             "free" => {
-                let &[ptr] = check_arg_count(args)?;
-                let ptr = this.read_scalar(ptr)?.check_init()?;
+                let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let ptr = this.read_pointer(ptr)?;
                 this.free(ptr, MiriMemoryKind::C)?;
             }
             "realloc" => {
-                let &[old_ptr, new_size] = check_arg_count(args)?;
-                let old_ptr = this.read_scalar(old_ptr)?.check_init()?;
+                let [old_ptr, new_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let old_ptr = this.read_pointer(old_ptr)?;
                 let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?;
                 let res = this.realloc(old_ptr, new_size, MiriMemoryKind::C)?;
-                this.write_scalar(res, dest)?;
+                this.write_pointer(res, dest)?;
             }
 
             // Rust allocation
-            // (Usually these would be forwarded to to `#[global_allocator]`; we instead implement a generic
-            // allocation that also checks that all conditions are met, such as not permitting zero-sized allocations.)
             "__rust_alloc" => {
-                let &[size, align] = check_arg_count(args)?;
+                let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
                 let align = this.read_scalar(align)?.to_machine_usize(this)?;
-                Self::check_alloc_request(size, align)?;
-                let ptr = this.memory.allocate(
-                    Size::from_bytes(size),
-                    Align::from_bytes(align).unwrap(),
-                    MiriMemoryKind::Rust.into(),
-                );
-                this.write_scalar(ptr, dest)?;
+
+                return this.emulate_allocator(Symbol::intern("__rg_alloc"), |this| {
+                    Self::check_alloc_request(size, align)?;
+
+                    let ptr = this.allocate_ptr(
+                        Size::from_bytes(size),
+                        Align::from_bytes(align).unwrap(),
+                        MiriMemoryKind::Rust.into(),
+                    )?;
+
+                    this.write_pointer(ptr, dest)
+                });
             }
             "__rust_alloc_zeroed" => {
-                let &[size, align] = check_arg_count(args)?;
+                let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
                 let align = this.read_scalar(align)?.to_machine_usize(this)?;
-                Self::check_alloc_request(size, align)?;
-                let ptr = this.memory.allocate(
-                    Size::from_bytes(size),
-                    Align::from_bytes(align).unwrap(),
-                    MiriMemoryKind::Rust.into(),
-                );
-                // We just allocated this, the access is definitely in-bounds.
-                this.memory.write_bytes(ptr.into(), iter::repeat(0u8).take(usize::try_from(size).unwrap())).unwrap();
-                this.write_scalar(ptr, dest)?;
+
+                return this.emulate_allocator(Symbol::intern("__rg_alloc_zeroed"), |this| {
+                    Self::check_alloc_request(size, align)?;
+
+                    let ptr = this.allocate_ptr(
+                        Size::from_bytes(size),
+                        Align::from_bytes(align).unwrap(),
+                        MiriMemoryKind::Rust.into(),
+                    )?;
+
+                    // We just allocated this, the access is definitely in-bounds.
+                    this.write_bytes_ptr(ptr.into(), iter::repeat(0u8).take(usize::try_from(size).unwrap())).unwrap();
+                    this.write_pointer(ptr, dest)
+                });
             }
             "__rust_dealloc" => {
-                let &[ptr, old_size, align] = check_arg_count(args)?;
-                let ptr = this.read_scalar(ptr)?.check_init()?;
+                let [ptr, old_size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let ptr = this.read_pointer(ptr)?;
                 let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?;
                 let align = this.read_scalar(align)?.to_machine_usize(this)?;
-                // No need to check old_size/align; we anyway check that they match the allocation.
-                let ptr = this.force_ptr(ptr)?;
-                this.memory.deallocate(
-                    ptr,
-                    Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
-                    MiriMemoryKind::Rust.into(),
-                )?;
+
+                return this.emulate_allocator(Symbol::intern("__rg_dealloc"), |this| {
+                    // No need to check old_size/align; we anyway check that they match the allocation.
+                    this.deallocate_ptr(
+                        ptr,
+                        Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
+                        MiriMemoryKind::Rust.into(),
+                    )
+                });
             }
             "__rust_realloc" => {
-                let &[ptr, old_size, align, new_size] = check_arg_count(args)?;
-                let ptr = this.force_ptr(this.read_scalar(ptr)?.check_init()?)?;
+                let [ptr, old_size, align, new_size] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let ptr = this.read_pointer(ptr)?;
                 let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?;
                 let align = this.read_scalar(align)?.to_machine_usize(this)?;
                 let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?;
-                Self::check_alloc_request(new_size, align)?;
                 // No need to check old_size; we anyway check that they match the allocation.
-                let align = Align::from_bytes(align).unwrap();
-                let new_ptr = this.memory.reallocate(
-                    ptr,
-                    Some((Size::from_bytes(old_size), align)),
-                    Size::from_bytes(new_size),
-                    align,
-                    MiriMemoryKind::Rust.into(),
-                )?;
-                this.write_scalar(new_ptr, dest)?;
+
+                return this.emulate_allocator(Symbol::intern("__rg_realloc"), |this| {
+                    Self::check_alloc_request(new_size, align)?;
+
+                    let align = Align::from_bytes(align).unwrap();
+                    let new_ptr = this.reallocate_ptr(
+                        ptr,
+                        Some((Size::from_bytes(old_size), align)),
+                        Size::from_bytes(new_size),
+                        align,
+                        MiriMemoryKind::Rust.into(),
+                    )?;
+                    this.write_pointer(new_ptr, dest)
+                });
             }
 
             // C memory handling functions
             "memcmp" => {
-                let &[left, right, n] = check_arg_count(args)?;
-                let left = this.read_scalar(left)?.check_init()?;
-                let right = this.read_scalar(right)?.check_init()?;
+                let [left, right, n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let left = this.read_pointer(left)?;
+                let right = this.read_pointer(right)?;
                 let n = Size::from_bytes(this.read_scalar(n)?.to_machine_usize(this)?);
 
                 let result = {
-                    let left_bytes = this.memory.read_bytes(left, n)?;
-                    let right_bytes = this.memory.read_bytes(right, n)?;
+                    let left_bytes = this.read_bytes_ptr(left, n)?;
+                    let right_bytes = this.read_bytes_ptr(right, n)?;
 
                     use std::cmp::Ordering::*;
                     match left_bytes.cmp(right_bytes) {
@@ -336,48 +521,47 @@ fn emulate_foreign_item_by_name(
                 this.write_scalar(Scalar::from_i32(result), dest)?;
             }
             "memrchr" => {
-                let &[ptr, val, num] = check_arg_count(args)?;
-                let ptr = this.read_scalar(ptr)?.check_init()?;
+                let [ptr, val, num] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let ptr = this.read_pointer(ptr)?;
                 let val = this.read_scalar(val)?.to_i32()? as u8;
                 let num = this.read_scalar(num)?.to_machine_usize(this)?;
                 if let Some(idx) = this
-                    .memory
-                    .read_bytes(ptr, Size::from_bytes(num))?
+                    .read_bytes_ptr(ptr, Size::from_bytes(num))?
                     .iter()
                     .rev()
                     .position(|&c| c == val)
                 {
-                    let new_ptr = ptr.ptr_offset(Size::from_bytes(num - idx as u64 - 1), this)?;
-                    this.write_scalar(new_ptr, dest)?;
+                    let new_ptr = ptr.offset(Size::from_bytes(num - idx as u64 - 1), this)?;
+                    this.write_pointer(new_ptr, dest)?;
                 } else {
                     this.write_null(dest)?;
                 }
             }
             "memchr" => {
-                let &[ptr, val, num] = check_arg_count(args)?;
-                let ptr = this.read_scalar(ptr)?.check_init()?;
+                let [ptr, val, num] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let ptr = this.read_pointer(ptr)?;
                 let val = this.read_scalar(val)?.to_i32()? as u8;
                 let num = this.read_scalar(num)?.to_machine_usize(this)?;
                 let idx = this
-                    .memory
-                    .read_bytes(ptr, Size::from_bytes(num))?
+                    .read_bytes_ptr(ptr, Size::from_bytes(num))?
                     .iter()
                     .position(|&c| c == val);
                 if let Some(idx) = idx {
-                    let new_ptr = ptr.ptr_offset(Size::from_bytes(idx as u64), this)?;
-                    this.write_scalar(new_ptr, dest)?;
+                    let new_ptr = ptr.offset(Size::from_bytes(idx as u64), this)?;
+                    this.write_pointer(new_ptr, dest)?;
                 } else {
                     this.write_null(dest)?;
                 }
             }
             "strlen" => {
-                let &[ptr] = check_arg_count(args)?;
-                let ptr = this.read_scalar(ptr)?.check_init()?;
-                let n = this.memory.read_c_str(ptr)?.len();
+                let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let ptr = this.read_pointer(ptr)?;
+                let n = this.read_c_str(ptr)?.len();
                 this.write_scalar(Scalar::from_machine_usize(u64::try_from(n).unwrap(), this), dest)?;
             }
 
             // math functions
+            #[rustfmt::skip]
             | "cbrtf"
             | "coshf"
             | "sinhf"
@@ -386,10 +570,10 @@ fn emulate_foreign_item_by_name(
             | "asinf"
             | "atanf"
             => {
-                let &[f] = check_arg_count(args)?;
+                let [f] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 // FIXME: Using host floats.
                 let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
-                let f = match link_name {
+                let f = match &*link_name.as_str() {
                     "cbrtf" => f.cbrt(),
                     "coshf" => f.cosh(),
                     "sinhf" => f.sinh(),
@@ -401,23 +585,25 @@ fn emulate_foreign_item_by_name(
                 };
                 this.write_scalar(Scalar::from_u32(f.to_bits()), dest)?;
             }
+            #[rustfmt::skip]
             | "_hypotf"
             | "hypotf"
             | "atan2f"
             => {
-                let &[f1, f2] = check_arg_count(args)?;
+                let [f1, f2] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 // underscore case for windows, here and below
                 // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019)
                 // FIXME: Using host floats.
                 let f1 = f32::from_bits(this.read_scalar(f1)?.to_u32()?);
                 let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?);
-                let n = match link_name {
+                let n = match &*link_name.as_str() {
                     "_hypotf" | "hypotf" => f1.hypot(f2),
                     "atan2f" => f1.atan2(f2),
                     _ => bug!(),
                 };
                 this.write_scalar(Scalar::from_u32(n.to_bits()), dest)?;
             }
+            #[rustfmt::skip]
             | "cbrt"
             | "cosh"
             | "sinh"
@@ -426,10 +612,10 @@ fn emulate_foreign_item_by_name(
             | "asin"
             | "atan"
             => {
-                let &[f] = check_arg_count(args)?;
+                let [f] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 // FIXME: Using host floats.
                 let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
-                let f = match link_name {
+                let f = match &*link_name.as_str() {
                     "cbrt" => f.cbrt(),
                     "cosh" => f.cosh(),
                     "sinh" => f.sinh(),
@@ -441,26 +627,28 @@ fn emulate_foreign_item_by_name(
                 };
                 this.write_scalar(Scalar::from_u64(f.to_bits()), dest)?;
             }
+            #[rustfmt::skip]
             | "_hypot"
             | "hypot"
             | "atan2"
             => {
-                let &[f1, f2] = check_arg_count(args)?;
+                let [f1, f2] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 // FIXME: Using host floats.
                 let f1 = f64::from_bits(this.read_scalar(f1)?.to_u64()?);
                 let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?);
-                let n = match link_name {
+                let n = match &*link_name.as_str() {
                     "_hypot" | "hypot" => f1.hypot(f2),
                     "atan2" => f1.atan2(f2),
                     _ => bug!(),
                 };
                 this.write_scalar(Scalar::from_u64(n.to_bits()), dest)?;
             }
+            #[rustfmt::skip]
             | "_ldexp"
             | "ldexp"
             | "scalbn"
             => {
-                let &[x, exp] = check_arg_count(args)?;
+                let [x, exp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same.
                 let x = this.read_scalar(x)?.to_f64()?;
                 let exp = this.read_scalar(exp)?.to_i32()?;
@@ -480,20 +668,49 @@ fn emulate_foreign_item_by_name(
             }
 
             // Architecture-specific shims
+            "llvm.x86.addcarry.64" if this.tcx.sess.target.arch == "x86_64" => {
+                // Computes u8+u64+u64, returning tuple (u8,u64) comprising the output carry and truncated sum.
+                let [c_in, a, b] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?;
+                let c_in = this.read_scalar(c_in)?.to_u8()?;
+                let a = this.read_scalar(a)?.to_u64()?;
+                let b = this.read_scalar(b)?.to_u64()?;
+
+                let wide_sum = u128::from(c_in) + u128::from(a) + u128::from(b);
+                let (c_out, sum) = ((wide_sum >> 64).truncate::<u8>(), wide_sum.truncate::<u64>());
+
+                let c_out_field = this.place_field(dest, 0)?;
+                this.write_scalar(Scalar::from_u8(c_out), &c_out_field)?;
+                let sum_field = this.place_field(dest, 1)?;
+                this.write_scalar(Scalar::from_u64(sum), &sum_field)?;
+            }
             "llvm.x86.sse2.pause" if this.tcx.sess.target.arch == "x86" || this.tcx.sess.target.arch == "x86_64" => {
-                let &[] = check_arg_count(args)?;
+                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 this.yield_active_thread();
             }
+            "llvm.aarch64.isb" if this.tcx.sess.target.arch == "aarch64" => {
+                let [arg] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?;
+                let arg = this.read_scalar(arg)?.to_i32()?;
+                match arg {
+                    15 => { // SY ("full system scope")
+                        this.yield_active_thread();
+                    }
+                    _ => {
+                        throw_unsup_format!("unsupported llvm.aarch64.isb argument {}", arg);
+                    }
+                }
+            }
 
             // Platform-specific shims
-            _ => match this.tcx.sess.target.os.as_str() {
-                "linux" | "macos" => return shims::posix::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret),
-                "windows" => return shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, args, dest, ret),
+            _ => match this.tcx.sess.target.os.as_ref() {
+                "linux" | "macos" => return shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret),
+                "windows" => return shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_by_name(this, link_name, abi, args, dest, ret),
                 target => throw_unsup_format!("the target `{}` is not supported", target),
             }
         };
 
-        Ok(true)
+        // We only fall through to here if we did *not* hit the `_` arm above,
+        // i.e., if we actually emulated the function.
+        Ok(EmulateByNameResult::NeedsJumping)
     }
 
     /// Check some basic requirements for this allocation request: