Skip to content

Commit 79cd5a8

Browse files
committed
Auto merge of #1489 - RalfJung:tls-alloc-ids, r=oli-obk
Adjust for new rustc alloc ID handling and deallocate thread-local statics Miri side of rust-lang/rust#74775. Fixes #1369 Fixes #1488
2 parents 345b033 + cae90b6 commit 79cd5a8

14 files changed

+131
-139
lines changed

rust-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
13f9aa190957b993a268fd4a046fce76ca8814ee
1+
efc02b03d18b0cbaa55b1e421d792f70a39230b2

src/diagnostics.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ pub fn report_error<'tcx, 'mir>(
9494
let helps = match e.kind {
9595
Unsupported(UnsupportedOpInfo::NoMirFor(..)) =>
9696
vec![format!("make sure to use a Miri sysroot, which you can prepare with `cargo miri setup`")],
97-
Unsupported(UnsupportedOpInfo::ReadBytesAsPointer) =>
98-
panic!("`ReadBytesAsPointer` cannot be raised by Miri"),
97+
Unsupported(UnsupportedOpInfo::ReadBytesAsPointer | UnsupportedOpInfo::ThreadLocalStatic(_) | UnsupportedOpInfo::ReadExternStatic(_)) =>
98+
panic!("Error should never be raised by Miri: {:?}", e.kind),
9999
Unsupported(_) =>
100100
vec![format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support")],
101101
UndefinedBehavior(UndefinedBehaviorInfo::AlignmentCheckFailed { .. }) =>
@@ -162,7 +162,15 @@ fn report_msg<'tcx>(
162162
} else {
163163
tcx.sess.diagnostic().span_note_diag(span, title)
164164
};
165-
err.span_label(span, span_msg);
165+
// Show main message.
166+
if span != DUMMY_SP {
167+
err.span_label(span, span_msg);
168+
} else {
169+
// Make sure we show the message even when it is a dummy span.
170+
err.note(&span_msg);
171+
err.note("(no span available)");
172+
}
173+
// Show help messages.
166174
if !helps.is_empty() {
167175
// Add visual separator before backtrace.
168176
helps.last_mut().unwrap().push_str("\n");
@@ -198,7 +206,7 @@ pub fn register_diagnostic(e: NonHaltingDiagnostic) {
198206
/// after a step was taken.
199207
pub struct TopFrameInfo<'tcx> {
200208
stack_size: usize,
201-
instance: ty::Instance<'tcx>,
209+
instance: Option<ty::Instance<'tcx>>,
202210
span: Span,
203211
}
204212

@@ -209,11 +217,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
209217
DIAGNOSTICS.with(|diagnostics| assert!(diagnostics.borrow().is_empty()));
210218

211219
let this = self.eval_context_ref();
220+
if this.active_thread_stack().is_empty() {
221+
// Diagnostics can happen even with the empty stack (e.g. deallocation of thread-local statics).
222+
return TopFrameInfo { stack_size: 0, instance: None, span: DUMMY_SP };
223+
}
212224
let frame = this.frame();
213225

214226
TopFrameInfo {
215227
stack_size: this.active_thread_stack().len(),
216-
instance: frame.instance,
228+
instance: Some(frame.instance),
217229
span: frame.current_source_info().map_or(DUMMY_SP, |si| si.span),
218230
}
219231
}
@@ -237,15 +249,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
237249
if stacktrace.len() < info.stack_size {
238250
assert!(stacktrace.len() == info.stack_size-1, "we should never pop more than one frame at once");
239251
let frame_info = FrameInfo {
240-
instance: info.instance,
252+
instance: info.instance.unwrap(),
241253
span: info.span,
242254
lint_root: None,
243255
};
244256
stacktrace.insert(0, frame_info);
245-
} else {
257+
} else if let Some(instance) = info.instance {
246258
// Adjust topmost frame.
247259
stacktrace[0].span = info.span;
248-
assert_eq!(stacktrace[0].instance, info.instance, "we should not pop and push a frame in one step");
260+
assert_eq!(stacktrace[0].instance, instance, "we should not pop and push a frame in one step");
249261
}
250262

251263
// Show diagnostics.

src/eval.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,10 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
211211
let res: InterpResult<'_, i64> = (|| {
212212
// Main loop.
213213
loop {
214+
let info = ecx.preprocess_diagnostics();
214215
match ecx.schedule()? {
215216
SchedulingAction::ExecuteStep => {
216-
let info = ecx.preprocess_diagnostics();
217217
assert!(ecx.step()?, "a terminated thread was scheduled for execution");
218-
ecx.process_diagnostics(info);
219218
}
220219
SchedulingAction::ExecuteTimeoutCallback => {
221220
assert!(ecx.machine.communicate,
@@ -233,6 +232,7 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
233232
break;
234233
}
235234
}
235+
ecx.process_diagnostics(info);
236236
}
237237
let return_code = ecx.read_scalar(ret_place.into())?.check_init()?.to_machine_isize(&ecx)?;
238238
Ok(return_code)

src/intptrcast.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use log::trace;
66
use rand::Rng;
77

88
use rustc_data_structures::fx::FxHashMap;
9-
use rustc_mir::interpret::{AllocCheck, AllocId, InterpResult, Memory, Machine, Pointer, PointerArithmetic};
109
use rustc_target::abi::{Size, HasDataLayout};
1110

1211
use crate::*;
@@ -79,7 +78,7 @@ impl<'mir, 'tcx> GlobalState {
7978
) -> InterpResult<'tcx, u64> {
8079
let mut global_state = memory.extra.intptrcast.borrow_mut();
8180
let global_state = &mut *global_state;
82-
let id = Evaluator::canonical_alloc_id(memory, ptr.alloc_id);
81+
let id = ptr.alloc_id;
8382

8483
// There is nothing wrong with a raw pointer being cast to an integer only after
8584
// it became dangling. Hence `MaybeDead`.

src/machine.rs

+20-34
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ pub enum MiriMemoryKind {
6464
Global,
6565
/// Memory for extern statics.
6666
/// This memory may leak.
67-
ExternGlobal,
67+
ExternStatic,
68+
/// Memory for thread-local statics.
69+
/// This memory may leak.
70+
Tls,
6871
}
6972

7073
impl Into<MemoryKind<MiriMemoryKind>> for MiriMemoryKind {
@@ -80,7 +83,7 @@ impl MayLeak for MiriMemoryKind {
8083
use self::MiriMemoryKind::*;
8184
match self {
8285
Rust | C | WinHeap | Env => false,
83-
Machine | Global | ExternGlobal => true,
86+
Machine | Global | ExternStatic | Tls => true,
8487
}
8588
}
8689
}
@@ -94,8 +97,9 @@ impl fmt::Display for MiriMemoryKind {
9497
WinHeap => write!(f, "Windows heap"),
9598
Machine => write!(f, "machine-managed memory"),
9699
Env => write!(f, "environment variable"),
97-
Global => write!(f, "global"),
98-
ExternGlobal => write!(f, "extern global"),
100+
Global => write!(f, "global (static or const)"),
101+
ExternStatic => write!(f, "extern static"),
102+
Tls => write!(f, "thread-local static"),
99103
}
100104
}
101105
}
@@ -175,7 +179,7 @@ impl MemoryExtra {
175179
// "__cxa_thread_atexit_impl"
176180
// This should be all-zero, pointer-sized.
177181
let layout = this.machine.layouts.usize;
178-
let place = this.allocate(layout, MiriMemoryKind::ExternGlobal.into());
182+
let place = this.allocate(layout, MiriMemoryKind::ExternStatic.into());
179183
this.write_scalar(Scalar::from_machine_usize(0, this), place.into())?;
180184
Self::add_extern_static(this, "__cxa_thread_atexit_impl", place.ptr);
181185
// "environ"
@@ -185,7 +189,7 @@ impl MemoryExtra {
185189
// "_tls_used"
186190
// This is some obscure hack that is part of the Windows TLS story. It's a `u8`.
187191
let layout = this.machine.layouts.u8;
188-
let place = this.allocate(layout, MiriMemoryKind::ExternGlobal.into());
192+
let place = this.allocate(layout, MiriMemoryKind::ExternStatic.into());
189193
this.write_scalar(Scalar::from_u8(0), place.into())?;
190194
Self::add_extern_static(this, "_tls_used", place.ptr);
191195
}
@@ -426,44 +430,26 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
426430
Ok(())
427431
}
428432

429-
fn thread_local_alloc_id(
433+
fn thread_local_static_alloc_id(
430434
ecx: &mut InterpCx<'mir, 'tcx, Self>,
431435
def_id: DefId,
432436
) -> InterpResult<'tcx, AllocId> {
433437
ecx.get_or_create_thread_local_alloc_id(def_id)
434438
}
435439

436-
fn adjust_global_const(
437-
ecx: &InterpCx<'mir, 'tcx, Self>,
438-
mut val: mir::interpret::ConstValue<'tcx>,
439-
) -> InterpResult<'tcx, mir::interpret::ConstValue<'tcx>> {
440-
// FIXME: Remove this, do The Right Thing in `thread_local_alloc_id` instead.
441-
ecx.remap_thread_local_alloc_ids(&mut val)?;
442-
Ok(val)
443-
}
444-
445-
fn canonical_alloc_id(mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId {
446-
let tcx = mem.tcx;
447-
// Figure out if this is an extern static, and if yes, which one.
448-
let def_id = match tcx.get_global_alloc(id) {
449-
Some(GlobalAlloc::Static(def_id)) if tcx.is_foreign_item(def_id) => def_id,
450-
_ => {
451-
// No need to canonicalize anything.
452-
return id;
453-
}
454-
};
455-
let attrs = tcx.get_attrs(def_id);
440+
fn extern_static_alloc_id(
441+
memory: &Memory<'mir, 'tcx, Self>,
442+
def_id: DefId,
443+
) -> InterpResult<'tcx, AllocId> {
444+
let attrs = memory.tcx.get_attrs(def_id);
456445
let link_name = match attr::first_attr_value_str_by_name(&attrs, sym::link_name) {
457446
Some(name) => name,
458-
None => tcx.item_name(def_id),
447+
None => memory.tcx.item_name(def_id),
459448
};
460-
// Check if we know this one.
461-
if let Some(canonical_id) = mem.extra.extern_statics.get(&link_name) {
462-
trace!("canonical_alloc_id: {:?} ({}) -> {:?}", id, link_name, canonical_id);
463-
*canonical_id
449+
if let Some(&id) = memory.extra.extern_statics.get(&link_name) {
450+
Ok(id)
464451
} else {
465-
// Return original id; `Memory::get_static_alloc` will throw an error.
466-
id
452+
throw_unsup_format!("`extern` static {:?} is not supported by Miri", def_id)
467453
}
468454
}
469455

src/shims/env.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -383,9 +383,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
383383
this.memory.deallocate(this.force_ptr(old_vars_ptr)?, None, MiriMemoryKind::Env.into())?;
384384
} else {
385385
// No `environ` allocated yet, let's do that.
386-
// This is memory backing an extern static, hence `ExternGlobal`, not `Env`.
386+
// This is memory backing an extern static, hence `ExternStatic`, not `Env`.
387387
let layout = this.machine.layouts.usize;
388-
let place = this.allocate(layout, MiriMemoryKind::ExternGlobal.into());
388+
let place = this.allocate(layout, MiriMemoryKind::ExternStatic.into());
389389
this.machine.env_vars.environ = Some(place);
390390
}
391391

src/shims/tls.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -328,9 +328,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
328328
/// schedules them one by one each time it is called and reenables the
329329
/// thread so that it can be executed normally by the main execution loop.
330330
///
331-
/// FIXME: we do not support yet deallocation of thread local statics.
332-
/// Issue: https://github.com/rust-lang/miri/issues/1369
333-
///
334331
/// Note: we consistently run TLS destructors for all threads, including the
335332
/// main thread. However, it is not clear that we should run the TLS
336333
/// destructors for the main thread. See issue:
@@ -351,6 +348,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
351348
return Ok(())
352349
}
353350
}
351+
// The remaining dtors make some progress each time around the scheduler loop,
352+
// until they return `false` to indicate that they are done.
353+
354354
// The macOS thread wide destructor runs "before any TLS slots get
355355
// freed", so do that first.
356356
if this.schedule_macos_tls_dtor()? {
@@ -367,6 +367,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
367367

368368
// All dtors done!
369369
this.machine.tls.delete_all_thread_tls(active_thread);
370+
this.thread_terminated()?;
370371

371372
Ok(())
372373
}

src/stacked_borrows.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -469,10 +469,10 @@ impl Stacks {
469469
// `Global` memory can be referenced by global pointers from `tcx`.
470470
// Thus we call `global_base_ptr` such that the global pointers get the same tag
471471
// as what we use here.
472-
// `ExternGlobal` is used for extern statics, and thus must also be listed here.
472+
// `ExternStatic` is used for extern statics, and thus must also be listed here.
473473
// `Env` we list because we can get away with precise tracking there.
474474
// The base pointer is not unique, so the base permission is `SharedReadWrite`.
475-
MemoryKind::Machine(MiriMemoryKind::Global | MiriMemoryKind::ExternGlobal | MiriMemoryKind::Env) =>
475+
MemoryKind::Machine(MiriMemoryKind::Global | MiriMemoryKind::ExternStatic | MiriMemoryKind::Tls | MiriMemoryKind::Env) =>
476476
(extra.borrow_mut().global_base_ptr(id), Permission::SharedReadWrite),
477477
// Everything else we handle entirely untagged for now.
478478
// FIXME: experiment with more precise tracking.

0 commit comments

Comments
 (0)