From a2baeb516cd24c78bdedb8a6ddcbfb9d82a79b50 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 19 Jun 2017 23:32:53 +0200 Subject: [PATCH] Run the tls destructors in the correct order --- src/eval_context.rs | 34 +++++++++++++++++++++++++++++----- src/memory.rs | 18 +++++++++++------- src/step.rs | 17 ----------------- 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 55a031b310a5..69469c30e481 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -17,7 +17,7 @@ use syntax::abi::Abi; use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; -use memory::{Memory, Pointer}; +use memory::{Memory, Pointer, TlsKey}; use operator; use value::{PrimVal, PrimValKind, Value}; @@ -100,6 +100,11 @@ pub enum StackPopCleanup { /// A regular stackframe added due to a function call will need to get forwarded to the next /// block Goto(mir::BasicBlock), + /// After finishing a tls destructor, find the next one instead of starting from the beginning + /// and thus just rerunning the first one until its `data` argument is null + /// + /// The index is the current tls destructor's index + Tls(Option), /// The main function and diverging functions have nowhere to return to None, } @@ -351,6 +356,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, StackPopCleanup::Goto(target) => self.goto_block(target), StackPopCleanup::None => {}, + StackPopCleanup::Tls(key) => { + // either fetch the next dtor or start new from the beginning, if any are left with a non-null data + if let Some((instance, ptr, key)) = self.memory.fetch_tls_dtor(key).or_else(|| self.memory.fetch_tls_dtor(None)) { + trace!("Running TLS dtor {:?} on {:?}", instance, ptr); + // TODO: Potentially, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs + let mir = self.load_mir(instance.def)?; + self.push_stack_frame( + instance, + mir.span, + mir, + Lvalue::zst(), + StackPopCleanup::Tls(Some(key)), + )?; + let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?; + let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; + let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); + self.write_primval(dest, ptr, ty)?; + } + } } // deallocate all locals that are backed by an allocation for local in frame.locals { @@ -1630,8 +1654,8 @@ pub fn eval_main<'a, 'tcx: 'a>( start_instance, start_mir.span, start_mir, - Some(Lvalue::from_ptr(ret_ptr)), - StackPopCleanup::None, + Lvalue::from_ptr(ret_ptr), + StackPopCleanup::Tls(None), )?; let mut args = ecx.frame().mir.args_iter(); @@ -1657,8 +1681,8 @@ pub fn eval_main<'a, 'tcx: 'a>( main_instance, main_mir.span, main_mir, - Some(Lvalue::zst()), - StackPopCleanup::None, + Lvalue::zst(), + StackPopCleanup::Tls(None), )?; } diff --git a/src/memory.rs b/src/memory.rs index 4fcdb07fa423..ea1d8695d598 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -141,7 +141,7 @@ pub struct Memory<'a, 'tcx> { literal_alloc_cache: HashMap, AllocId>, /// pthreads-style thread-local storage. - thread_local: HashMap>, + thread_local: BTreeMap>, /// The Key to use for the next thread-local allocation. next_thread_local: TlsKey, @@ -162,7 +162,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { packed: BTreeSet::new(), static_alloc: HashSet::new(), literal_alloc_cache: HashMap::new(), - thread_local: HashMap::new(), + thread_local: BTreeMap::new(), next_thread_local: 0, } } @@ -391,7 +391,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - /// Returns a dtor and its argument, if one is supposed to run + /// Returns a dtor, its argument and its index, if one is supposed to run /// /// An optional destructor function may be associated with each key value. /// At thread exit, if a key value has a non-NULL destructor pointer, @@ -409,12 +409,16 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// with associated destructors, implementations may stop calling destructors, /// or they may continue calling destructors until no non-NULL values with /// associated destructors exist, even though this might result in an infinite loop. - - pub(crate) fn fetch_tls_dtor(&mut self) -> Option<(ty::Instance<'tcx>, PrimVal)> { - for (_, &mut TlsEntry { ref mut data, dtor }) in self.thread_local.iter_mut() { + pub(crate) fn fetch_tls_dtor(&mut self, key: Option) -> Option<(ty::Instance<'tcx>, PrimVal, TlsKey)> { + use std::collections::Bound::*; + let start = match key { + Some(key) => Excluded(key), + None => Unbounded, + }; + for (&key, &mut TlsEntry { ref mut data, dtor }) in self.thread_local.range_mut((start, Unbounded)) { if *data != PrimVal::Bytes(0) { if let Some(dtor) = dtor { - let ret = Some((dtor, *data)); + let ret = Some((dtor, *data, key)); *data = PrimVal::Bytes(0); return ret; } diff --git a/src/step.rs b/src/step.rs index 0fd3f0b3a43e..a1a9409e2e6a 100644 --- a/src/step.rs +++ b/src/step.rs @@ -32,23 +32,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.clear_packed(); self.inc_step_counter_and_check_limit(1)?; if self.stack.is_empty() { - if let Some((instance, ptr)) = self.memory.fetch_tls_dtor() { - trace!("Running TLS dtor {:?} on {:?}", instance, ptr); - // TODO: Potientially, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs - let mir = self.load_mir(instance.def)?; - self.push_stack_frame( - instance, - mir.span, - mir, - Some(Lvalue::zst()), - StackPopCleanup::None, - )?; - let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?; - let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); - self.write_primval(dest, ptr, ty)?; - return Ok(true); - } return Ok(false); }