diff --git a/src/error.rs b/src/error.rs index 25d939be3ab3..7b6542bff934 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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 => diff --git a/src/memory.rs b/src/memory.rs index bb5a1d6f2690..0280ad4c379b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -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>, } //////////////////////////////////////////////////////////////////////////////// @@ -153,7 +164,7 @@ pub struct Memory<'a, 'tcx> { literal_alloc_cache: HashMap, 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, + thread_local: HashMap>, /// 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>) -> 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 diff --git a/src/step.rs b/src/step.rs index 110a7b2252a8..658a3445c433 100644 --- a/src/step.rs +++ b/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) } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 94a221b32c77..aaf03ea665f6 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -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::>()); @@ -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); }