@@ -54,6 +54,26 @@ pub enum AllocCheck {
54
54
MaybeDead ,
55
55
}
56
56
57
+ /// The value of a function pointer.
58
+ #[ derive( Debug , Copy , Clone ) ]
59
+ pub enum FnVal < ' tcx , Other > {
60
+ Instance ( Instance < ' tcx > ) ,
61
+ Other ( Other ) ,
62
+ }
63
+
64
+ impl < ' tcx , Other > FnVal < ' tcx , Other > {
65
+ pub fn as_instance ( self ) -> InterpResult < ' tcx , Instance < ' tcx > > {
66
+ match self {
67
+ FnVal :: Instance ( instance) =>
68
+ Ok ( instance) ,
69
+ FnVal :: Other ( _) =>
70
+ err ! ( MachineError (
71
+ format!( "Expected instance function pointer, got 'other' pointer" )
72
+ ) ) ,
73
+ }
74
+ }
75
+ }
76
+
57
77
// `Memory` has to depend on the `Machine` because some of its operations
58
78
// (e.g., `get`) call a `Machine` hook.
59
79
pub struct Memory < ' mir , ' tcx , M : Machine < ' mir , ' tcx > > {
@@ -69,16 +89,20 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
69
89
// FIXME: this should not be public, but interning currently needs access to it
70
90
pub ( super ) alloc_map : M :: MemoryMap ,
71
91
92
+ /// Map for "extra" function pointers.
93
+ extra_fn_ptr_map : FxHashMap < AllocId , M :: ExtraFnVal > ,
94
+
72
95
/// To be able to compare pointers with NULL, and to check alignment for accesses
73
96
/// to ZSTs (where pointers may dangle), we keep track of the size even for allocations
74
97
/// that do not exist any more.
98
+ // FIXME: this should not be public, but interning currently needs access to it
75
99
pub ( super ) dead_alloc_map : FxHashMap < AllocId , ( Size , Align ) > ,
76
100
77
101
/// Extra data added by the machine.
78
102
pub extra : M :: MemoryExtra ,
79
103
80
104
/// Lets us implement `HasDataLayout`, which is awfully convenient.
81
- pub ( super ) tcx : TyCtxtAt < ' tcx > ,
105
+ pub tcx : TyCtxtAt < ' tcx > ,
82
106
}
83
107
84
108
impl < ' mir , ' tcx , M : Machine < ' mir , ' tcx > > HasDataLayout for Memory < ' mir , ' tcx , M > {
98
122
fn clone ( & self ) -> Self {
99
123
Memory {
100
124
alloc_map : self . alloc_map . clone ( ) ,
125
+ extra_fn_ptr_map : self . extra_fn_ptr_map . clone ( ) ,
101
126
dead_alloc_map : self . dead_alloc_map . clone ( ) ,
102
127
extra : ( ) ,
103
128
tcx : self . tcx ,
@@ -109,6 +134,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
109
134
pub fn new ( tcx : TyCtxtAt < ' tcx > , extra : M :: MemoryExtra ) -> Self {
110
135
Memory {
111
136
alloc_map : M :: MemoryMap :: default ( ) ,
137
+ extra_fn_ptr_map : FxHashMap :: default ( ) ,
112
138
dead_alloc_map : FxHashMap :: default ( ) ,
113
139
extra,
114
140
tcx,
@@ -120,8 +146,21 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
120
146
ptr. with_tag ( M :: tag_static_base_pointer ( ptr. alloc_id , & self ) )
121
147
}
122
148
123
- pub fn create_fn_alloc ( & mut self , instance : Instance < ' tcx > ) -> Pointer < M :: PointerTag > {
124
- let id = self . tcx . alloc_map . lock ( ) . create_fn_alloc ( instance) ;
149
+ pub fn create_fn_alloc (
150
+ & mut self ,
151
+ fn_val : FnVal < ' tcx , M :: ExtraFnVal > ,
152
+ ) -> Pointer < M :: PointerTag >
153
+ {
154
+ let id = match fn_val {
155
+ FnVal :: Instance ( instance) => self . tcx . alloc_map . lock ( ) . create_fn_alloc ( instance) ,
156
+ FnVal :: Other ( extra) => {
157
+ // FIXME(RalfJung): Should we have a cache here?
158
+ let id = self . tcx . alloc_map . lock ( ) . reserve ( ) ;
159
+ let old = self . extra_fn_ptr_map . insert ( id, extra) ;
160
+ assert ! ( old. is_none( ) ) ;
161
+ id
162
+ }
163
+ } ;
125
164
self . tag_static_base_pointer ( Pointer :: from ( id) )
126
165
}
127
166
@@ -495,56 +534,65 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
495
534
id : AllocId ,
496
535
liveness : AllocCheck ,
497
536
) -> InterpResult < ' static , ( Size , Align ) > {
537
+ // Regular allocations.
498
538
if let Ok ( alloc) = self . get ( id) {
499
539
return Ok ( ( Size :: from_bytes ( alloc. bytes . len ( ) as u64 ) , alloc. align ) ) ;
500
540
}
501
- // can't do this in the match argument, we may get cycle errors since the lock would get
502
- // dropped after the match.
541
+ // Function pointers.
542
+ if let Ok ( _) = self . get_fn_alloc ( id) {
543
+ return if let AllocCheck :: Dereferencable = liveness {
544
+ // The caller requested no function pointers.
545
+ err ! ( DerefFunctionPointer )
546
+ } else {
547
+ Ok ( ( Size :: ZERO , Align :: from_bytes ( 1 ) . unwrap ( ) ) )
548
+ } ;
549
+ }
550
+ // Foreign statics.
551
+ // Can't do this in the match argument, we may get cycle errors since the lock would
552
+ // be held throughout the match.
503
553
let alloc = self . tcx . alloc_map . lock ( ) . get ( id) ;
504
- // Could also be a fn ptr or extern static
505
554
match alloc {
506
- Some ( GlobalAlloc :: Function ( ..) ) => {
507
- if let AllocCheck :: Dereferencable = liveness {
508
- // The caller requested no function pointers.
509
- err ! ( DerefFunctionPointer )
510
- } else {
511
- Ok ( ( Size :: ZERO , Align :: from_bytes ( 1 ) . unwrap ( ) ) )
512
- }
513
- }
514
- // `self.get` would also work, but can cause cycles if a static refers to itself
515
555
Some ( GlobalAlloc :: Static ( did) ) => {
516
- // The only way `get` couldn't have worked here is if this is an extern static
517
556
assert ! ( self . tcx. is_foreign_item( did) ) ;
518
557
// Use size and align of the type
519
558
let ty = self . tcx . type_of ( did) ;
520
559
let layout = self . tcx . layout_of ( ParamEnv :: empty ( ) . and ( ty) ) . unwrap ( ) ;
521
- Ok ( ( layout. size , layout. align . abi ) )
560
+ return Ok ( ( layout. size , layout. align . abi ) ) ;
522
561
}
523
- _ => {
524
- if let Ok ( alloc) = self . get ( id) {
525
- Ok ( ( Size :: from_bytes ( alloc. bytes . len ( ) as u64 ) , alloc. align ) )
526
- }
527
- else if let AllocCheck :: MaybeDead = liveness {
528
- // Deallocated pointers are allowed, we should be able to find
529
- // them in the map.
530
- Ok ( * self . dead_alloc_map . get ( & id)
531
- . expect ( "deallocated pointers should all be recorded in `dead_alloc_map`" ) )
532
- } else {
533
- err ! ( DanglingPointerDeref )
534
- }
535
- } ,
562
+ _ => { }
563
+ }
564
+ // The rest must be dead.
565
+ if let AllocCheck :: MaybeDead = liveness {
566
+ // Deallocated pointers are allowed, we should be able to find
567
+ // them in the map.
568
+ Ok ( * self . dead_alloc_map . get ( & id)
569
+ . expect ( "deallocated pointers should all be recorded in `dead_alloc_map`" ) )
570
+ } else {
571
+ err ! ( DanglingPointerDeref )
536
572
}
537
573
}
538
574
539
- pub fn get_fn ( & self , ptr : Pointer < M :: PointerTag > ) -> InterpResult < ' tcx , Instance < ' tcx > > {
575
+ fn get_fn_alloc ( & self , id : AllocId ) -> InterpResult < ' tcx , FnVal < ' tcx , M :: ExtraFnVal > > {
576
+ trace ! ( "reading fn ptr: {}" , id) ;
577
+ if let Some ( extra) = self . extra_fn_ptr_map . get ( & id) {
578
+ Ok ( FnVal :: Other ( * extra) )
579
+ } else {
580
+ match self . tcx . alloc_map . lock ( ) . get ( id) {
581
+ Some ( GlobalAlloc :: Function ( instance) ) => Ok ( FnVal :: Instance ( instance) ) ,
582
+ _ => Err ( InterpError :: ExecuteMemory . into ( ) ) ,
583
+ }
584
+ }
585
+ }
586
+
587
+ pub fn get_fn (
588
+ & self ,
589
+ ptr : Scalar < M :: PointerTag > ,
590
+ ) -> InterpResult < ' tcx , FnVal < ' tcx , M :: ExtraFnVal > > {
591
+ let ptr = self . force_ptr ( ptr) ?; // We definitely need a pointer value.
540
592
if ptr. offset . bytes ( ) != 0 {
541
593
return err ! ( InvalidFunctionPointer ) ;
542
594
}
543
- trace ! ( "reading fn ptr: {}" , ptr. alloc_id) ;
544
- match self . tcx . alloc_map . lock ( ) . get ( ptr. alloc_id ) {
545
- Some ( GlobalAlloc :: Function ( instance) ) => Ok ( instance) ,
546
- _ => Err ( InterpError :: ExecuteMemory . into ( ) ) ,
547
- }
595
+ self . get_fn_alloc ( ptr. alloc_id )
548
596
}
549
597
550
598
pub fn mark_immutable ( & mut self , id : AllocId ) -> InterpResult < ' tcx > {
0 commit comments