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+
114use rustc:: mir;
215use crate :: * ;
316use super :: machine:: FrameData ;
417use rustc_target:: spec:: PanicStrategy ;
518use crate :: rustc_target:: abi:: LayoutOf ;
619
720/// Holds all of the relevant data for a call to
8- /// __rust_maybe_catch_panic
21+ /// ` __rust_maybe_catch_panic`.
922///
1023/// If a panic occurs, we update this data with
11- /// the information from the panic site
24+ /// the information from the panic site.
1225#[ derive( Debug ) ]
1326pub 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`.
1728 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`.
1930 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`.
2132 pub dest : PlaceTy < ' tcx , Tag > ,
22- /// The `ret` from the original call to `__rust_maybe_catch_panic`
23- pub ret : mir:: BasicBlock ,
2433}
2534
2635impl < ' mir , ' tcx > EvalContextExt < ' mir , ' tcx > for crate :: MiriEvalContext < ' mir , ' tcx > { }
2736pub trait EvalContextExt < ' mir , ' tcx : ' mir > : crate :: MiriEvalContextExt < ' mir , ' tcx > {
2837
2938 /// 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.
3140 #[ inline( always) ]
3241 fn handle_miri_start_panic (
3342 & mut self ,
@@ -44,14 +53,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4453 throw_unsup_format ! ( "the evaluated program panicked" ) ;
4554 }
4655
47- // Get the raw pointer stored in arg[0]
56+ // Get the raw pointer stored in arg[0] (the panic payload).
4857 let scalar = this. read_immediate ( args[ 0 ] ) ?;
58+ assert ! ( this. machine. panic_payload. is_none( ) , "the panic runtime should avoid double-panics" ) ;
4959 this. machine . panic_payload = Some ( scalar) ;
5060
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.
5563 let next_frame = this. frame_mut ( ) ;
5664 next_frame. block = unwind;
5765 next_frame. stmt = 0 ;
@@ -74,16 +82,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
7482 // data_ptr: *mut usize,
7583 // vtable_ptr: *mut usize,
7684 // ) -> u32
85+
86+ // Get all the arguments.
7787 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 ( ) ?;
7989 let data_place = this. deref_operand ( args[ 2 ] ) ?;
8090 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.
8193 let f_instance = this. memory . get_fn ( f) ?. as_instance ( ) ?;
82- this. write_null ( dest) ?;
8394 trace ! ( "__rust_maybe_catch_panic: {:?}" , f_instance) ;
84-
85-
86- // Now we make a function call.
8795 // TODO: consider making this reusable? `InterpCx::step` does something similar
8896 // for the TLS destructors, and of course `eval_main`.
8997 let mir = this. load_mir ( f_instance. def , None ) ?;
@@ -98,32 +106,30 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
98106 StackPopCleanup :: Goto { ret : Some ( ret) , unwind : None } ,
99107 ) ?;
100108
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+
101122 // In unwind mode, we tag this frame with some extra data.
102123 // This lets `handle_stack_pop` (below) know that we should stop unwinding
103124 // when we pop this frame.
104125 if this. tcx . tcx . sess . panic_strategy ( ) == PanicStrategy :: Unwind {
105126 this. frame_mut ( ) . extra . catch_panic = Some ( CatchUnwindData {
106- data : data. to_ptr ( ) ?,
107127 data_place,
108128 vtable_place,
109129 dest,
110- ret,
111130 } )
112131 }
113132
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-
127133 return Ok ( ( ) ) ;
128134 }
129135
@@ -140,33 +146,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
140146 // We only care about `catch_panic` if we're unwinding - if we're doing a normal
141147 // return, then we don't need to do anything special.
142148 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) ;
146152
147- // `panic_payload` now holds a ' *mut (dyn Any + Send)' ,
153+ // `panic_payload` now holds a ` *mut (dyn Any + Send)` ,
148154 // provided by the `miri_start_panic` intrinsic.
149155 // 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 ) ?;
155161 let payload_data_place = payload. ptr ;
156162 let payload_vtable_place = payload. meta . expect ( "Expected fat pointer" ) ;
157163
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 ( ) ) ?;
167166
168167 // We set the return value of `__rust_maybe_catch_panic` to 1,
169168 // since there was a panic.
169+ let dest = unwind_data. dest ;
170170 this. write_scalar ( Scalar :: from_int ( 1 , dest. layout . size ) , dest) ?;
171171
172172 StackPopInfo :: StopUnwinding
0 commit comments