Skip to content

Commit f4308a0

Browse files
committed
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 #443
2 parents 01bc08a + c5ef8a6 commit f4308a0

File tree

5 files changed

+48
-7
lines changed

5 files changed

+48
-7
lines changed

src/eval.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,8 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
186186
pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option<i64> {
187187
// FIXME: We always ignore leaks on some OSs where we do not
188188
// correctly implement TLS destructors.
189-
let target_os = tcx.sess.target.target.target_os.as_str();
190-
let ignore_leaks = config.ignore_leaks || target_os == "windows" || target_os == "macos";
189+
let target_os = &tcx.sess.target.target.target_os;
190+
let ignore_leaks = config.ignore_leaks || target_os == "windows";
191191

192192
let (mut ecx, ret_place) = match create_ecx(tcx, main_id, config) {
193193
Ok(v) => v,

src/shims/foreign_items/posix/macos.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
8787
}
8888

8989
"_tlv_atexit" => {
90-
// FIXME: register the destructor.
90+
let dtor = this.read_scalar(args[0])?.not_undef()?;
91+
let dtor = this.memory.get_fn(dtor)?.as_instance()?;
92+
let data = this.read_scalar(args[1])?.not_undef()?;
93+
this.machine.tls.set_global_dtor(dtor, data)?;
9194
}
9295

9396
"_NSGetArgc" => {

src/shims/tls.rs

+42-2
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,21 @@ pub struct TlsData<'tcx> {
2525

2626
/// pthreads-style thread-local storage.
2727
keys: BTreeMap<TlsKey, TlsEntry<'tcx>>,
28+
29+
/// A single global dtor (that's how things work on macOS) with a data argument.
30+
global_dtor: Option<(ty::Instance<'tcx>, Scalar<Tag>)>,
31+
32+
/// Whether we are in the "destruct" phase, during which some operations are UB.
33+
dtors_running: bool,
2834
}
2935

3036
impl<'tcx> Default for TlsData<'tcx> {
3137
fn default() -> Self {
3238
TlsData {
3339
next_key: 1, // start with 1 as we must not use 0 on Windows
3440
keys: Default::default(),
41+
global_dtor: None,
42+
dtors_running: false,
3543
}
3644
}
3745
}
@@ -86,6 +94,19 @@ impl<'tcx> TlsData<'tcx> {
8694
}
8795
}
8896

97+
pub fn set_global_dtor(&mut self, dtor: ty::Instance<'tcx>, data: Scalar<Tag>) -> InterpResult<'tcx> {
98+
if self.dtors_running {
99+
// UB, according to libstd docs.
100+
throw_ub_format!("setting global destructor while destructors are already running");
101+
}
102+
if self.global_dtor.is_some() {
103+
throw_unsup_format!("setting more than one global destructor is not supported");
104+
}
105+
106+
self.global_dtor = Some((dtor, data));
107+
Ok(())
108+
}
109+
89110
/// Returns a dtor, its argument and its index, if one is supposed to run
90111
///
91112
/// 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
134155
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
135156
fn run_tls_dtors(&mut self) -> InterpResult<'tcx> {
136157
let this = self.eval_context_mut();
158+
assert!(!this.machine.tls.dtors_running, "running TLS dtors twice");
159+
this.machine.tls.dtors_running = true;
160+
161+
// The macOS global dtor runs "before any TLS slots get freed", so do that first.
162+
if let Some((instance, data)) = this.machine.tls.global_dtor {
163+
trace!("Running global dtor {:?} on {:?}", instance, data);
164+
165+
let ret_place = MPlaceTy::dangling(this.layout_of(this.tcx.mk_unit())?, this).into();
166+
this.call_function(
167+
instance,
168+
&[data.into()],
169+
Some(ret_place),
170+
StackPopCleanup::None { cleanup: true },
171+
)?;
172+
173+
// step until out of stackframes
174+
this.run()?;
175+
}
176+
177+
// Now run the "keyed" destructors.
137178
let mut dtor = this.machine.tls.fetch_tls_dtor(None);
138-
// FIXME: replace loop by some structure that works with stepping
139179
while let Some((instance, ptr, key)) = dtor {
140180
trace!("Running TLS dtor {:?} on {:?}", instance, ptr);
141-
assert!(!this.is_null(ptr).unwrap(), "Data can't be NULL when dtor is called!");
181+
assert!(!this.is_null(ptr).unwrap(), "data can't be NULL when dtor is called!");
142182

143183
let ret_place = MPlaceTy::dangling(this.layout_of(this.tcx.mk_unit())?, this).into();
144184
this.call_function(

tests/compile-fail/memleak.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// ignore-windows: We do not check leaks on Windows
2-
// ignore-macos: We do not check leaks on macOS
32

43
//error-pattern: the evaluated program leaked memory
54

tests/compile-fail/memleak_rc.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// ignore-windows: We do not check leaks on Windows
2-
// ignore-macos: We do not check leaks on macOS
32

43
//error-pattern: the evaluated program leaked memory
54

0 commit comments

Comments
 (0)