Skip to content

Commit 36c8199

Browse files
authored
Rollup merge of #147718 - RalfJung:miri-allocator_shim_contents, r=bjorn3
miri: use allocator_shim_contents codegen helper r? `@bjorn3` Follow-up to rust-lang/rust#147526, also using that new infrastructure in Miri.
2 parents c7f0651 + c1a0d35 commit 36c8199

File tree

4 files changed

+192
-198
lines changed

4 files changed

+192
-198
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
extern crate rustc_abi;
5454
extern crate rustc_apfloat;
5555
extern crate rustc_ast;
56+
extern crate rustc_codegen_ssa;
5657
extern crate rustc_const_eval;
5758
extern crate rustc_data_structures;
5859
extern crate rustc_errors;

src/machine.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use rand::rngs::StdRng;
1212
use rand::{Rng, SeedableRng};
1313
use rustc_abi::{Align, ExternAbi, Size};
1414
use rustc_apfloat::{Float, FloatConvert};
15+
use rustc_ast::expand::allocator::{self, SpecialAllocatorMethod};
16+
use rustc_data_structures::either::Either;
1517
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1618
#[allow(unused)]
1719
use rustc_data_structures::static_assert_size;
@@ -27,6 +29,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
2729
use rustc_session::config::InliningThreshold;
2830
use rustc_span::def_id::{CrateNum, DefId};
2931
use rustc_span::{Span, SpanData, Symbol};
32+
use rustc_symbol_mangling::mangle_internal_symbol;
3033
use rustc_target::callconv::FnAbi;
3134

3235
use crate::alloc_addresses::EvalContextExt;
@@ -652,6 +655,10 @@ pub struct MiriMachine<'tcx> {
652655
pub(crate) pthread_rwlock_sanity: Cell<bool>,
653656
pub(crate) pthread_condvar_sanity: Cell<bool>,
654657

658+
/// (Foreign) symbols that are synthesized as part of the allocator shim: the key indicates the
659+
/// name of the symbol being synthesized; the value indicates whether this should invoke some
660+
/// other symbol or whether this has special allocator semantics.
661+
pub(crate) allocator_shim_symbols: FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>>,
655662
/// Cache for `mangle_internal_symbol`.
656663
pub(crate) mangle_internal_symbol_cache: FxHashMap<&'static str, String>,
657664

@@ -819,6 +826,7 @@ impl<'tcx> MiriMachine<'tcx> {
819826
pthread_mutex_sanity: Cell::new(false),
820827
pthread_rwlock_sanity: Cell::new(false),
821828
pthread_condvar_sanity: Cell::new(false),
829+
allocator_shim_symbols: Self::allocator_shim_symbols(tcx),
822830
mangle_internal_symbol_cache: Default::default(),
823831
force_intrinsic_fallback: config.force_intrinsic_fallback,
824832
float_nondet: config.float_nondet,
@@ -827,6 +835,36 @@ impl<'tcx> MiriMachine<'tcx> {
827835
}
828836
}
829837

838+
fn allocator_shim_symbols(
839+
tcx: TyCtxt<'tcx>,
840+
) -> FxHashMap<Symbol, Either<Symbol, SpecialAllocatorMethod>> {
841+
use rustc_codegen_ssa::base::allocator_shim_contents;
842+
843+
// codegen uses `allocator_kind_for_codegen` here, but that's only needed to deal with
844+
// dylibs which we do not support.
845+
let Some(kind) = tcx.allocator_kind(()) else {
846+
return Default::default();
847+
};
848+
let methods = allocator_shim_contents(tcx, kind);
849+
let mut symbols = FxHashMap::default();
850+
for method in methods {
851+
let from_name = Symbol::intern(&mangle_internal_symbol(
852+
tcx,
853+
&allocator::global_fn_name(method.name),
854+
));
855+
let to = match method.special {
856+
Some(special) => Either::Right(special),
857+
None =>
858+
Either::Left(Symbol::intern(&mangle_internal_symbol(
859+
tcx,
860+
&allocator::default_fn_name(method.name),
861+
))),
862+
};
863+
symbols.try_insert(from_name, to).unwrap();
864+
}
865+
symbols
866+
}
867+
830868
pub(crate) fn late_init(
831869
ecx: &mut MiriInterpCx<'tcx>,
832870
config: &MiriConfig,
@@ -992,6 +1030,7 @@ impl VisitProvenance for MiriMachine<'_> {
9921030
pthread_mutex_sanity: _,
9931031
pthread_rwlock_sanity: _,
9941032
pthread_condvar_sanity: _,
1033+
allocator_shim_symbols: _,
9951034
mangle_internal_symbol_cache: _,
9961035
force_intrinsic_fallback: _,
9971036
float_nondet: _,

src/shims/alloc.rs

Lines changed: 94 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
use rustc_abi::{Align, Size};
2-
use rustc_ast::expand::allocator::AllocatorKind;
1+
use rustc_abi::{Align, AlignFromBytesError, CanonAbi, Size};
2+
use rustc_ast::expand::allocator::SpecialAllocatorMethod;
3+
use rustc_middle::ty::Ty;
4+
use rustc_span::Symbol;
5+
use rustc_target::callconv::FnAbi;
36

47
use crate::*;
58

@@ -54,30 +57,100 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
5457
Align::from_bytes(prev_power_of_two(size)).unwrap()
5558
}
5659

