Skip to content

Commit

Permalink
Auto merge of #1267 - RalfJung:macos-dtors, r=RalfJung
Browse files Browse the repository at this point in the history
implement TLS cleanup for macOS

Now that I can run macOS interpretation locally, this was not that hard to fix. ;)
Fixes #443
  • Loading branch information
bors committed Mar 28, 2020
2 parents 01bc08a + c5ef8a6 commit f4308a0
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 7 deletions.
4 changes: 2 additions & 2 deletions src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 4 additions & 1 deletion src/shims/foreign_items/posix/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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" => {
Expand Down
44 changes: 42 additions & 2 deletions src/shims/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,21 @@ 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> {
fn default() -> Self {
TlsData {
next_key: 1, // start with 1 as we must not use 0 on Windows
keys: Default::default(),
global_dtor: None,
dtors_running: false,
}
}
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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(
Expand Down
1 change: 0 additions & 1 deletion tests/compile-fail/memleak.rs
Original file line number Diff line number Diff line change
@@ -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

Expand Down
1 change: 0 additions & 1 deletion tests/compile-fail/memleak_rc.rs
Original file line number Diff line number Diff line change
@@ -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

Expand Down

0 comments on commit f4308a0

Please sign in to comment.