diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 2c5ec9dad59f1..447b103726a3e 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -482,6 +482,10 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::SIMULATE_ALLOCATOR) { + let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]); + } if let Some(align) = codegen_fn_attrs.alignment { llvm::set_alignment(llfn, align); } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index d536419ab3c20..c615aed6edf15 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -110,6 +110,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { sym::rustc_nounwind => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND, sym::rustc_reallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR, sym::rustc_deallocator => codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR, + sym::rustc_simulate_allocator => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::SIMULATE_ALLOCATOR + } sym::rustc_allocator_zeroed => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 17827b4e43b37..30b107e8b3f42 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -691,6 +691,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, IMPL_DETAIL ), + rustc_attr!( + rustc_simulate_allocator, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, IMPL_DETAIL + ), gated!( default_lib_allocator, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, allocator_internals, experimental!(default_lib_allocator), diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 90dff0f5c7da8..4bb118f4cd7c5 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -133,6 +133,8 @@ bitflags::bitflags! { const ALLOCATOR_ZEROED = 1 << 18; /// `#[no_builtins]`: indicates that disable implicit builtin knowledge of functions for the function. const NO_BUILTINS = 1 << 19; + /// `#[rustc_simulate_allocator]`: a hint to LLVM that the function simulates an allocation + const SIMULATE_ALLOCATOR = 1 << 20; } } rustc_data_structures::external_bitflags_debug! { CodegenFnAttrFlags } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cc3bda99a117b..7be366dcc8089 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1735,6 +1735,7 @@ symbols! { rustc_runtime, rustc_safe_intrinsic, rustc_serialize, + rustc_simulate_allocator, rustc_skip_during_method_dispatch, rustc_specialization_trait, rustc_std_internal_symbol, diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 6b07449292417..58de13d681ce1 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -446,6 +446,7 @@ // There are many unsafe functions taking pointers that don't dereference them. #![allow(clippy::not_unsafe_ptr_arg_deref)] +use crate::arch::asm; use crate::cmp::Ordering; use crate::marker::FnPtr; use crate::mem::{self, MaybeUninit}; @@ -2441,3 +2442,33 @@ pub macro addr_of($place:expr) { pub macro addr_of_mut($place:expr) { &raw mut $place } + +/// Simulate a realloc to a new address +/// +/// Intended for use with pointer tagging architecture features such as AArch64 TBI. +/// This function creates a new pointer with the address `new_address` and a brand new provenance, +/// simulating a realloc from the original address to the new address. +/// Note that this is only a simulated realloc - nothing actually gets moved or reallocated. +/// +/// SAFETY: Users *must* ensure that `new_address` actually contains the same memory as the original. +/// The primary use-case is working with various architecture pointer tagging schemes, where two +/// different 64-bit addresses can point to the same chunk of memory due to some bits being ignored. +/// When used incorrectly, this function can be used to violate the memory model in arbitrary ways. +/// Furthermore, after using this function, users must ensure that the underlying memory is only ever +/// accessed through the newly created pointer. Any accesses through the original pointer +/// (or any pointers derived from it) would be Undefined Behaviour. +#[inline(never)] +#[unstable(feature = "ptr_simulate_realloc", issue = "none")] +#[cfg_attr(not(bootstrap), rustc_simulate_allocator)] +#[allow(fuzzy_provenance_casts)] +pub unsafe fn simulate_realloc(original: *mut T, new_address: usize) -> *mut T { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + let mut ptr = new_address as *mut T; + // SAFETY: This does not do anything + unsafe { + asm!("/* simulate realloc from {original} to {ptr} */", + original = in(reg) original, ptr = inout(reg) ptr); + } + // FIXME: call Miri hooks to update the address of the original allocation + ptr +}