support TLS destructors
This commit is contained in:
parent
238211e1b3
commit
a66f359d91
4 changed files with 84 additions and 22 deletions
|
|
@ -40,6 +40,7 @@ pub enum EvalError<'tcx> {
|
|||
StackFrameLimitReached,
|
||||
OutOfTls,
|
||||
TlsOutOfBounds,
|
||||
AbiViolation(String),
|
||||
AlignmentCheckFailed {
|
||||
required: u64,
|
||||
has: u64,
|
||||
|
|
@ -107,6 +108,7 @@ impl<'tcx> Error for EvalError<'tcx> {
|
|||
"reached the maximum number of representable TLS keys",
|
||||
EvalError::TlsOutOfBounds =>
|
||||
"accessed an invalid (unallocated) TLS key",
|
||||
EvalError::AbiViolation(ref msg) => msg,
|
||||
EvalError::AlignmentCheckFailed{..} =>
|
||||
"tried to execute a misaligned read or write",
|
||||
EvalError::CalledClosureAsFunction =>
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ use value::PrimVal;
|
|||
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct AllocId(pub u64);
|
||||
|
||||
pub type TlsKey = usize;
|
||||
|
||||
impl fmt::Display for AllocId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
|
|
@ -100,6 +98,19 @@ impl Pointer {
|
|||
pub fn never_ptr() -> Self {
|
||||
Pointer::new(NEVER_ALLOC_ID, 0)
|
||||
}
|
||||
|
||||
pub fn is_null_ptr(&self) -> bool {
|
||||
// FIXME: Is this the right way?
|
||||
return *self == Pointer::from_int(0)
|
||||
}
|
||||
}
|
||||
|
||||
pub type TlsKey = usize;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct TlsEntry<'tcx> {
|
||||
data: Pointer, // will eventually become a map from thread IDs to pointers
|
||||
dtor: Option<ty::Instance<'tcx>>,
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -153,7 +164,7 @@ pub struct Memory<'a, 'tcx> {
|
|||
literal_alloc_cache: HashMap<Vec<u8>, AllocId>,
|
||||
|
||||
/// pthreads-style Thread-local storage. We only have one thread, so this is just a map from TLS keys (indices into the vector) to the pointer stored there.
|
||||
thread_local: HashMap<TlsKey, Pointer>,
|
||||
thread_local: HashMap<TlsKey, TlsEntry<'tcx>>,
|
||||
|
||||
/// The Key to use for the next thread-local allocation.
|
||||
next_thread_local: TlsKey,
|
||||
|
|
@ -356,11 +367,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
self.packed.clear();
|
||||
}
|
||||
|
||||
pub(crate) fn create_tls_key(&mut self) -> TlsKey {
|
||||
pub(crate) fn create_tls_key(&mut self, dtor: Option<ty::Instance<'tcx>>) -> TlsKey {
|
||||
let new_key = self.next_thread_local;
|
||||
self.next_thread_local += 1;
|
||||
self.thread_local.insert(new_key, Pointer::from_int(0));
|
||||
trace!("New TLS key allocated: {}", new_key);
|
||||
self.thread_local.insert(new_key, TlsEntry { data: Pointer::from_int(0), dtor });
|
||||
trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor);
|
||||
return new_key;
|
||||
}
|
||||
|
||||
|
|
@ -376,24 +387,38 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
|
||||
pub(crate) fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Pointer> {
|
||||
return match self.thread_local.get(&key) {
|
||||
Some(&ptr) => {
|
||||
trace!("TLS key {} loaded: {:?}", key, ptr);
|
||||
Ok(ptr)
|
||||
Some(&TlsEntry { data, .. }) => {
|
||||
trace!("TLS key {} loaded: {:?}", key, data);
|
||||
Ok(data)
|
||||
},
|
||||
None => Err(EvalError::TlsOutOfBounds)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn store_tls(&mut self, key: TlsKey, new_ptr: Pointer) -> EvalResult<'tcx> {
|
||||
pub(crate) fn store_tls(&mut self, key: TlsKey, new_data: Pointer) -> EvalResult<'tcx> {
|
||||
return match self.thread_local.get_mut(&key) {
|
||||
Some(ptr) => {
|
||||
trace!("TLS key {} stored: {:?}", key, new_ptr);
|
||||
*ptr = new_ptr;
|
||||
Some(&mut TlsEntry { ref mut data, .. }) => {
|
||||
trace!("TLS key {} stored: {:?}", key, new_data);
|
||||
*data = new_data;
|
||||
Ok(())
|
||||
},
|
||||
None => Err(EvalError::TlsOutOfBounds)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a dtor and its argument, if one is supposed to run
|
||||
pub(crate) fn fetch_tls_dtor(&mut self) -> Option<(ty::Instance<'tcx>, Pointer)> {
|
||||
for (_, &mut TlsEntry { ref mut data, dtor }) in self.thread_local.iter_mut() {
|
||||
if !data.is_null_ptr() {
|
||||
if let Some(dtor) = dtor {
|
||||
let old_data = *data;
|
||||
*data = Pointer::from_int(0);
|
||||
return Some((dtor, old_data));
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// The derived `Ord` impl sorts first by the first field, then, if the fields are the same
|
||||
|
|
|
|||
32
src/step.rs
32
src/step.rs
|
|
@ -14,7 +14,7 @@ use eval_context::{EvalContext, StackPopCleanup};
|
|||
use lvalue::{Global, GlobalId, Lvalue};
|
||||
use value::{Value, PrimVal};
|
||||
use memory::Pointer;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::codemap::{Span, DUMMY_SP};
|
||||
|
||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> {
|
||||
|
|
@ -32,6 +32,28 @@ 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: Potientiually, this has to support all the other possible instances? See eval_fn_call in terminator/mod.rs
|
||||
let mir = self.load_mir(instance.def)?;
|
||||
// FIXME: Are these the right dummy values?
|
||||
self.push_stack_frame(
|
||||
instance,
|
||||
DUMMY_SP,
|
||||
mir,
|
||||
Lvalue::from_ptr(Pointer::zst_ptr()),
|
||||
StackPopCleanup::None,
|
||||
)?;
|
||||
if let Some(arg_local) = self.frame().mir.args_iter().next() {
|
||||
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
|
||||
let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
|
||||
self.write_value(Value::ByVal(PrimVal::Ptr(ptr)), dest, ty)?;
|
||||
} else {
|
||||
return Err(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()));
|
||||
}
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
|
|
@ -49,11 +71,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
mir,
|
||||
new_constants: &mut new,
|
||||
}.visit_statement(block, stmt, mir::Location { block, statement_index: stmt_id });
|
||||
// if ConstantExtractor added new frames, we don't execute anything here
|
||||
// but await the next call to step
|
||||
if new? == 0 {
|
||||
self.statement(stmt)?;
|
||||
}
|
||||
// if ConstantExtractor added new frames, we don't execute anything here
|
||||
// but await the next call to step
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
|
|
@ -66,11 +88,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
mir,
|
||||
new_constants: &mut new,
|
||||
}.visit_terminator(block, terminator, mir::Location { block, statement_index: stmt_id });
|
||||
// if ConstantExtractor added new frames, we don't execute anything here
|
||||
// but await the next call to step
|
||||
if new? == 0 {
|
||||
self.terminator(terminator)?;
|
||||
}
|
||||
// if ConstantExtractor added new frames, we don't execute anything here
|
||||
// but await the next call to step
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
args.push((arg_val, arg_ty));
|
||||
}
|
||||
|
||||
// Push the stack frame, and potentially be entirely done if the call got hooked
|
||||
if self.eval_fn_call_inner(
|
||||
instance,
|
||||
destination,
|
||||
|
|
@ -229,6 +230,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
// Pass the arguments
|
||||
let mut arg_locals = self.frame().mir.args_iter();
|
||||
trace!("ABI: {:?}", sig.abi);
|
||||
trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::<Vec<_>>());
|
||||
|
|
@ -595,19 +597,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
// Hook pthread calls that go to the thread-local storage memory subsystem
|
||||
"pthread_key_create" => {
|
||||
let key = self.memory.create_tls_key();
|
||||
let key_ptr = args[0].read_ptr(&self.memory)?;
|
||||
|
||||
|
||||
// Extract the function type out of the signature (that seems easier than constructing it ourselves...)
|
||||
// FIXME: Or should we instead construct the type we expect it to have?
|
||||
let dtor_fn_ty = match self.operand_ty(&arg_operands[1]) {
|
||||
&TyS { sty: TypeVariants::TyAdt(_, ref substs), .. } => {
|
||||
substs.type_at(0)
|
||||
}
|
||||
_ => return Err(EvalError::AbiViolation("Wrong signature used for pthread_key_create: Second argument must be option of a function pointer.".to_owned()))
|
||||
};
|
||||
let dtor_ptr = self.value_to_primval(args[1], dtor_fn_ty)?.to_ptr()?;
|
||||
let dtor = if dtor_ptr.is_null_ptr() { None } else { Some(self.memory.get_fn(dtor_ptr.alloc_id)?) };
|
||||
|
||||
// Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t.
|
||||
let key_size = match self.operand_ty(&arg_operands[0]) {
|
||||
&TyS { sty: TypeVariants::TyRawPtr(TypeAndMut { ty, .. }), .. } => {
|
||||
let layout = self.type_layout(ty)?;
|
||||
layout.size(&self.tcx.data_layout)
|
||||
}
|
||||
_ => return Err(EvalError::Unimplemented("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned()))
|
||||
_ => return Err(EvalError::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned()))
|
||||
};
|
||||
|
||||
// Write the key into the memory where key_ptr wants it
|
||||
// Create key and write it into the memory where key_ptr wants it
|
||||
let key = self.memory.create_tls_key(dtor);
|
||||
if key >= (1 << key_size.bits()) {
|
||||
return Err(EvalError::OutOfTls);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue