Skip to content

Commit d01c9cd

Browse files
committed
Support unwinding after a panic
Fixes rust-lang#658 This commit adds support for unwinding after a panic. It requires a companion rustc PR to be merged, in order for the necessary hooks to work properly. Currently implemented: * Selecting between unwind/abort mode based on the rustc Session * Properly popping off stack frames, unwinding back the caller * Running 'unwind' blocks in Mir terminators Not yet implemented: * 'Abort' terminators This PR was getting fairly large, so I decided to open it for review without implementing 'Abort' terminator support. This could either be added on to this PR, or merged separately.
1 parent 4dbc90b commit d01c9cd

File tree

14 files changed

+451
-61
lines changed

14 files changed

+451
-61
lines changed

src/eval.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use crate::{
1414
TlsEvalContextExt,
1515
};
1616

17+
use crate::machine::CachedTypes;
18+
1719
/// Configuration needed to spawn a Miri instance.
1820
#[derive(Clone)]
1921
pub struct MiriConfig {
@@ -38,11 +40,8 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
3840
let mut ecx = InterpCx::new(
3941
tcx.at(syntax::source_map::DUMMY_SP),
4042
ty::ParamEnv::reveal_all(),
41-
Evaluator::new(config.communicate),
42-
MemoryExtra::new(
43-
StdRng::seed_from_u64(config.seed.unwrap_or(0)),
44-
config.validate,
45-
),
43+
Evaluator::new(config.communicate, CachedTypes::new(tcx)?),
44+
MemoryExtra::new(StdRng::seed_from_u64(config.seed.unwrap_or(0)), config.validate),
4645
);
4746
// Complete initialization.
4847
EnvVars::init(&mut ecx, config.excluded_env_vars);

src/helpers.rs

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc::mir;
66
use rustc::ty::{
77
self,
88
List,
9-
layout::{self, LayoutOf, Size, TyLayout},
9+
layout::{self, LayoutOf, Size, TyLayout, Align},
1010
};
1111

1212
use rand::RngCore;
@@ -15,32 +15,31 @@ use crate::*;
1515

1616
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
1717

18-
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
19-
/// Gets an instance for a path.
20-
fn resolve_path(&self, path: &[&str]) -> InterpResult<'tcx, ty::Instance<'tcx>> {
21-
let this = self.eval_context_ref();
22-
this.tcx
23-
.crates()
24-
.iter()
25-
.find(|&&krate| this.tcx.original_crate_name(krate).as_str() == path[0])
26-
.and_then(|krate| {
27-
let krate = DefId {
28-
krate: *krate,
29-
index: CRATE_DEF_INDEX,
30-
};
31-
let mut items = this.tcx.item_children(krate);
32-
let mut path_it = path.iter().skip(1).peekable();
33-
34-
while let Some(segment) = path_it.next() {
35-
for item in mem::replace(&mut items, Default::default()).iter() {
36-
if item.ident.name.as_str() == *segment {
37-
if path_it.peek().is_none() {
38-
return Some(ty::Instance::mono(this.tcx.tcx, item.res.def_id()));
39-
}
40-
41-
items = this.tcx.item_children(item.res.def_id());
42-
break;
18+
/// Gets an instance for a path.
19+
pub fn resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> InterpResult<'tcx, DefId> {
20+
tcx
21+
.crates()
22+
.iter()
23+
.find(|&&krate| tcx.original_crate_name(krate).as_str() == path[0])
24+
.and_then(|krate| {
25+
let krate = DefId {
26+
krate: *krate,
27+
index: CRATE_DEF_INDEX,
28+
};
29+
let mut items = tcx.item_children(krate);
30+
let mut path_it = path.iter().skip(1).peekable();
31+
32+
while let Some(segment) = path_it.next() {
33+
for item in mem::replace(&mut items, Default::default()).iter() {
34+
if item.ident.name.as_str() == *segment {
35+
if path_it.peek().is_none() {
36+
return Some(item.res.def_id())
37+
//eprintln!("Generics: {:?}", this.tcx.generics_of(item.res.def_id()));
38+
//return Some(ty::Instance::mono(this.tcx.tcx, item.res.def_id()).def_id());
4339
}
40+
41+
items = tcx.item_children(item.res.def_id());
42+
break;
4443
}
4544
}
4645
None
@@ -49,6 +48,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4948
let path = path.iter().map(|&s| s.to_owned()).collect();
5049
err_unsup!(PathNotFound(path)).into()
5150
})
51+
}
52+
53+
54+
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
55+
56+
fn resolve_path(&self, path: &[&str]) -> InterpResult<'tcx, ty::Instance<'tcx>> {
57+
Ok(ty::Instance::mono(self.eval_context_ref().tcx.tcx, resolve_did(self.eval_context_ref().tcx.tcx, path)?))
5258
}
5359

5460
/// Write a 0 of the appropriate size to `dest`.

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ mod machine;
2525
mod eval;
2626

2727
// Make all those symbols available in the same place as our own.
28+
2829
pub use rustc_mir::interpret::*;
2930
// Resolve ambiguity.
3031
pub use rustc_mir::interpret::{self, AllocMap, PlaceTy};

src/machine.rs

Lines changed: 126 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use std::rc::Rc;
88
use rand::rngs::StdRng;
99

1010
use rustc::hir::def_id::DefId;
11+
use rustc::ty::{self, Ty, TyCtxt, layout::{Size, LayoutOf, TyLayout}};
12+
use rustc::ty::{ExistentialPredicate, ExistentialTraitRef, RegionKind, List, ParamEnv, TyCtxt};
1113
use rustc::mir;
1214
use rustc::ty::{
1315
self,
@@ -25,6 +27,25 @@ pub const STACK_ADDR: u64 = 32 * PAGE_SIZE; // not really about the "stack", but
2527
pub const STACK_SIZE: u64 = 16 * PAGE_SIZE; // whatever
2628
pub const NUM_CPUS: u64 = 1;
2729

30+
pub struct FrameData<'tcx> {
31+
pub call_id: stacked_borrows::CallId,
32+
pub catch_panic: Option<UnwindData<'tcx>>,
33+
pub is_box_me_frame: bool
34+
}
35+
36+
/// Hold all of the relevant data for a call to
37+
/// __rust_maybe_catch_panic
38+
///
39+
/// If a panic occurs, we update this data with
40+
/// the information from the panic site
41+
pub struct UnwindData<'tcx> {
42+
pub data: Pointer<Tag>,
43+
pub data_ptr: MPlaceTy<'tcx, Tag>,
44+
pub vtable_ptr: MPlaceTy<'tcx, Tag>,
45+
pub dest: PlaceTy<'tcx, Tag>,
46+
pub ret: mir::BasicBlock,
47+
}
48+
2849
/// Extra memory kinds
2950
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
3051
pub enum MiriMemoryKind {
@@ -38,6 +59,8 @@ pub enum MiriMemoryKind {
3859
Env,
3960
/// Statics.
4061
Static,
62+
/// Temporary storage for implementing unwinding
63+
UnwindHelper,
4164
}
4265

4366
impl Into<MemoryKind<MiriMemoryKind>> for MiriMemoryKind {
@@ -78,6 +101,36 @@ impl MemoryExtra {
78101
}
79102
}
80103

104+
pub struct CachedTypes<'tcx> {
105+
/// The layout of the type '*mut &mut dyn core::panic::BoxMeUp'
106+
pub box_me_up_layout: TyLayout<'tcx>,
107+
}
108+
109+
impl<'tcx> CachedTypes<'tcx> {
110+
pub fn new(tcx: TyCtxt<'tcx>) -> InterpResult<'tcx, CachedTypes<'tcx>> {
111+
// We build up the layout of '*mut &mut dyn core::panic::BoxMeUp'
112+
let box_me_up_did = helpers::resolve_did(tcx, &["core", "panic", "BoxMeUp"])?;
113+
let traits = &[ExistentialPredicate::Trait(ExistentialTraitRef {
114+
def_id: box_me_up_did,
115+
substs: List::empty()
116+
})];
117+
118+
let me_mut_dyn = tcx.mk_dynamic(
119+
ty::Binder::dummy(tcx.intern_existential_predicates(traits)),
120+
&RegionKind::ReErased
121+
);
122+
123+
let me_mut_ref = tcx.mk_mut_ref(&RegionKind::ReErased, me_mut_dyn);
124+
let me_mut_raw = tcx.mk_mut_ptr(me_mut_ref);
125+
let box_me_up_layout = tcx.layout_of(ParamEnv::empty().and(me_mut_raw))
126+
.map_err(|layout| InterpError::Layout(layout))?;
127+
128+
Ok(CachedTypes {
129+
box_me_up_layout
130+
})
131+
}
132+
}
133+
81134
/// The machine itself.
82135
pub struct Evaluator<'tcx> {
83136
/// Environment variables set by `setenv`.
@@ -102,10 +155,21 @@ pub struct Evaluator<'tcx> {
102155
pub(crate) communicate: bool,
103156

104157
pub(crate) file_handler: FileHandler,
158+
159+
/// Extra information needed for unwinding
160+
/// We create this even in abort mode, so
161+
/// that we can perform some basic validation
162+
/// during panics
163+
pub(crate) cached_data: CachedTypes<'tcx>,
164+
165+
/// Whether or not we are currently unwinding from
166+
/// a panic
167+
pub(crate) unwinding: bool,
168+
pub(crate) box_me_up_tmp_ptr: Option<MPlaceTy<'tcx, Tag>>
105169
}
106170

107171
impl<'tcx> Evaluator<'tcx> {
108-
pub(crate) fn new(communicate: bool) -> Self {
172+
pub(crate) fn new(communicate: bool, cached_data: CachedTypes<'tcx>) -> Self {
109173
Evaluator {
110174
// `env_vars` could be initialized properly here if `Memory` were available before
111175
// calling this method.
@@ -117,6 +181,9 @@ impl<'tcx> Evaluator<'tcx> {
117181
tls: TlsData::default(),
118182
communicate,
119183
file_handler: Default::default(),
184+
cached_data,
185+
unwinding: false,
186+
box_me_up_tmp_ptr: None
120187
}
121188
}
122189
}
@@ -144,7 +211,7 @@ impl<'mir, 'tcx> MiriEvalContextExt<'mir, 'tcx> for MiriEvalContext<'mir, 'tcx>
144211
impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
145212
type MemoryKinds = MiriMemoryKind;
146213

147-
type FrameExtra = stacked_borrows::CallId;
214+
type FrameExtra = FrameData<'tcx>;
148215
type MemoryExtra = MemoryExtra;
149216
type AllocExtra = AllocExtra;
150217
type PointerTag = Tag;
@@ -174,8 +241,9 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
174241
args: &[OpTy<'tcx, Tag>],
175242
dest: Option<PlaceTy<'tcx, Tag>>,
176243
ret: Option<mir::BasicBlock>,
244+
unwind: Option<mir::BasicBlock>,
177245
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
178-
ecx.find_fn(instance, args, dest, ret)
246+
ecx.find_fn(instance, args, dest, ret, unwind)
179247
}
180248

181249
#[inline(always)]
@@ -345,21 +413,65 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
345413
#[inline(always)]
346414
fn stack_push(
347415
ecx: &mut InterpCx<'mir, 'tcx, Self>,
348-
) -> InterpResult<'tcx, stacked_borrows::CallId> {
349-
Ok(ecx.memory.extra.stacked_borrows.borrow_mut().new_call())
416+
) -> InterpResult<'tcx, FrameData<'tcx>> {
417+
Ok(FrameData {
418+
call_id: ecx.memory.extra.stacked_borrows.borrow_mut().new_call(),
419+
catch_panic: None,
420+
is_box_me_frame: false
421+
})
350422
}
351423

352424
#[inline(always)]
353425
fn stack_pop(
354426
ecx: &mut InterpCx<'mir, 'tcx, Self>,
355-
extra: stacked_borrows::CallId,
356-
) -> InterpResult<'tcx> {
357-
Ok(ecx
358-
.memory
359-
.extra
360-
.stacked_borrows
361-
.borrow_mut()
362-
.end_call(extra))
427+
extra: FrameData<'tcx>,
428+
) -> InterpResult<'tcx, StackPopInfo> {
429+
if extra.is_box_me_frame {
430+
trace!("unwinding: found box_me_frame");
431+
ecx.machine.unwinding = true;
432+
}
433+
if ecx.machine.unwinding {
434+
trace!("Popping during unwind!");
435+
if let Some(unwind_data) = ecx.frame_mut().extra.catch_panic.take() {
436+
// We've just popped the frame that was immediately above
437+
// our target frame on the stack.
438+
//
439+
trace!("unwinding: found target frame: {:?}", ecx.frame().span);
440+
441+
// 'box_me_up' has finished. 'temp_ptr' now holds
442+
// a '*mut (dyn Any + Send)'
443+
// We want to split this into its consituient parts -
444+
// the data and vtable pointers - and store them back
445+
// into the panic handler frame
446+
let tmp_ptr = ecx.machine.box_me_up_tmp_ptr.take().unwrap();
447+
let real_ret = ecx.read_immediate(tmp_ptr.into())?;
448+
let payload_data_ptr = real_ret.to_scalar_ptr()?;
449+
let payload_vtable_ptr = real_ret.to_meta()?.expect("Expected fat pointer");
450+
451+
452+
let data_ptr = unwind_data.data_ptr.clone();
453+
let vtable_ptr = unwind_data.vtable_ptr.clone();
454+
let dest = unwind_data.dest.clone();
455+
drop(unwind_data);
456+
457+
458+
// Here, we write directly into the frame of the function
459+
// that called '__rust_maybe_catch_panic'.
460+
// (NOT the function that called '__rust_start_panic')
461+
462+
ecx.write_scalar(payload_data_ptr, data_ptr.into())?;
463+
ecx.write_scalar(payload_vtable_ptr, vtable_ptr.into())?;
464+
465+
// We 'return' the value 1 from __rust_maybe_catch_panic,
466+
// since there was a panic
467+
ecx.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
468+
ecx.machine.unwinding = false;
469+
470+
ecx.memory.deallocate(tmp_ptr.to_ptr()?, None, MiriMemoryKind::UnwindHelper.into())?;
471+
}
472+
}
473+
ecx.memory.extra.stacked_borrows.borrow_mut().end_call(extra.call_id);
474+
Ok(StackPopInfo { unwinding: ecx.machine.unwinding })
363475
}
364476

365477
#[inline(always)]
@@ -425,7 +537,7 @@ impl MayLeak for MiriMemoryKind {
425537
fn may_leak(self) -> bool {
426538
use self::MiriMemoryKind::*;
427539
match self {
428-
Rust | C | WinHeap => false,
540+
Rust | C | WinHeap | UnwindHelper => false,
429541
Env | Static => true,
430542
}
431543
}

0 commit comments

Comments
 (0)