Run the tls destructors in the correct order
This commit is contained in:
parent
43afa20dc7
commit
a2baeb516c
3 changed files with 40 additions and 29 deletions
|
|
@ -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),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
17
src/step.rs
17
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue