1 use std::cell::{Cell, RefCell};
3 use rustc::mir::interpret::{AllocId, Pointer, InterpResult};
4 use rustc_mir::interpret::Memory;
5 use rustc_target::abi::Size;
7 use crate::stacked_borrows::Tag;
10 pub type MemoryExtra = RefCell<GlobalState>;
12 #[derive(Clone, Debug, Default)]
13 pub struct AllocExtra {
14 base_addr: Cell<Option<u64>>
17 #[derive(Clone, Debug)]
18 pub struct GlobalState {
19 /// This is used as a map between the address of each allocation and its `AllocId`.
20 /// It is always sorted
21 pub int_to_ptr_map: Vec<(u64, AllocId)>,
22 /// This is used as a memory address when a new pointer is casted to an integer. It
23 /// is always larger than any address that was previously made part of a block.
24 pub next_base_addr: u64,
27 impl Default for GlobalState {
28 // FIXME: Query the page size in the future
29 fn default() -> Self {
31 int_to_ptr_map: Vec::default(),
32 next_base_addr: 2u64.pow(16)
37 impl<'mir, 'tcx> GlobalState {
40 memory: &Memory<'mir, 'tcx, Evaluator<'tcx>>,
41 ) -> InterpResult<'tcx, Pointer<Tag>> {
42 let global_state = memory.extra.intptrcast.borrow();
44 match global_state.int_to_ptr_map.binary_search_by_key(&int, |(addr, _)| *addr) {
46 let (_, alloc_id) = global_state.int_to_ptr_map[pos];
47 // `int` is equal to the starting address for an allocation, the offset should be
48 // zero. The pointer is untagged because it was created from a cast
49 Ok(Pointer::new_with_tag(alloc_id, Size::from_bytes(0), Tag::Untagged))
51 Err(0) => err!(DanglingPointerDeref),
53 // This is the largest of the adresses smaller than `int`,
54 // i.e. the greatest lower bound (glb)
55 let (glb, alloc_id) = global_state.int_to_ptr_map[pos - 1];
56 // This never overflows because `int >= glb`
57 let offset = int - glb;
58 // If the offset exceeds the size of the allocation, this access is illegal
59 if offset <= memory.get(alloc_id)?.bytes.len() as u64 {
60 // This pointer is untagged because it was created from a cast
61 Ok(Pointer::new_with_tag(alloc_id, Size::from_bytes(offset), Tag::Untagged))
63 err!(DanglingPointerDeref)
71 memory: &Memory<'mir, 'tcx, Evaluator<'tcx>>,
72 ) -> InterpResult<'tcx, u64> {
73 let mut global_state = memory.extra.intptrcast.borrow_mut();
75 let alloc = memory.get(ptr.alloc_id)?;
77 let base_addr = match alloc.extra.intptrcast.base_addr.get() {
78 Some(base_addr) => base_addr,
80 // This allocation does not have a base address yet, pick one.
81 let base_addr = Self::align_addr(global_state.next_base_addr, alloc.align.bytes());
82 global_state.next_base_addr = base_addr + alloc.bytes.len() as u64;
83 alloc.extra.intptrcast.base_addr.set(Some(base_addr));
84 // Given that `next_base_addr` increases in each allocation, pushing the
85 // corresponding tuple keeps `int_to_ptr_map` sorted
86 global_state.int_to_ptr_map.push((base_addr, ptr.alloc_id));
92 Ok(base_addr + ptr.offset.bytes())
95 /// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple
96 /// of `align` that is strictly larger to `addr`
97 fn align_addr(addr: u64, align: u64) -> u64 {
98 addr + align - addr % align