57-
/// Emulates calling the internal __rust_* allocator functions
58-
fn emulate_allocator(
60+
/// Check some basic requirements for this allocation request:
61+
/// non-zero size, power-of-two alignment.
62+
fn check_rust_alloc_request(&self, size: u64, align: u64) -> InterpResult<'tcx> {
63+
let this = self.eval_context_ref();
64+
if size == 0 {
65+
throw_ub_format!("creating allocation with size 0");
66+
}
67+
if size > this.max_size_of_val().bytes() {
68+
throw_ub_format!("creating an allocation larger than half the address space");
69+
}
70+
if let Err(e) = Align::from_bytes(align) {
71+
match e {
72+
AlignFromBytesError::TooLarge(_) => {
73+
throw_unsup_format!(
74+
"creating allocation with alignment {align} exceeding rustc's maximum \
75+
supported value"
76+
);
77+
}
78+
AlignFromBytesError::NotPowerOfTwo(_) => {
79+
throw_ub_format!("creating allocation with non-power-of-two alignment {align}");
80+
}
81+
}
82+
}
83+
84+
interp_ok(())
85+
}
86+
87+
fn rust_special_allocator_method(
5988
&mut self,
60-
default: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx>,
61-
) -> InterpResult<'tcx, EmulateItemResult> {
89+
method: SpecialAllocatorMethod,
90+
link_name: Symbol,
91+
abi: &FnAbi<'tcx, Ty<'tcx>>,
92+
args: &[OpTy<'tcx>],
93+
dest: &PlaceTy<'tcx>,
94+
) -> InterpResult<'tcx> {
6295
let this = self.eval_context_mut();
6396

64-
let Some(allocator_kind) = this.tcx.allocator_kind(()) else {
65-
// in real code, this symbol does not exist without an allocator
66-
return interp_ok(EmulateItemResult::NotSupported);
67-
};
97+
match method {
98+
SpecialAllocatorMethod::Alloc | SpecialAllocatorMethod::AllocZeroed => {
99+
let [size, align] =
100+
this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
101+
let size = this.read_target_usize(size)?;
102+
let align = this.read_target_usize(align)?;
103+
104+
this.check_rust_alloc_request(size, align)?;
68105

69-
match allocator_kind {
70-
AllocatorKind::Global => {
71-
// When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion
72-
// of this attribute. As such we have to call an exported Rust function,
73-
// and not execute any Miri shim. Somewhat unintuitively doing so is done
74-
// by returning `NotSupported`, which triggers the `lookup_exported_symbol`
75-
// fallback case in `emulate_foreign_item`.
76-
interp_ok(EmulateItemResult::NotSupported)
106+
let ptr = this.allocate_ptr(
107+
Size::from_bytes(size),
108+
Align::from_bytes(align).unwrap(),
109+
MiriMemoryKind::Rust.into(),
110+
if matches!(method, SpecialAllocatorMethod::AllocZeroed) {
111+
AllocInit::Zero
112+
} else {
113+
AllocInit::Uninit
114+
},
115+
)?;
116+
117+
this.write_pointer(ptr, dest)
118+
}
119+
SpecialAllocatorMethod::Dealloc => {
120+
let [ptr, old_size, align] =
121+
this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
122+
let ptr = this.read_pointer(ptr)?;
123+
let old_size = this.read_target_usize(old_size)?;
124+
let align = this.read_target_usize(align)?;
125+
126+
// No need to check old_size/align; we anyway check that they match the allocation.
127+
this.deallocate_ptr(
128+
ptr,
129+
Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
130+
MiriMemoryKind::Rust.into(),
131+
)
77132
}
78-
AllocatorKind::Default => {
79-
default(this)?;
80-
interp_ok(EmulateItemResult::NeedsReturn)
133+
SpecialAllocatorMethod::Realloc => {
134+
let [ptr, old_size, align, new_size] =
135+
this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
136+
let ptr = this.read_pointer(ptr)?;
137+
let old_size = this.read_target_usize(old_size)?;
138+
let align = this.read_target_usize(align)?;
139+
let new_size = this.read_target_usize(new_size)?;
140+
// No need to check old_size; we anyway check that they match the allocation.
141+
142+
this.check_rust_alloc_request(new_size, align)?;
143+
144+
let align = Align::from_bytes(align).unwrap();
145+
let new_ptr = this.reallocate_ptr(
146+
ptr,
147+
Some((Size::from_bytes(old_size), align)),
148+
Size::from_bytes(new_size),
149+
align,
150+
MiriMemoryKind::Rust.into(),
151+
AllocInit::Uninit,
152+
)?;
153+
this.write_pointer(new_ptr, dest)
81154
}
82155
}
83156
}

0 commit comments

Comments
 (0)