@@ -5,7 +5,6 @@ mod reuse_pool;
5
5
6
6
use std:: cell:: RefCell ;
7
7
use std:: cmp:: max;
8
- use std:: collections:: hash_map:: Entry ;
9
8
10
9
use rand:: Rng ;
11
10
@@ -151,6 +150,95 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
151
150
}
152
151
}
153
152
153
+ fn addr_from_alloc_id_uncached (
154
+ & self ,
155
+ global_state : & mut GlobalStateInner ,
156
+ alloc_id : AllocId ,
157
+ memory_kind : MemoryKind ,
158
+ ) -> InterpResult < ' tcx , u64 > {
159
+ let ecx = self . eval_context_ref ( ) ;
160
+ let mut rng = ecx. machine . rng . borrow_mut ( ) ;
161
+ let ( size, align, kind) = ecx. get_alloc_info ( alloc_id) ;
162
+ // This is either called immediately after allocation (and then cached), or when
163
+ // adjusting `tcx` pointers (which never get freed). So assert that we are looking
164
+ // at a live allocation. This also ensures that we never re-assign an address to an
165
+ // allocation that previously had an address, but then was freed and the address
166
+ // information was removed.
167
+ assert ! ( !matches!( kind, AllocKind :: Dead ) ) ;
168
+
169
+ // This allocation does not have a base address yet, pick or reuse one.
170
+ if ecx. machine . native_lib . is_some ( ) {
171
+ // In native lib mode, we use the "real" address of the bytes for this allocation.
172
+ // This ensures the interpreted program and native code have the same view of memory.
173
+ let base_ptr = match kind {
174
+ AllocKind :: LiveData => {
175
+ if ecx. tcx . try_get_global_alloc ( alloc_id) . is_some ( ) {
176
+ // For new global allocations, we always pre-allocate the memory to be able use the machine address directly.
177
+ let prepared_bytes = MiriAllocBytes :: zeroed ( size, align)
178
+ . unwrap_or_else ( || {
179
+ panic ! ( "Miri ran out of memory: cannot create allocation of {size:?} bytes" )
180
+ } ) ;
181
+ let ptr = prepared_bytes. as_ptr ( ) ;
182
+ // Store prepared allocation space to be picked up for use later.
183
+ global_state
184
+ . prepared_alloc_bytes
185
+ . try_insert ( alloc_id, prepared_bytes)
186
+ . unwrap ( ) ;
187
+ ptr
188
+ } else {
189
+ ecx. get_alloc_bytes_unchecked_raw ( alloc_id) ?
190
+ }
191
+ }
192
+ AllocKind :: Function | AllocKind :: VTable => {
193
+ // Allocate some dummy memory to get a unique address for this function/vtable.
194
+ let alloc_bytes =
195
+ MiriAllocBytes :: from_bytes ( & [ 0u8 ; 1 ] , Align :: from_bytes ( 1 ) . unwrap ( ) ) ;
196
+ let ptr = alloc_bytes. as_ptr ( ) ;
197
+ // Leak the underlying memory to ensure it remains unique.
198
+ std:: mem:: forget ( alloc_bytes) ;
199
+ ptr
200
+ }
201
+ AllocKind :: Dead => unreachable ! ( ) ,
202
+ } ;
203
+ // Ensure this pointer's provenance is exposed, so that it can be used by FFI code.
204
+ return Ok ( base_ptr. expose_provenance ( ) . try_into ( ) . unwrap ( ) ) ;
205
+ }
206
+ // We are not in native lib mode, so we control the addresses ourselves.
207
+ if let Some ( ( reuse_addr, clock) ) =
208
+ global_state. reuse . take_addr ( & mut * rng, size, align, memory_kind, ecx. active_thread ( ) )
209
+ {
210
+ if let Some ( clock) = clock {
211
+ ecx. acquire_clock ( & clock) ;
212
+ }
213
+ Ok ( reuse_addr)
214
+ } else {
215
+ // We have to pick a fresh address.
216
+ // Leave some space to the previous allocation, to give it some chance to be less aligned.
217
+ // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
218
+ let slack = rng. gen_range ( 0 ..16 ) ;
219
+ // From next_base_addr + slack, round up to adjust for alignment.
220
+ let base_addr = global_state
221
+ . next_base_addr
222
+ . checked_add ( slack)
223
+ . ok_or_else ( || err_exhaust ! ( AddressSpaceFull ) ) ?;
224
+ let base_addr = align_addr ( base_addr, align. bytes ( ) ) ;
225
+
226
+ // Remember next base address. If this allocation is zero-sized, leave a gap of at
227
+ // least 1 to avoid two allocations having the same base address. (The logic in
228
+ // `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers
229
+ // need to be distinguishable!)
230
+ global_state. next_base_addr = base_addr
231
+ . checked_add ( max ( size. bytes ( ) , 1 ) )
232
+ . ok_or_else ( || err_exhaust ! ( AddressSpaceFull ) ) ?;
233
+ // Even if `Size` didn't overflow, we might still have filled up the address space.
234
+ if global_state. next_base_addr > ecx. target_usize_max ( ) {
235
+ throw_exhaust ! ( AddressSpaceFull ) ;
236
+ }
237
+
238
+ Ok ( base_addr)
239
+ }
240
+ }
241
+
154
242
fn addr_from_alloc_id (
155
243
& self ,
156
244
alloc_id : AllocId ,
@@ -160,98 +248,16 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
160
248
let mut global_state = ecx. machine . alloc_addresses . borrow_mut ( ) ;
161
249
let global_state = & mut * global_state;
162
250
163
- Ok ( match global_state. base_addr . entry ( alloc_id) {
164
- Entry :: Occupied ( entry) => * entry. get ( ) ,
165
- Entry :: Vacant ( entry) => {
166
- let mut rng = ecx. machine . rng . borrow_mut ( ) ;
167
- let ( size, align, kind) = ecx. get_alloc_info ( alloc_id) ;
168
- // This is either called immediately after allocation (and then cached), or when
169
- // adjusting `tcx` pointers (which never get freed). So assert that we are looking
170
- // at a live allocation. This also ensures that we never re-assign an address to an
171
- // allocation that previously had an address, but then was freed and the address
172
- // information was removed.
173
- assert ! ( !matches!( kind, AllocKind :: Dead ) ) ;
174
-
175
- // This allocation does not have a base address yet, pick or reuse one.
176
- let base_addr = if ecx. machine . native_lib . is_some ( ) {
177
- // In native lib mode, we use the "real" address of the bytes for this allocation.
178
- // This ensures the interpreted program and native code have the same view of memory.
179
- match kind {
180
- AllocKind :: LiveData => {
181
- let ptr = if ecx. tcx . try_get_global_alloc ( alloc_id) . is_some ( ) {
182
- // For new global allocations, we always pre-allocate the memory to be able use the machine address directly.
183
- let prepared_bytes = MiriAllocBytes :: zeroed ( size, align)
184
- . unwrap_or_else ( || {
185
- panic ! ( "Miri ran out of memory: cannot create allocation of {size:?} bytes" )
186
- } ) ;
187
- let ptr = prepared_bytes. as_ptr ( ) ;
188
- // Store prepared allocation space to be picked up for use later.
189
- global_state. prepared_alloc_bytes . try_insert ( alloc_id, prepared_bytes) . unwrap ( ) ;
190
- ptr
191
- } else {
192
- ecx. get_alloc_bytes_unchecked_raw ( alloc_id) ?
193
- } ;
194
- // Ensure this pointer's provenance is exposed, so that it can be used by FFI code.
195
- ptr. expose_provenance ( ) . try_into ( ) . unwrap ( )
196
- }
197
- AllocKind :: Function | AllocKind :: VTable => {
198
- // Allocate some dummy memory to get a unique address for this function/vtable.
199
- let alloc_bytes = MiriAllocBytes :: from_bytes ( & [ 0u8 ; 1 ] , Align :: from_bytes ( 1 ) . unwrap ( ) ) ;
200
- // We don't need to expose these bytes as nobody is allowed to access them.
201
- let addr = alloc_bytes. as_ptr ( ) . addr ( ) . try_into ( ) . unwrap ( ) ;
202
- // Leak the underlying memory to ensure it remains unique.
203
- std:: mem:: forget ( alloc_bytes) ;
204
- addr
205
- }
206
- AllocKind :: Dead => unreachable ! ( )
207
- }
208
- } else if let Some ( ( reuse_addr, clock) ) = global_state. reuse . take_addr (
209
- & mut * rng,
210
- size,
211
- align,
212
- memory_kind,
213
- ecx. active_thread ( ) ,
214
- ) {
215
- if let Some ( clock) = clock {
216
- ecx. acquire_clock ( & clock) ;
217
- }
218
- reuse_addr
219
- } else {
220
- // We have to pick a fresh address.
221
- // Leave some space to the previous allocation, to give it some chance to be less aligned.
222
- // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
223
- let slack = rng. gen_range ( 0 ..16 ) ;
224
- // From next_base_addr + slack, round up to adjust for alignment.
225
- let base_addr = global_state
226
- . next_base_addr
227
- . checked_add ( slack)
228
- . ok_or_else ( || err_exhaust ! ( AddressSpaceFull ) ) ?;
229
- let base_addr = align_addr ( base_addr, align. bytes ( ) ) ;
230
-
231
- // Remember next base address. If this allocation is zero-sized, leave a gap
232
- // of at least 1 to avoid two allocations having the same base address.
233
- // (The logic in `alloc_id_from_addr` assumes unique addresses, and different
234
- // function/vtable pointers need to be distinguishable!)
235
- global_state. next_base_addr = base_addr
236
- . checked_add ( max ( size. bytes ( ) , 1 ) )
237
- . ok_or_else ( || err_exhaust ! ( AddressSpaceFull ) ) ?;
238
- // Even if `Size` didn't overflow, we might still have filled up the address space.
239
- if global_state. next_base_addr > ecx. target_usize_max ( ) {
240
- throw_exhaust ! ( AddressSpaceFull ) ;
241
- }
242
-
243
- base_addr
244
- } ;
245
- trace ! (
246
- "Assigning base address {:#x} to allocation {:?} (size: {}, align: {})" ,
247
- base_addr,
248
- alloc_id,
249
- size. bytes( ) ,
250
- align. bytes( ) ,
251
- ) ;
251
+ match global_state. base_addr . get ( & alloc_id) {
252
+ Some ( & addr) => Ok ( addr) ,
253
+ None => {
254
+ // First time we're looking for the absolute address of this allocation.
255
+ let base_addr =
256
+ self . addr_from_alloc_id_uncached ( global_state, alloc_id, memory_kind) ?;
257
+ trace ! ( "Assigning base address {:#x} to allocation {:?}" , base_addr, alloc_id) ;
252
258
253
259
// Store address in cache.
254
- entry . insert ( base_addr) ;
260
+ global_state . base_addr . try_insert ( alloc_id , base_addr) . unwrap ( ) ;
255
261
256
262
// Also maintain the opposite mapping in `int_to_ptr_map`, ensuring we keep it sorted.
257
263
// We have a fast-path for the common case that this address is bigger than all previous ones.
@@ -269,9 +275,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
269
275
} ;
270
276
global_state. int_to_ptr_map . insert ( pos, ( base_addr, alloc_id) ) ;
271
277
272
- base_addr
278
+ Ok ( base_addr)
273
279
}
274
- } )
280
+ }
275
281
}
276
282
}
277
283
@@ -359,16 +365,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
359
365
360
366
// This returns some prepared `MiriAllocBytes`, either because `addr_from_alloc_id` reserved
361
367
// memory space in the past, or by doing the pre-allocation right upon being called.
362
- fn get_global_alloc_bytes ( & self , id : AllocId , kind : MemoryKind , bytes : & [ u8 ] , align : Align ) -> InterpResult < ' tcx , MiriAllocBytes > {
368
+ fn get_global_alloc_bytes (
369
+ & self ,
370
+ id : AllocId ,
371
+ kind : MemoryKind ,
372
+ bytes : & [ u8 ] ,
373
+ align : Align ,
374
+ ) -> InterpResult < ' tcx , MiriAllocBytes > {
363
375
let ecx = self . eval_context_ref ( ) ;
364
- Ok ( if ecx. machine . native_lib . is_some ( ) {
376
+ if ecx. machine . native_lib . is_some ( ) {
365
377
// In native lib mode, MiriAllocBytes for global allocations are handled via `prepared_alloc_bytes`.
366
- // This additional call ensures that some `MiriAllocBytes` are always prepared.
378
+ // This additional call ensures that some `MiriAllocBytes` are always prepared, just in case
379
+ // this function gets called before the first time `addr_from_alloc_id` gets called.
367
380
ecx. addr_from_alloc_id ( id, kind) ?;
368
- let mut global_state = ecx. machine . alloc_addresses . borrow_mut ( ) ;
369
381
// The memory we need here will have already been allocated during an earlier call to
370
382
// `addr_from_alloc_id` for this allocation. So don't create a new `MiriAllocBytes` here, instead
371
383
// fetch the previously prepared bytes from `prepared_alloc_bytes`.
384
+ let mut global_state = ecx. machine . alloc_addresses . borrow_mut ( ) ;
372
385
let mut prepared_alloc_bytes = global_state
373
386
. prepared_alloc_bytes
374
387
. remove ( & id)
@@ -378,10 +391,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
378
391
assert_eq ! ( prepared_alloc_bytes. len( ) , bytes. len( ) ) ;
379
392
// Copy allocation contents into prepared memory.
380
393
prepared_alloc_bytes. copy_from_slice ( bytes) ;
381
- prepared_alloc_bytes
394
+ Ok ( prepared_alloc_bytes)
382
395
} else {
383
- MiriAllocBytes :: from_bytes ( std:: borrow:: Cow :: Borrowed ( & * bytes) , align)
384
- } )
396
+ Ok ( MiriAllocBytes :: from_bytes ( std:: borrow:: Cow :: Borrowed ( bytes) , align) )
397
+ }
385
398
}
386
399
387
400
/// When a pointer is used for a memory access, this computes where in which allocation the
0 commit comments