1
+ //! Panic runtime for Miri.
2
+ //!
3
+ //! The core pieces of the runtime are:
4
+ //! - An implementation of `__rust_maybe_catch_panic` that pushes the invoked stack frame with
5
+ //! some extra metadata derived from the panic-catching arguments of `__rust_maybe_catch_panic`.
6
+ //! - A hack in `libpanic_unwind` that calls the `miri_start_panic` intrinsic instead of the
7
+ //! target-native panic runtime. (This lives in the rustc repo.)
8
+ //! - An implementation of `miri_start_panic` that stores its argument (the panic payload), and then
9
+ //! immediately returns, but on the *unwind* edge (not the normal return edge), thus initiating unwinding.
10
+ //! - A hook executed each time a frame is popped, such that if the frame pushed by `__rust_maybe_catch_panic`
11
+ //! gets popped *during unwinding*, we take the panic payload and store it according to the extra
12
+ //! metadata we remembered when pushing said frame.
13
+
1
14
use rustc:: mir;
2
15
use crate :: * ;
3
16
use super :: machine:: FrameData ;
4
17
use rustc_target:: spec:: PanicStrategy ;
5
18
use crate :: rustc_target:: abi:: LayoutOf ;
6
19
7
20
/// Holds all of the relevant data for a call to
8
- /// __rust_maybe_catch_panic
21
+ /// ` __rust_maybe_catch_panic`.
9
22
///
10
23
/// If a panic occurs, we update this data with
11
- /// the information from the panic site
24
+ /// the information from the panic site.
12
25
#[ derive( Debug ) ]
13
26
pub struct CatchUnwindData < ' tcx > {
14
- /// The 'data' argument passed to `__rust_maybe_catch_panic`
15
- pub data : Pointer < Tag > ,
16
- /// The `data_ptr` argument passed to `__rust_maybe_catch_panic`
27
+ /// The dereferenced `data_ptr` argument passed to `__rust_maybe_catch_panic`.
17
28
pub data_place : MPlaceTy < ' tcx , Tag > ,
18
- /// The `vtable_ptr` argument passed to `__rust_maybe_catch_panic`
29
+ /// The dereferenced `vtable_ptr` argument passed to `__rust_maybe_catch_panic`.
19
30
pub vtable_place : MPlaceTy < ' tcx , Tag > ,
20
- /// The `dest` from the original call to `__rust_maybe_catch_panic`
31
+ /// The `dest` from the original call to `__rust_maybe_catch_panic`.
21
32
pub dest : PlaceTy < ' tcx , Tag > ,
22
- /// The `ret` from the original call to `__rust_maybe_catch_panic`
23
- pub ret : mir:: BasicBlock ,
24
33
}
25
34
26
35
impl < ' mir , ' tcx > EvalContextExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
27
36
pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriEvalContextExt < ' mir , ' tcx > {
28
37
29
38
/// Handles the special "miri_start_panic" intrinsic, which is called
30
- /// by libpanic_unwind to delegate the actual unwinding process to Miri
39
+ /// by libpanic_unwind to delegate the actual unwinding process to Miri.
31
40
#[ inline( always) ]
32
41
fn handle_miri_start_panic (
33
42
& mut self ,
@@ -44,14 +53,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
44
53
throw_unsup_format ! ( "the evaluated program panicked" ) ;
45
54
}
46
55
47
- // Get the raw pointer stored in arg[0]
56
+ // Get the raw pointer stored in arg[0] (the panic payload).
48
57
let scalar = this. read_immediate ( args[ 0 ] ) ?;
58
+ assert ! ( this. machine. panic_payload. is_none( ) , "the panic runtime should avoid double-panics" ) ;
49
59
this. machine . panic_payload = Some ( scalar) ;
50
60
51
- // Jump to the unwind block to begin unwinding
52
- // We don't use 'goto_block' - if `unwind` is None,
53
- // we'll end up immediately returning out of the
54
- // function during the next step() call
61
+ // Jump to the unwind block to begin unwinding.
62
+ // We don't use `goto_block` as that is just meant for normal returns.
55
63
let next_frame = this. frame_mut ( ) ;
56
64
next_frame. block = unwind;
57
65
next_frame. stmt = 0 ;
@@ -74,16 +82,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
74
82
// data_ptr: *mut usize,
75
83
// vtable_ptr: *mut usize,
76
84
// ) -> u32
85
+
86
+ // Get all the arguments.
77
87
let f = this. read_scalar ( args[ 0 ] ) ?. not_undef ( ) ?;
78
- let data = this. read_scalar ( args[ 1 ] ) ?. not_undef ( ) ?;
88
+ let f_arg = this. read_scalar ( args[ 1 ] ) ?. not_undef ( ) ?;
79
89
let data_place = this. deref_operand ( args[ 2 ] ) ?;
80
90
let vtable_place = this. deref_operand ( args[ 3 ] ) ?;
91
+
92
+ // Now we make a function call, and pass `f_arg` as first and only argument.
81
93
let f_instance = this. memory . get_fn ( f) ?. as_instance ( ) ?;
82
- this. write_null ( dest) ?;
83
94
trace ! ( "__rust_maybe_catch_panic: {:?}" , f_instance) ;
84
-
85
-
86
- // Now we make a function call.
87
95
// TODO: consider making this reusable? `InterpCx::step` does something similar
88
96
// for the TLS destructors, and of course `eval_main`.
89
97
let mir = this. load_mir ( f_instance. def , None ) ?;
@@ -98,32 +106,30 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
98
106
StackPopCleanup :: Goto { ret : Some ( ret) , unwind : None } ,
99
107
) ?;
100
108
109
+ let mut args = this. frame ( ) . body . args_iter ( ) ;
110
+ // First argument.
111
+ let arg_local = args
112
+ . next ( )
113
+ . expect ( "Argument to __rust_maybe_catch_panic does not take enough arguments." ) ;
114
+ let arg_dest = this. local_place ( arg_local) ?;
115
+ this. write_scalar ( f_arg, arg_dest) ?;
116
+ // No more arguments.
117
+ args. next ( ) . expect_none ( "__rust_maybe_catch_panic argument has more arguments than expected" ) ;
118
+
119
+ // We ourselves will return `0`, eventually (will be overwritten if we catch a panic).
120
+ this. write_null ( dest) ?;
121
+
101
122
// In unwind mode, we tag this frame with some extra data.
102
123
// This lets `handle_stack_pop` (below) know that we should stop unwinding
103
124
// when we pop this frame.
104
125
if this. tcx . tcx . sess . panic_strategy ( ) == PanicStrategy :: Unwind {
105
126
this. frame_mut ( ) . extra . catch_panic = Some ( CatchUnwindData {
106
- data : data. to_ptr ( ) ?,
107
127
data_place,
108
128
vtable_place,
109
129
dest,
110
- ret,
111
130
} )
112
131
}
113
132
114
- let mut args = this. frame ( ) . body . args_iter ( ) ;
115
-
116
- let arg_local = args
117
- . next ( )
118
- . expect ( "Argument to __rust_maybe_catch_panic does not take enough arguments." ) ;
119
- let arg_dest = this. local_place ( arg_local) ?;
120
- this. write_scalar ( data, arg_dest) ?;
121
-
122
- args. next ( ) . expect_none ( "__rust_maybe_catch_panic argument has more arguments than expected" ) ;
123
-
124
- // We ourselves will return `0`, eventually (because we will not return if we paniced).
125
- this. write_null ( dest) ?;
126
-
127
133
return Ok ( ( ) ) ;
128
134
}
129
135
@@ -140,33 +146,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
140
146
// We only care about `catch_panic` if we're unwinding - if we're doing a normal
141
147
// return, then we don't need to do anything special.
142
148
let res = if let ( true , Some ( unwind_data) ) = ( unwinding, extra. catch_panic . take ( ) ) {
143
- // We've just popped the frame that was immediately above
144
- // the frame which originally called `__rust_maybe_catch_panic`
145
- trace ! ( "unwinding: found catch_panic frame: {:?}" , this. frame( ) . span) ;
149
+ // We've just popped a frame that was pushed by `__rust_maybe_catch_panic`,
150
+ // and we are unwinding, so we should catch that.
151
+ trace ! ( "unwinding: found catch_panic frame during unwinding : {:?}" , this. frame( ) . span) ;
146
152
147
- // `panic_payload` now holds a ' *mut (dyn Any + Send)' ,
153
+ // `panic_payload` now holds a ` *mut (dyn Any + Send)` ,
148
154
// provided by the `miri_start_panic` intrinsic.
149
155
// We want to split this into its consituient parts -
150
- // the data and vtable pointers - and store them back
151
- // into the panic handler frame
152
- let real_ret = this . machine . panic_payload . take ( ) . unwrap ( ) ;
153
-
154
- let payload = this. ref_to_mplace ( real_ret ) ?;
156
+ // the data and vtable pointers - and store them according to
157
+ // `unwind_data`, i.e., we store them where `__rust_maybe_catch_panic`
158
+ // was told to put them.
159
+ let payload = this . machine . panic_payload . take ( ) . unwrap ( ) ;
160
+ let payload = this. ref_to_mplace ( payload ) ?;
155
161
let payload_data_place = payload. ptr ;
156
162
let payload_vtable_place = payload. meta . expect ( "Expected fat pointer" ) ;
157
163
158
-
159
- let data_place = unwind_data. data_place ;
160
- let vtable_place = unwind_data. vtable_place ;
161
- let dest = unwind_data. dest ;
162
-
163
- // Here, we write to the pointers provided to the call
164
- // to '__rust_maybe_catch_panic`.
165
- this. write_scalar ( payload_data_place, data_place. into ( ) ) ?;
166
- this. write_scalar ( payload_vtable_place, vtable_place. into ( ) ) ?;
164
+ this. write_scalar ( payload_data_place, unwind_data. data_place . into ( ) ) ?;
165
+ this. write_scalar ( payload_vtable_place, unwind_data. vtable_place . into ( ) ) ?;
167
166
168
167
// We set the return value of `__rust_maybe_catch_panic` to 1,
169
168
// since there was a panic.
169
+ let dest = unwind_data. dest ;
170
170
this. write_scalar ( Scalar :: from_int ( 1 , dest. layout . size ) , dest) ?;
171
171
172
172
StackPopInfo :: StopUnwinding
0 commit comments