Auto merge of #1269 - RalfJung:windows-dtor, r=RalfJung
run Windows TLS dtor function Mostly fixes https://github.com/rust-lang/miri/issues/442
This commit is contained in:
commit
f274111113
@ -184,10 +184,9 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
|
|||||||
/// Returns `Some(return_code)` if program executed completed.
|
/// Returns `Some(return_code)` if program executed completed.
|
||||||
/// Returns `None` if an evaluation error occured.
|
/// Returns `None` if an evaluation error occured.
|
||||||
pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option<i64> {
|
pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option<i64> {
|
||||||
// FIXME: We always ignore leaks on some OSs where we do not
|
// FIXME: on Windows, locks and TLS dtor management allocate and leave that memory in `static`s.
|
||||||
// correctly implement TLS destructors.
|
// So we need https://github.com/rust-lang/miri/issues/940 to fix the leaks there.
|
||||||
let target_os = &tcx.sess.target.target.target_os;
|
let ignore_leaks = config.ignore_leaks || tcx.sess.target.target.target_os == "windows";
|
||||||
let ignore_leaks = config.ignore_leaks || target_os == "windows";
|
|
||||||
|
|
||||||
let (mut ecx, ret_place) = match create_ecx(tcx, main_id, config) {
|
let (mut ecx, ret_place) = match create_ecx(tcx, main_id, config) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
|
@ -413,7 +413,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
fn set_last_error_from_io_error(&mut self, e: std::io::Error) -> InterpResult<'tcx> {
|
fn set_last_error_from_io_error(&mut self, e: std::io::Error) -> InterpResult<'tcx> {
|
||||||
use std::io::ErrorKind::*;
|
use std::io::ErrorKind::*;
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
let target = &this.tcx.tcx.sess.target.target;
|
let target = &this.tcx.sess.target.target;
|
||||||
let last_error = if target.options.target_family == Some("unix".to_owned()) {
|
let last_error = if target.options.target_family == Some("unix".to_owned()) {
|
||||||
this.eval_libc(match e.kind() {
|
this.eval_libc(match e.kind() {
|
||||||
ConnectionRefused => "ECONNREFUSED",
|
ConnectionRefused => "ECONNREFUSED",
|
||||||
|
@ -102,6 +102,20 @@ impl MemoryExtra {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_extern_static<'tcx, 'mir>(
|
||||||
|
this: &mut MiriEvalContext<'mir, 'tcx>,
|
||||||
|
name: &str,
|
||||||
|
ptr: Scalar<Tag>,
|
||||||
|
) {
|
||||||
|
let ptr = ptr.assert_ptr();
|
||||||
|
assert_eq!(ptr.offset, Size::ZERO);
|
||||||
|
this.memory
|
||||||
|
.extra
|
||||||
|
.extern_statics
|
||||||
|
.insert(Symbol::intern(name), ptr.alloc_id)
|
||||||
|
.unwrap_none();
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets up the "extern statics" for this machine.
|
/// Sets up the "extern statics" for this machine.
|
||||||
pub fn init_extern_statics<'tcx, 'mir>(
|
pub fn init_extern_statics<'tcx, 'mir>(
|
||||||
this: &mut MiriEvalContext<'mir, 'tcx>,
|
this: &mut MiriEvalContext<'mir, 'tcx>,
|
||||||
@ -113,17 +127,17 @@ impl MemoryExtra {
|
|||||||
let layout = this.layout_of(this.tcx.types.usize)?;
|
let layout = this.layout_of(this.tcx.types.usize)?;
|
||||||
let place = this.allocate(layout, MiriMemoryKind::Machine.into());
|
let place = this.allocate(layout, MiriMemoryKind::Machine.into());
|
||||||
this.write_scalar(Scalar::from_machine_usize(0, &*this.tcx), place.into())?;
|
this.write_scalar(Scalar::from_machine_usize(0, &*this.tcx), place.into())?;
|
||||||
this.memory
|
Self::add_extern_static(this, "__cxa_thread_atexit_impl", place.ptr);
|
||||||
.extra
|
|
||||||
.extern_statics
|
|
||||||
.insert(Symbol::intern("__cxa_thread_atexit_impl"), place.ptr.assert_ptr().alloc_id)
|
|
||||||
.unwrap_none();
|
|
||||||
// "environ"
|
// "environ"
|
||||||
this.memory
|
Self::add_extern_static(this, "environ", this.machine.env_vars.environ.unwrap().ptr);
|
||||||
.extra
|
}
|
||||||
.extern_statics
|
"windows" => {
|
||||||
.insert(Symbol::intern("environ"), this.machine.env_vars.environ.unwrap().ptr.assert_ptr().alloc_id)
|
// "_tls_used"
|
||||||
.unwrap_none();
|
// This is some obscure hack that is part of the Windows TLS story. It's a `u8`.
|
||||||
|
let layout = this.layout_of(this.tcx.types.u8)?;
|
||||||
|
let place = this.allocate(layout, MiriMemoryKind::Machine.into());
|
||||||
|
this.write_scalar(Scalar::from_u8(0), place.into())?;
|
||||||
|
Self::add_extern_static(this, "_tls_used", place.ptr);
|
||||||
}
|
}
|
||||||
_ => {} // No "extern statics" supported on this target
|
_ => {} // No "extern statics" supported on this target
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align {
|
fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align {
|
||||||
let this = self.eval_context_ref();
|
let this = self.eval_context_ref();
|
||||||
// List taken from `libstd/sys_common/alloc.rs`.
|
// List taken from `libstd/sys_common/alloc.rs`.
|
||||||
let min_align = match this.tcx.tcx.sess.target.target.arch.as_str() {
|
let min_align = match this.tcx.sess.target.target.arch.as_str() {
|
||||||
"x86" | "arm" | "mips" | "powerpc" | "powerpc64" | "asmjs" | "wasm32" => 8,
|
"x86" | "arm" | "mips" | "powerpc" | "powerpc64" | "asmjs" | "wasm32" => 8,
|
||||||
"x86_64" | "aarch64" | "mips64" | "s390x" | "sparc64" => 16,
|
"x86_64" | "aarch64" | "mips64" | "s390x" | "sparc64" => 16,
|
||||||
arch => bug!("Unsupported target architecture: {}", arch),
|
arch => bug!("Unsupported target architecture: {}", arch),
|
||||||
@ -124,7 +124,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
};
|
};
|
||||||
// Strip linker suffixes (seen on 32-bit macOS).
|
// Strip linker suffixes (seen on 32-bit macOS).
|
||||||
let link_name = link_name.trim_end_matches("$UNIX2003");
|
let link_name = link_name.trim_end_matches("$UNIX2003");
|
||||||
let tcx = &{ this.tcx.tcx };
|
let tcx = this.tcx.tcx;
|
||||||
|
|
||||||
// First: functions that diverge.
|
// First: functions that diverge.
|
||||||
let (dest, ret) = match ret {
|
let (dest, ret) = match ret {
|
||||||
@ -133,8 +133,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
// The implementation is provided by the function with the `#[panic_handler]` attribute.
|
// The implementation is provided by the function with the `#[panic_handler]` attribute.
|
||||||
"panic_impl" => {
|
"panic_impl" => {
|
||||||
this.check_panic_supported()?;
|
this.check_panic_supported()?;
|
||||||
let panic_impl_id = this.tcx.lang_items().panic_impl().unwrap();
|
let panic_impl_id = tcx.lang_items().panic_impl().unwrap();
|
||||||
let panic_impl_instance = ty::Instance::mono(*this.tcx, panic_impl_id);
|
let panic_impl_instance = ty::Instance::mono(tcx, panic_impl_id);
|
||||||
return Ok(Some(&*this.load_mir(panic_impl_instance.def, None)?));
|
return Ok(Some(&*this.load_mir(panic_impl_instance.def, None)?));
|
||||||
}
|
}
|
||||||
| "exit"
|
| "exit"
|
||||||
|
@ -17,7 +17,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
ret: mir::BasicBlock,
|
ret: mir::BasicBlock,
|
||||||
) -> InterpResult<'tcx, bool> {
|
) -> InterpResult<'tcx, bool> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
let tcx = &{ this.tcx.tcx };
|
|
||||||
|
|
||||||
match link_name {
|
match link_name {
|
||||||
// Environment related shims
|
// Environment related shims
|
||||||
@ -65,7 +64,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
"write" => {
|
"write" => {
|
||||||
let fd = this.read_scalar(args[0])?.to_i32()?;
|
let fd = this.read_scalar(args[0])?.to_i32()?;
|
||||||
let buf = this.read_scalar(args[1])?.not_undef()?;
|
let buf = this.read_scalar(args[1])?.not_undef()?;
|
||||||
let n = this.read_scalar(args[2])?.to_machine_usize(tcx)?;
|
let n = this.read_scalar(args[2])?.to_machine_usize(this)?;
|
||||||
trace!("Called write({:?}, {:?}, {:?})", fd, buf, n);
|
trace!("Called write({:?}, {:?}, {:?})", fd, buf, n);
|
||||||
let result = if fd == 1 || fd == 2 {
|
let result = if fd == 1 || fd == 2 {
|
||||||
// stdout/stderr
|
// stdout/stderr
|
||||||
@ -209,7 +208,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
}
|
}
|
||||||
"pthread_getspecific" => {
|
"pthread_getspecific" => {
|
||||||
let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?;
|
let key = this.force_bits(this.read_scalar(args[0])?.not_undef()?, args[0].layout.size)?;
|
||||||
let ptr = this.machine.tls.load_tls(key, tcx)?;
|
let ptr = this.machine.tls.load_tls(key, this)?;
|
||||||
this.write_scalar(ptr, dest)?;
|
this.write_scalar(ptr, dest)?;
|
||||||
}
|
}
|
||||||
"pthread_setspecific" => {
|
"pthread_setspecific" => {
|
||||||
|
@ -13,7 +13,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
_ret: mir::BasicBlock,
|
_ret: mir::BasicBlock,
|
||||||
) -> InterpResult<'tcx, bool> {
|
) -> InterpResult<'tcx, bool> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
let tcx = &{ this.tcx.tcx };
|
|
||||||
|
|
||||||
match link_name {
|
match link_name {
|
||||||
// Windows API stubs.
|
// Windows API stubs.
|
||||||
@ -160,7 +159,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
}
|
}
|
||||||
"TlsGetValue" => {
|
"TlsGetValue" => {
|
||||||
let key = u128::from(this.read_scalar(args[0])?.to_u32()?);
|
let key = u128::from(this.read_scalar(args[0])?.to_u32()?);
|
||||||
let ptr = this.machine.tls.load_tls(key, tcx)?;
|
let ptr = this.machine.tls.load_tls(key, this)?;
|
||||||
this.write_scalar(ptr, dest)?;
|
this.write_scalar(ptr, dest)?;
|
||||||
}
|
}
|
||||||
"TlsSetValue" => {
|
"TlsSetValue" => {
|
||||||
|
@ -24,13 +24,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
if this.emulate_intrinsic(span, instance, args, ret)? {
|
if this.emulate_intrinsic(span, instance, args, ret)? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let tcx = &{ this.tcx.tcx };
|
|
||||||
let substs = instance.substs;
|
let substs = instance.substs;
|
||||||
|
|
||||||
// All these intrinsics take raw pointers, so if we access memory directly
|
// All these intrinsics take raw pointers, so if we access memory directly
|
||||||
// (as opposed to through a place), we have to remember to erase any tag
|
// (as opposed to through a place), we have to remember to erase any tag
|
||||||
// that might still hang around!
|
// that might still hang around!
|
||||||
let intrinsic_name = &*tcx.item_name(instance.def_id()).as_str();
|
let intrinsic_name = &*this.tcx.item_name(instance.def_id()).as_str();
|
||||||
|
|
||||||
// First handle intrinsics without return place.
|
// First handle intrinsics without return place.
|
||||||
let (dest, ret) = match ret {
|
let (dest, ret) = match ret {
|
||||||
|
@ -70,7 +70,7 @@ impl<'tcx> TlsData<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_tls(
|
pub fn load_tls(
|
||||||
&mut self,
|
&self,
|
||||||
key: TlsKey,
|
key: TlsKey,
|
||||||
cx: &impl HasDataLayout,
|
cx: &impl HasDataLayout,
|
||||||
) -> InterpResult<'tcx, Scalar<Tag>> {
|
) -> InterpResult<'tcx, Scalar<Tag>> {
|
||||||
@ -107,7 +107,8 @@ impl<'tcx> TlsData<'tcx> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a dtor, its argument and its index, if one is supposed to run
|
/// Returns a dtor, its argument and its index, if one is supposed to run.
|
||||||
|
/// `key` is the last dtors that was run; we return the *next* one after that.
|
||||||
///
|
///
|
||||||
/// An optional destructor function may be associated with each key value.
|
/// An optional destructor function may be associated with each key value.
|
||||||
/// At thread exit, if a key value has a non-NULL destructor pointer,
|
/// At thread exit, if a key value has a non-NULL destructor pointer,
|
||||||
@ -158,6 +159,32 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
assert!(!this.machine.tls.dtors_running, "running TLS dtors twice");
|
assert!(!this.machine.tls.dtors_running, "running TLS dtors twice");
|
||||||
this.machine.tls.dtors_running = true;
|
this.machine.tls.dtors_running = true;
|
||||||
|
|
||||||
|
if this.tcx.sess.target.target.target_os == "windows" {
|
||||||
|
// Windows has a special magic linker section that is run on certain events.
|
||||||
|
// Instead of searching for that section and supporting arbitrary hooks in there
|
||||||
|
// (that would be basically https://github.com/rust-lang/miri/issues/450),
|
||||||
|
// we specifically look up the static in libstd that we know is placed
|
||||||
|
// in that section.
|
||||||
|
let thread_callback = this.eval_path_scalar(&["std", "sys", "windows", "thread_local", "p_thread_callback"])?;
|
||||||
|
let thread_callback = this.memory.get_fn(thread_callback.not_undef()?)?.as_instance()?;
|
||||||
|
|
||||||
|
// The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`.
|
||||||
|
let reason = this.eval_path_scalar(&["std", "sys", "windows", "c", "DLL_PROCESS_DETACH"])?;
|
||||||
|
let ret_place = MPlaceTy::dangling(this.layout_of(this.tcx.mk_unit())?, this).into();
|
||||||
|
this.call_function(
|
||||||
|
thread_callback,
|
||||||
|
&[Scalar::ptr_null(this).into(), reason.into(), Scalar::ptr_null(this).into()],
|
||||||
|
Some(ret_place),
|
||||||
|
StackPopCleanup::None { cleanup: true },
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// step until out of stackframes
|
||||||
|
this.run()?;
|
||||||
|
|
||||||
|
// Windows doesn't have other destructors.
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
// The macOS global dtor runs "before any TLS slots get freed", so do that first.
|
// The macOS global dtor runs "before any TLS slots get freed", so do that first.
|
||||||
if let Some((instance, data)) = this.machine.tls.global_dtor {
|
if let Some((instance, data)) = this.machine.tls.global_dtor {
|
||||||
trace!("Running global dtor {:?} on {:?}", instance, data);
|
trace!("Running global dtor {:?} on {:?}", instance, data);
|
||||||
@ -191,12 +218,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||||||
// step until out of stackframes
|
// step until out of stackframes
|
||||||
this.run()?;
|
this.run()?;
|
||||||
|
|
||||||
|
// Fetch next dtor after `key`.
|
||||||
dtor = match this.machine.tls.fetch_tls_dtor(Some(key)) {
|
dtor = match this.machine.tls.fetch_tls_dtor(Some(key)) {
|
||||||
dtor @ Some(_) => dtor,
|
dtor @ Some(_) => dtor,
|
||||||
|
// We ran each dtor once, start over from the beginning.
|
||||||
None => this.machine.tls.fetch_tls_dtor(None),
|
None => this.machine.tls.fetch_tls_dtor(None),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// FIXME: On a windows target, call `unsafe extern "system" fn on_tls_callback`.
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user