Auto merge of #1267 - RalfJung:macos-dtors, r=RalfJung
implement TLS cleanup for macOS Now that I can run macOS interpretation locally, this was not that hard to fix. ;) Fixes https://github.com/rust-lang/miri/issues/443
This commit is contained in:
commit
f4308a0728
@ -186,8 +186,8 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
|
||||
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
|
||||
// correctly implement TLS destructors.
|
||||
let target_os = tcx.sess.target.target.target_os.as_str();
|
||||
let ignore_leaks = config.ignore_leaks || target_os == "windows" || target_os == "macos";
|
||||
let target_os = &tcx.sess.target.target.target_os;
|
||||
let ignore_leaks = config.ignore_leaks || target_os == "windows";
|
||||
|
||||
let (mut ecx, ret_place) = match create_ecx(tcx, main_id, config) {
|
||||
Ok(v) => v,
|
||||
|
@ -87,7 +87,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
|
||||
"_tlv_atexit" => {
|
||||
// FIXME: register the destructor.
|
||||
let dtor = this.read_scalar(args[0])?.not_undef()?;
|
||||
let dtor = this.memory.get_fn(dtor)?.as_instance()?;
|
||||
let data = this.read_scalar(args[1])?.not_undef()?;
|
||||
this.machine.tls.set_global_dtor(dtor, data)?;
|
||||
}
|
||||
|
||||
"_NSGetArgc" => {
|
||||
|
@ -25,6 +25,12 @@ pub struct TlsData<'tcx> {
|
||||
|
||||
/// pthreads-style thread-local storage.
|
||||
keys: BTreeMap<TlsKey, TlsEntry<'tcx>>,
|
||||
|
||||
/// A single global dtor (that's how things work on macOS) with a data argument.
|
||||
global_dtor: Option<(ty::Instance<'tcx>, Scalar<Tag>)>,
|
||||
|
||||
/// Whether we are in the "destruct" phase, during which some operations are UB.
|
||||
dtors_running: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> Default for TlsData<'tcx> {
|
||||
@ -32,6 +38,8 @@ impl<'tcx> Default for TlsData<'tcx> {
|
||||
TlsData {
|
||||
next_key: 1, // start with 1 as we must not use 0 on Windows
|
||||
keys: Default::default(),
|
||||
global_dtor: None,
|
||||
dtors_running: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -86,6 +94,19 @@ impl<'tcx> TlsData<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_global_dtor(&mut self, dtor: ty::Instance<'tcx>, data: Scalar<Tag>) -> InterpResult<'tcx> {
|
||||
if self.dtors_running {
|
||||
// UB, according to libstd docs.
|
||||
throw_ub_format!("setting global destructor while destructors are already running");
|
||||
}
|
||||
if self.global_dtor.is_some() {
|
||||
throw_unsup_format!("setting more than one global destructor is not supported");
|
||||
}
|
||||
|
||||
self.global_dtor = Some((dtor, data));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@ -134,11 +155,30 @@ impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tc
|
||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
|
||||
fn run_tls_dtors(&mut self) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
assert!(!this.machine.tls.dtors_running, "running TLS dtors twice");
|
||||
this.machine.tls.dtors_running = true;
|
||||
|
||||
// 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 {
|
||||
trace!("Running global dtor {:?} on {:?}", instance, data);
|
||||
|
||||
let ret_place = MPlaceTy::dangling(this.layout_of(this.tcx.mk_unit())?, this).into();
|
||||
this.call_function(
|
||||
instance,
|
||||
&[data.into()],
|
||||
Some(ret_place),
|
||||
StackPopCleanup::None { cleanup: true },
|
||||
)?;
|
||||
|
||||
// step until out of stackframes
|
||||
this.run()?;
|
||||
}
|
||||
|
||||
// Now run the "keyed" destructors.
|
||||
let mut dtor = this.machine.tls.fetch_tls_dtor(None);
|
||||
// FIXME: replace loop by some structure that works with stepping
|
||||
while let Some((instance, ptr, key)) = dtor {
|
||||
trace!("Running TLS dtor {:?} on {:?}", instance, ptr);
|
||||
assert!(!this.is_null(ptr).unwrap(), "Data can't be NULL when dtor is called!");
|
||||
assert!(!this.is_null(ptr).unwrap(), "data can't be NULL when dtor is called!");
|
||||
|
||||
let ret_place = MPlaceTy::dangling(this.layout_of(this.tcx.mk_unit())?, this).into();
|
||||
this.call_function(
|
||||
|
@ -1,5 +1,4 @@
|
||||
// ignore-windows: We do not check leaks on Windows
|
||||
// ignore-macos: We do not check leaks on macOS
|
||||
|
||||
//error-pattern: the evaluated program leaked memory
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
// ignore-windows: We do not check leaks on Windows
|
||||
// ignore-macos: We do not check leaks on macOS
|
||||
|
||||
//error-pattern: the evaluated program leaked memory
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user