@@ -3,44 +3,79 @@ use std::mem::variant_count;
3
3
4
4
use crate :: * ;
5
5
6
- /// A Windows `HANDLE` that represents a resource instead of being null or a pseudohandle.
7
- ///
8
- /// This is a seperate type from [`Handle`] to simplify the packing and unpacking code.
9
- #[ derive( Clone , Copy ) ]
10
- enum RealHandle {
11
- Thread ( ThreadId ) ,
6
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq , Hash ) ]
7
+ pub enum PseudoHandle {
8
+ CurrentThread ,
12
9
}
13
10
14
- impl RealHandle {
15
- const USABLE_BITS : u32 = 31 ;
11
+ impl PseudoHandle {
12
+ const CURRENT_THREAD_VALUE : u32 = 0 ;
16
13
17
- const THREAD_DISCRIMINANT : u32 = 1 ;
14
+ fn value ( self ) -> u32 {
15
+ match self {
16
+ Self :: CurrentThread => Self :: CURRENT_THREAD_VALUE ,
17
+ }
18
+ }
19
+
20
+ fn from_value ( value : u32 ) -> Option < Self > {
21
+ match value {
22
+ Self :: CURRENT_THREAD_VALUE => Some ( Self :: CurrentThread ) ,
23
+ _ => None ,
24
+ }
25
+ }
26
+ }
27
+
28
+ /// Miri representation of a Windows `HANDLE`
29
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq , Hash ) ]
30
+ pub enum Handle {
31
+ Null ,
32
+ Pseudo ( PseudoHandle ) ,
33
+ Thread ( ThreadId ) ,
34
+ }
35
+
36
+ impl Handle {
37
+ const NULL_DISCRIMINANT : u32 = 0 ;
38
+ const PSEUDO_DISCRIMINANT : u32 = 1 ;
39
+ const THREAD_DISCRIMINANT : u32 = 2 ;
18
40
19
41
fn discriminant ( self ) -> u32 {
20
42
match self {
21
- // can't use zero here because all zero handle is invalid
43
+ Self :: Null => Self :: NULL_DISCRIMINANT ,
44
+ Self :: Pseudo ( _) => Self :: PSEUDO_DISCRIMINANT ,
22
45
Self :: Thread ( _) => Self :: THREAD_DISCRIMINANT ,
23
46
}
24
47
}
25
48
26
49
fn data ( self ) -> u32 {
27
50
match self {
51
+ Self :: Null => 0 ,
52
+ Self :: Pseudo ( pseudo_handle) => pseudo_handle. value ( ) ,
28
53
Self :: Thread ( thread) => thread. to_u32 ( ) ,
29
54
}
30
55
}
31
56
32
57
fn packed_disc_size ( ) -> u32 {
33
- // log2(x) + 1 is how many bits it takes to store x
34
- // because the discriminants start at 1, the variant count is equal to the highest discriminant
35
- variant_count :: < Self > ( ) . ilog2 ( ) + 1
58
+ // ceil(log2(x)) is how many bits it takes to store x numbers
59
+ let variant_count = variant_count :: < Self > ( ) ;
60
+
61
+ // however, std's ilog2 is floor(log2(x))
62
+ let floor_log2 = variant_count. ilog2 ( ) ;
63
+
64
+ // we need to add one for non powers of two to compensate for the difference
65
+ let ceil_log2 = if variant_count. is_power_of_two ( ) { floor_log2 } else { floor_log2 + 1 } ;
66
+
67
+ ceil_log2
36
68
}
37
69
38
- /// This function packs the discriminant and data values into a 31-bit space.
70
+ /// Converts a handle into its machine representation.
71
+ ///
72
+ /// The upper [`Self::packed_disc_size()`] bits are used to store a discriminant corresponding to the handle variant.
73
+ /// The remaining bits are used for the variant's field.
74
+ ///
39
75
/// None of this layout is guaranteed to applications by Windows or Miri.
40
- /// The sign bit is not used to avoid overlapping any pseudo-handles.
41
- fn to_packed ( self ) -> i32 {
76
+ fn to_packed ( self ) -> u32 {
42
77
let disc_size = Self :: packed_disc_size ( ) ;
43
- let data_size = Self :: USABLE_BITS - disc_size;
78
+ let data_size = u32 :: BITS - disc_size;
44
79
45
80
let discriminant = self . discriminant ( ) ;
46
81
let data = self . data ( ) ;
@@ -53,90 +88,54 @@ impl RealHandle {
53
88
54
89
// packs the data into the lower `data_size` bits
55
90
// and packs the discriminant right above the data
56
- ( discriminant << data_size | data) as i32
91
+ discriminant << data_size | data
57
92
}
58
93
59
94
fn new ( discriminant : u32 , data : u32 ) -> Option < Self > {
60
95
match discriminant {
96
+ Self :: NULL_DISCRIMINANT if data == 0 => Some ( Self :: Null ) ,
97
+ Self :: PSEUDO_DISCRIMINANT => Some ( Self :: Pseudo ( PseudoHandle :: from_value ( data) ?) ) ,
61
98
Self :: THREAD_DISCRIMINANT => Some ( Self :: Thread ( data. into ( ) ) ) ,
62
99
_ => None ,
63
100
}
64
101
}
65
102
66
103
/// see docs for `to_packed`
67
- fn from_packed ( handle : i32 ) -> Option < Self > {
68
- let handle_bits = handle as u32 ;
69
-
104
+ fn from_packed ( handle : u32 ) -> Option < Self > {
70
105
let disc_size = Self :: packed_disc_size ( ) ;
71
- let data_size = Self :: USABLE_BITS - disc_size;
106
+ let data_size = u32 :: BITS - disc_size;
72
107
73
108
// the lower `data_size` bits of this mask are 1
74
109
let data_mask = 2u32 . pow ( data_size) - 1 ;
75
110
76
111
// the discriminant is stored right above the lower `data_size` bits
77
- let discriminant = handle_bits >> data_size;
112
+ let discriminant = handle >> data_size;
78
113
79
114
// the data is stored in the lower `data_size` bits
80
- let data = handle_bits & data_mask;
115
+ let data = handle & data_mask;
81
116
82
117
Self :: new ( discriminant, data)
83
118
}
84
- }
85
-
86
- /// Miri representation of a Windows `HANDLE`
87
- #[ derive( Clone , Copy , Debug , PartialEq , Eq , Hash ) ]
88
- pub enum Handle {
89
- Null , // = 0
90
-
91
- // pseudo-handles
92
- // The lowest real windows pseudo-handle is -6, so miri pseduo-handles start at -7 to break code hardcoding these values
93
- CurrentThread , // = -7
94
-
95
- // real handles
96
- Thread ( ThreadId ) ,
97
- }
98
-
99
- impl Handle {
100
- const CURRENT_THREAD_VALUE : i32 = -7 ;
101
-
102
- fn to_packed ( self ) -> i32 {
103
- match self {
104
- Self :: Null => 0 ,
105
- Self :: CurrentThread => Self :: CURRENT_THREAD_VALUE ,
106
- Self :: Thread ( thread) => RealHandle :: Thread ( thread) . to_packed ( ) ,
107
- }
108
- }
109
119
110
120
pub fn to_scalar ( self , cx : & impl HasDataLayout ) -> Scalar < Provenance > {
111
121
// 64-bit handles are sign extended 32-bit handles
112
122
// see https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication
113
- let handle = self . to_packed ( ) . into ( ) ;
114
-
115
- Scalar :: from_machine_isize ( handle, cx)
116
- }
117
-
118
- fn from_packed ( handle : i64 ) -> Option < Self > {
119
- let current_thread_val = Self :: CURRENT_THREAD_VALUE as i64 ;
120
-
121
- if handle == 0 {
122
- Some ( Self :: Null )
123
- } else if handle == current_thread_val {
124
- Some ( Self :: CurrentThread )
125
- } else if let Ok ( handle) = handle. try_into ( ) {
126
- match RealHandle :: from_packed ( handle) ? {
127
- RealHandle :: Thread ( id) => Some ( Self :: Thread ( id) ) ,
128
- }
129
- } else {
130
- // if a handle doesn't fit in an i32, it isn't valid.
131
- None
132
- }
123
+ let signed_handle = self . to_packed ( ) as i32 ;
124
+ Scalar :: from_machine_isize ( signed_handle. into ( ) , cx)
133
125
}
134
126
135
127
pub fn from_scalar < ' tcx > (
136
128
handle : Scalar < Provenance > ,
137
129
cx : & impl HasDataLayout ,
138
130
) -> InterpResult < ' tcx , Option < Self > > {
139
- let handle = handle. to_machine_isize ( cx) ?;
131
+ let sign_extended_handle = handle. to_machine_isize ( cx) ?;
132
+
133
+ let handle = if let Ok ( signed_handle) = i32:: try_from ( sign_extended_handle) {
134
+ signed_handle as u32
135
+ } else {
136
+ // if a handle doesn't fit in an i32, it isn't valid.
137
+ return Ok ( None ) ;
138
+ } ;
140
139
141
140
Ok ( Self :: from_packed ( handle) )
142
141
}
0 commit comments