@@ -25,13 +25,21 @@ pub struct TlsData<'tcx> {
2525
2626 /// pthreads-style thread-local storage.
2727 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 ,
2834}
2935
3036impl < ' tcx > Default for TlsData < ' tcx > {
3137 fn default ( ) -> Self {
3238 TlsData {
3339 next_key : 1 , // start with 1 as we must not use 0 on Windows
3440 keys : Default :: default ( ) ,
41+ global_dtor : None ,
42+ dtors_running : false ,
3543 }
3644 }
3745}
@@ -86,6 +94,19 @@ impl<'tcx> TlsData<'tcx> {
8694 }
8795 }
8896
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+
89110 /// Returns a dtor, its argument and its index, if one is supposed to run
90111 ///
91112 /// 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
134155pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriEvalContextExt < ' mir , ' tcx > {
135156 fn run_tls_dtors ( & mut self ) -> InterpResult < ' tcx > {
136157 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.
137178 let mut dtor = this. machine . tls . fetch_tls_dtor ( None ) ;
138- // FIXME: replace loop by some structure that works with stepping
139179 while let Some ( ( instance, ptr, key) ) = dtor {
140180 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!" ) ;
142182
143183 let ret_place = MPlaceTy :: dangling ( this. layout_of ( this. tcx . mk_unit ( ) ) ?, this) . into ( ) ;
144184 this. call_function (
0 commit comments