Run the tls destructors in the correct order

This commit is contained in:
Oliver Schneider 2017-06-19 23:32:53 +02:00 committed by Oliver Schneider
parent 43afa20dc7
commit a2baeb516c
No known key found for this signature in database
GPG key ID: A69F8D225B3AD7D9
3 changed files with 40 additions and 29 deletions

View file

@ -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<TlsKey>),
/// 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),
)?;
}

View file

@ -141,7 +141,7 @@ pub struct Memory<'a, 'tcx> {
literal_alloc_cache: HashMap<Vec<u8>, AllocId>,
/// pthreads-style thread-local storage.
thread_local: HashMap<TlsKey, TlsEntry<'tcx>>,
thread_local: BTreeMap<TlsKey, TlsEntry<'tcx>>,
/// 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<TlsKey>) -> 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;
}

View file

@ -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);
}