diff --git a/src/error.rs b/src/error.rs index fd692ef8b64a..25d939be3ab3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -38,6 +38,8 @@ pub enum EvalError<'tcx> { }, ExecutionTimeLimitReached, StackFrameLimitReached, + OutOfTls, + TlsOutOfBounds, AlignmentCheckFailed { required: u64, has: u64, @@ -101,6 +103,10 @@ impl<'tcx> Error for EvalError<'tcx> { "reached the configured maximum execution time", EvalError::StackFrameLimitReached => "reached the configured maximum number of stack frames", + EvalError::OutOfTls => + "reached the maximum number of representable TLS keys", + EvalError::TlsOutOfBounds => + "accessed an invalid (unallocated) TLS key", EvalError::AlignmentCheckFailed{..} => "tried to execute a misaligned read or write", EvalError::CalledClosureAsFunction => diff --git a/src/memory.rs b/src/memory.rs index f2440d3d7267..bb5a1d6f2690 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -15,6 +15,8 @@ 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) @@ -149,6 +151,12 @@ pub struct Memory<'a, 'tcx> { /// A cache for basic byte allocations keyed by their contents. This is used to deduplicate /// allocations for string and bytestring literals. 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, + + /// The Key to use for the next thread-local allocation. + next_thread_local: TlsKey, } const ZST_ALLOC_ID: AllocId = AllocId(0); @@ -167,6 +175,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { packed: BTreeSet::new(), static_alloc: HashSet::new(), literal_alloc_cache: HashMap::new(), + thread_local: HashMap::new(), + next_thread_local: 0, } } @@ -345,6 +355,45 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub(crate) fn clear_packed(&mut self) { self.packed.clear(); } + + pub(crate) fn create_tls_key(&mut self) -> 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); + return new_key; + } + + pub(crate) fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx> { + return match self.thread_local.remove(&key) { + Some(_) => { + trace!("TLS key {} removed", key); + Ok(()) + }, + None => Err(EvalError::TlsOutOfBounds) + } + } + + 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) + }, + None => Err(EvalError::TlsOutOfBounds) + } + } + + pub(crate) fn store_tls(&mut self, key: TlsKey, new_ptr: Pointer) -> EvalResult<'tcx> { + return match self.thread_local.get_mut(&key) { + Some(ptr) => { + trace!("TLS key {} stored: {:?}", key, new_ptr); + *ptr = new_ptr; + Ok(()) + }, + None => Err(EvalError::TlsOutOfBounds) + } + } } // The derived `Ord` impl sorts first by the first field, then, if the fields are the same diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 4834bba5b484..94a221b32c77 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -1,6 +1,6 @@ use rustc::hir::def_id::DefId; use rustc::mir; -use rustc::ty::{self, Ty}; +use rustc::ty::{self, TypeVariants, Ty, TyS, TypeAndMut}; use rustc::ty::layout::Layout; use syntax::codemap::Span; use syntax::attr; @@ -9,7 +9,7 @@ use syntax::abi::Abi; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; use lvalue::Lvalue; -use memory::Pointer; +use memory::{Pointer, TlsKey}; use value::PrimVal; use value::Value; use rustc_data_structures::indexed_vec::Idx; @@ -367,18 +367,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Only trait methods can have a Self parameter. - // Intercept some methods (even if we can find MIR for them) - if let ty::InstanceDef::Item(def_id) = instance.def { - match &self.tcx.item_path_str(def_id)[..] { - "std::sys::imp::fast_thread_local::register_dtor" => { - // Just don't execute this one, we don't handle all this thread-local stuff anyway. - self.goto_block(destination.unwrap().1); - return Ok(true) - } - _ => {} - } - } - let mir = match self.load_mir(instance.def) { Ok(mir) => mir, Err(EvalError::NoMirFor(path)) => { @@ -480,7 +468,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn call_c_abi( &mut self, def_id: DefId, - args: &[mir::Operand<'tcx>], + arg_operands: &[mir::Operand<'tcx>], dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { @@ -490,7 +478,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { .unwrap_or(name) .as_str(); - let args_res: EvalResult> = args.iter() + let args_res: EvalResult> = arg_operands.iter() .map(|arg| self.eval_operand(arg)) .collect(); let args = args_res?; @@ -555,9 +543,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { let new_ptr = ptr.offset(num - idx as u64 - 1); - self.write_value(Value::ByVal(PrimVal::Ptr(new_ptr)), dest, dest_ty)?; + self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } else { - self.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } } @@ -567,9 +555,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { let new_ptr = ptr.offset(idx as u64); - self.write_value(Value::ByVal(PrimVal::Ptr(new_ptr)), dest, dest_ty)?; + self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } else { - self.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } } @@ -579,7 +567,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let name = self.memory.read_c_str(name_ptr)?; info!("ignored env var request for `{:?}`", ::std::str::from_utf8(name)); } - self.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, dest_ty)?; + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } "write" => { @@ -605,9 +593,54 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; } + // 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)?; + + // 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())) + }; + + // Write the key into the memory where key_ptr wants it + if key >= (1 << key_size.bits()) { + return Err(EvalError::OutOfTls); + } + self.memory.write_int(key_ptr, key as i128, key_size.bytes())?; + + // Return success (0) + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + } + "pthread_key_delete" => { + // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t + let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; + self.memory.delete_tls_key(key)?; + // Return success (0) + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + } + "pthread_getspecific" => { + // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t + let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; + let ptr = self.memory.load_tls(key)?; + self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; + } + "pthread_setspecific" => { + // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t + let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; + let new_ptr = args[1].read_ptr(&self.memory)?; + self.memory.store_tls(key, new_ptr)?; + + // Return success (0) + self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + } + link_name if link_name.starts_with("pthread_") => { warn!("ignoring C ABI call: {}", link_name); - return Ok(()); }, _ => {