]> git.lizzy.rs Git - rust.git/blob - src/shims/tls.rs
fix NULL in TLS dtors
[rust.git] / src / shims / tls.rs
1 use std::collections::BTreeMap;
2
3 use rustc_target::abi::LayoutOf;
4 use rustc::{ty, ty::layout::HasDataLayout, mir};
5
6 use crate::{
7     InterpResult, InterpError, StackPopCleanup,
8     MPlaceTy, Scalar, Tag,
9     HelpersEvalContextExt,
10 };
11
12 pub type TlsKey = u128;
13
14 #[derive(Copy, Clone, Debug)]
15 pub struct TlsEntry<'tcx> {
16     /// The data for this key. None is used to represent NULL.
17     /// (We normalize this early to avoid having to do a NULL-ptr-test each time we access the data.)
18     /// Will eventually become a map from thread IDs to `Scalar`s, if we ever support more than one thread.
19     pub(crate) data: Option<Scalar<Tag>>,
20     pub(crate) dtor: Option<ty::Instance<'tcx>>,
21 }
22
23 #[derive(Debug)]
24 pub struct TlsData<'tcx> {
25     /// The Key to use for the next thread-local allocation.
26     pub(crate) next_key: TlsKey,
27
28     /// pthreads-style thread-local storage.
29     pub(crate) keys: BTreeMap<TlsKey, TlsEntry<'tcx>>,
30 }
31
32 impl<'tcx> Default for TlsData<'tcx> {
33     fn default() -> Self {
34         TlsData {
35             next_key: 1, // start with 1 as we must not use 0 on Windows
36             keys: Default::default(),
37         }
38     }
39 }
40
41 impl<'tcx> TlsData<'tcx> {
42     pub fn create_tls_key(
43         &mut self,
44         dtor: Option<ty::Instance<'tcx>>,
45     ) -> TlsKey {
46         let new_key = self.next_key;
47         self.next_key += 1;
48         self.keys.insert(
49             new_key,
50             TlsEntry {
51                 data: None,
52                 dtor,
53             },
54         );
55         trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor);
56         new_key
57     }
58
59     pub fn delete_tls_key(&mut self, key: TlsKey) -> InterpResult<'tcx> {
60         match self.keys.remove(&key) {
61             Some(_) => {
62                 trace!("TLS key {} removed", key);
63                 Ok(())
64             }
65             None => err!(TlsOutOfBounds),
66         }
67     }
68
69     pub fn load_tls(
70         &mut self,
71         key: TlsKey,
72         cx: &impl HasDataLayout,
73     ) -> InterpResult<'tcx, Scalar<Tag>> {
74         match self.keys.get(&key) {
75             Some(&TlsEntry { data, .. }) => {
76                 trace!("TLS key {} loaded: {:?}", key, data);
77                 Ok(data.unwrap_or_else(|| Scalar::ptr_null(cx).into()))
78             }
79             None => err!(TlsOutOfBounds),
80         }
81     }
82
83     pub fn store_tls(&mut self, key: TlsKey, new_data: Option<Scalar<Tag>>) -> InterpResult<'tcx> {
84         match self.keys.get_mut(&key) {
85             Some(&mut TlsEntry { ref mut data, .. }) => {
86                 trace!("TLS key {} stored: {:?}", key, new_data);
87                 *data = new_data;
88                 Ok(())
89             }
90             None => err!(TlsOutOfBounds),
91         }
92     }
93
94     /// Returns a dtor, its argument and its index, if one is supposed to run
95     ///
96     /// An optional destructor function may be associated with each key value.
97     /// At thread exit, if a key value has a non-NULL destructor pointer,
98     /// and the thread has a non-NULL value associated with that key,
99     /// the value of the key is set to NULL, and then the function pointed
100     /// to is called with the previously associated value as its sole argument.
101     /// The order of destructor calls is unspecified if more than one destructor
102     /// exists for a thread when it exits.
103     ///
104     /// If, after all the destructors have been called for all non-NULL values
105     /// with associated destructors, there are still some non-NULL values with
106     /// associated destructors, then the process is repeated.
107     /// If, after at least {PTHREAD_DESTRUCTOR_ITERATIONS} iterations of destructor
108     /// calls for outstanding non-NULL values, there are still some non-NULL values
109     /// with associated destructors, implementations may stop calling destructors,
110     /// or they may continue calling destructors until no non-NULL values with
111     /// associated destructors exist, even though this might result in an infinite loop.
112     fn fetch_tls_dtor(
113         &mut self,
114         key: Option<TlsKey>,
115     ) -> Option<(ty::Instance<'tcx>, Scalar<Tag>, TlsKey)> {
116         use std::collections::Bound::*;
117
118         let thread_local = &mut self.keys;
119         let start = match key {
120             Some(key) => Excluded(key),
121             None => Unbounded,
122         };
123         for (&key, &mut TlsEntry { ref mut data, dtor }) in
124             thread_local.range_mut((start, Unbounded))
125         {
126             if let Some(data_scalar) = *data {
127                 if let Some(dtor) = dtor {
128                     let ret = Some((dtor, data_scalar, key));
129                     *data = None;
130                     return ret;
131                 }
132             }
133         }
134         None
135     }
136 }
137
138 impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
139 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
140     fn run_tls_dtors(&mut self) -> InterpResult<'tcx> {
141         let this = self.eval_context_mut();
142         let mut dtor = this.machine.tls.fetch_tls_dtor(None);
143         // FIXME: replace loop by some structure that works with stepping
144         while let Some((instance, ptr, key)) = dtor {
145             trace!("Running TLS dtor {:?} on {:?}", instance, ptr);
146             assert!(!this.is_null(ptr).unwrap(), "Data can't be NULL when dtor is called!");
147             // TODO: Potentially, this has to support all the other possible instances?
148             // See eval_fn_call in interpret/terminator/mod.rs
149             let mir = this.load_mir(instance.def)?;
150             let ret_place = MPlaceTy::dangling(this.layout_of(this.tcx.mk_unit())?, this).into();
151             this.push_stack_frame(
152                 instance,
153                 mir.span,
154                 mir,
155                 Some(ret_place),
156                 StackPopCleanup::None { cleanup: true },
157             )?;
158             let arg_local = this.frame().body.args_iter().next().ok_or_else(
159                 || InterpError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()),
160             )?;
161             let dest = this.eval_place(&mir::Place::Base(mir::PlaceBase::Local(arg_local)))?;
162             this.write_scalar(ptr, dest)?;
163
164             // step until out of stackframes
165             this.run()?;
166
167             dtor = match this.machine.tls.fetch_tls_dtor(Some(key)) {
168                 dtor @ Some(_) => dtor,
169                 None => this.machine.tls.fetch_tls_dtor(None),
170             };
171         }
172         // FIXME: On a windows target, call `unsafe extern "system" fn on_tls_callback`.
173         Ok(())
174     }
175 }