@@ -25,13 +25,21 @@ pub struct TlsData<'tcx> {
25
25
26
26
/// pthreads-style thread-local storage.
27
27
keys : BTreeMap < TlsKey , TlsEntry < ' tcx > > ,
28
+
29
+ /// A single global dtor (that's how things work on macOS) with a data argument.
30
+ global_dtor : Option < ( ty:: Instance < ' tcx > , Scalar < Tag > ) > ,
31
+
32
+ /// Whether we are in the "destruct" phase, during which some operations are UB.
33
+ dtors_running : bool ,
28
34
}
29
35
30
36
impl < ' tcx > Default for TlsData < ' tcx > {
31
37
fn default ( ) -> Self {
32
38
TlsData {
33
39
next_key : 1 , // start with 1 as we must not use 0 on Windows
34
40
keys : Default :: default ( ) ,
41
+ global_dtor : None ,
42
+ dtors_running : false ,
35
43
}
36
44
}
37
45
}
@@ -86,6 +94,19 @@ impl<'tcx> TlsData<'tcx> {
86
94
}
87
95
}
88
96
97
+ pub fn set_global_dtor ( & mut self , dtor : ty:: Instance < ' tcx > , data : Scalar < Tag > ) -> InterpResult < ' tcx > {
98
+ if self . dtors_running {
99
+ // UB, according to libstd docs.
100
+ throw_ub_format ! ( "setting global destructor while destructors are already running" ) ;
101
+ }
102
+ if self . global_dtor . is_some ( ) {
103
+ throw_unsup_format ! ( "setting more than one global destructor is not supported" ) ;
104
+ }
105
+
106
+ self . global_dtor = Some ( ( dtor, data) ) ;
107
+ Ok ( ( ) )
108
+ }
109
+
89
110
/// Returns a dtor, its argument and its index, if one is supposed to run
90
111
///
91
112
/// An optional destructor function may be associated with each key value.
@@ -134,11 +155,30 @@ impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tc
134
155
pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriEvalContextExt < ' mir , ' tcx > {
135
156
fn run_tls_dtors ( & mut self ) -> InterpResult < ' tcx > {
136
157
let this = self . eval_context_mut ( ) ;
158
+ assert ! ( !this. machine. tls. dtors_running, "running TLS dtors twice" ) ;
159
+ this. machine . tls . dtors_running = true ;
160
+
161
+ // The macOS global dtor runs "before any TLS slots get freed", so do that first.
162
+ if let Some ( ( instance, data) ) = this. machine . tls . global_dtor {
163
+ trace ! ( "Running global dtor {:?} on {:?}" , instance, data) ;
164
+
165
+ let ret_place = MPlaceTy :: dangling ( this. layout_of ( this. tcx . mk_unit ( ) ) ?, this) . into ( ) ;
166
+ this. call_function (
167
+ instance,
168
+ & [ data. into ( ) ] ,
169
+ Some ( ret_place) ,
170
+ StackPopCleanup :: None { cleanup : true } ,
171
+ ) ?;
172
+
173
+ // step until out of stackframes
174
+ this. run ( ) ?;
175
+ }
176
+
177
+ // Now run the "keyed" destructors.
137
178
let mut dtor = this. machine . tls . fetch_tls_dtor ( None ) ;
138
- // FIXME: replace loop by some structure that works with stepping
139
179
while let Some ( ( instance, ptr, key) ) = dtor {
140
180
trace ! ( "Running TLS dtor {:?} on {:?}" , instance, ptr) ;
141
- assert ! ( !this. is_null( ptr) . unwrap( ) , "Data can't be NULL when dtor is called!" ) ;
181
+ assert ! ( !this. is_null( ptr) . unwrap( ) , "data can't be NULL when dtor is called!" ) ;
142
182
143
183
let ret_place = MPlaceTy :: dangling ( this. layout_of ( this. tcx . mk_unit ( ) ) ?, this) . into ( ) ;
144
184
this. call_function (
0 commit comments