1818//!
1919//! In a nutshell, what happens here is:
2020//!
21- //! 1. The `panic` function calls the standard Windows function `RaiseException`
22- //! with a Rust-specific code, triggering the unwinding process.
21+ //! 1. The `panic` function calls the standard Windows function
22+ //! `_CxxThrowException` to throw a C++-like exception, triggering the
23+ //! unwinding process.
2324//! 2. All landing pads generated by the compiler use the personality function
24- //! `__C_specific_handler` on 64-bit and `__except_handler3` on 32-bit,
25- //! functions in the CRT, and the unwinding code in Windows will use this
26- //! personality function to execute all cleanup code on the stack.
25+ //! `__CxxFrameHandler3`, a function in the CRT, and the unwinding code in
26+ //! Windows will use this personality function to execute all cleanup code on
27+ //! the stack.
2728//! 3. All compiler-generated calls to `invoke` have a landing pad set as a
2829//! `cleanuppad` LLVM instruction, which indicates the start of the cleanup
2930//! routine. The personality (in step 2, defined in the CRT) is responsible
3031//! for running the cleanup routines.
3132//! 4. Eventually the "catch" code in the `try` intrinsic (generated by the
32- //! compiler) is executed, which will ensure that the exception being caught
33- //! is indeed a Rust exception, indicating that control should come back to
33+ //! compiler) is executed and indicates that control should come back to
3434//! Rust. This is done via a `catchswitch` plus a `catchpad` instruction in
3535//! LLVM IR terms, finally returning normal control to the program with a
36- //! `catchret` instruction. The `try` intrinsic uses a filter function to
37- //! detect what kind of exception is being thrown, and this detection is
38- //! implemented as the msvc_try_filter language item below.
36+ //! `catchret` instruction.
3937//!
4038//! Some specific differences from the gcc-based exception handling are:
4139//!
4240//! * Rust has no custom personality function, it is instead *always*
43- //! __C_specific_handler or __except_handler3, so the filtering is done in a
44- //! C++-like manner instead of in the personality function itself. Note that
45- //! the precise codegen for this was lifted from an LLVM test case for SEH
46- //! (this is the `__rust_try_filter` function below) .
41+ //! `__CxxFrameHandler3`. Additionally, no extra filtering is performed, so we
42+ //! end up catching any C++ exceptions that happen to look like the kind we're
43+ //! throwing. Note that throwing an exception into Rust is undefined behavior
44+ //! anyway, so this should be fine .
4745//! * We've got some data to transmit across the unwinding boundary,
4846//! specifically a `Box<Any + Send>`. Like with Dwarf exceptions
4947//! these two pointers are stored as a payload in the exception itself. On
50- //! MSVC, however, there's no need for an extra allocation because the call
51- //! stack is preserved while filter functions are being executed. This means
52- //! that the pointers are passed directly to `RaiseException ` which are then
53- //! recovered in the filter function to be written to the stack frame of the
54- //! `try` intrinsic.
48+ //! MSVC, however, there's no need for an extra heap allocation because the
49+ //! call stack is preserved while filter functions are being executed. This
50+ //! means that the pointers are passed directly to `_CxxThrowException ` which
51+ //! are then recovered in the filter function to be written to the stack frame
52+ //! of the `try` intrinsic.
5553//!
5654//! [win64]: http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx
5755//! [llvm]: http://llvm.org/docs/ExceptionHandling.html#background-on-windows-exceptions
5856
57+ #![ allow( bad_style) ]
58+ #![ allow( private_no_mangle_fns) ]
59+
5960use alloc:: boxed:: Box ;
6061use core:: any:: Any ;
61- use core:: intrinsics;
6262use core:: mem;
6363use core:: raw;
6464
6565use windows as c;
66+ use libc:: { c_int, c_uint} ;
67+
68+ // First up, a whole bunch of type definitions. There's a few platform-specific
69+ // oddities here, and a lot that's just blatantly copied from LLVM. The purpose
70+ // of all this is to implement the `panic` function below through a call to
71+ // `_CxxThrowException`.
72+ //
73+ // This function takes two arguments. The first is a pointer to the data we're
74+ // passing in, which in this case is our trait object. Pretty easy to find! The
75+ // next, however, is more complicated. This is a pointer to a `_ThrowInfo`
76+ // structure, and it generally is just intended to just describe the exception
77+ // being thrown.
78+ //
79+ // Currently the definition of this type [1] is a little hairy, and the main
80+ // oddity (and difference from the online article) is that on 32-bit the
81+ // pointers are pointers but on 64-bit the pointers are expressed as 32-bit
82+ // offsets from the `__ImageBase` symbol. The `ptr_t` and `ptr!` macro in the
83+ // modules below are used to express this.
84+ //
85+ // The maze of type definitions also closely follows what LLVM emits for this
86+ // sort of operation. For example, if you compile this C++ code on MSVC and emit
87+ // the LLVM IR:
88+ //
89+ // #include <stdin.h>
90+ //
91+ // void foo() {
92+ // uint64_t a[2] = {0, 1};
93+ // throw a;
94+ // }
95+ //
96+ // That's essentially what we're trying to emulate. Most of the constant values
97+ // below were just copied from LLVM, I'm at least not 100% sure what's going on
98+ // everywhere. For example the `.PA_K\0` and `.PEA_K\0` strings below (stuck in
99+ // the names of a few of these) I'm not actually sure what they do, but it seems
100+ // to mirror what LLVM does!
101+ //
102+ // In any case, these structures are all constructed in a similar manner, and
103+ // it's just somewhat verbose for us.
104+ //
105+ // [1]: http://www.geoffchappell.com/studies/msvc/language/predefined/
106+
107+ #[ cfg( target_arch = "x86" ) ]
108+ #[ macro_use]
109+ mod imp {
110+ pub type ptr_t = * mut u8 ;
111+ pub const OFFSET : i32 = 4 ;
112+
113+ pub const NAME1 : [ u8 ; 7 ] = [ b'.' , b'P' , b'A' , b'_' , b'K' , 0 , 0 ] ;
114+ pub const NAME2 : [ u8 ; 7 ] = [ b'.' , b'P' , b'A' , b'X' , 0 , 0 , 0 ] ;
115+
116+ macro_rules! ptr {
117+ ( 0 ) => ( 0 as * mut u8 ) ;
118+ ( $e: expr) => ( $e as * mut u8 ) ;
119+ }
120+ }
121+
122+ #[ cfg( target_arch = "x86_64" ) ]
123+ #[ macro_use]
124+ mod imp {
125+ pub type ptr_t = u32 ;
126+ pub const OFFSET : i32 = 8 ;
127+
128+ pub const NAME1 : [ u8 ; 7 ] = [ b'.' , b'P' , b'E' , b'A' , b'_' , b'K' , 0 ] ;
129+ pub const NAME2 : [ u8 ; 7 ] = [ b'.' , b'P' , b'E' , b'A' , b'X' , 0 , 0 ] ;
130+
131+ extern {
132+ pub static __ImageBase: u8 ;
133+ }
134+
135+ macro_rules! ptr {
136+ ( 0 ) => ( 0 ) ;
137+ ( $e: expr) => {
138+ ( ( $e as usize ) - ( & imp:: __ImageBase as * const _ as usize ) ) as u32
139+ }
140+ }
141+ }
142+
143+ #[ repr( C ) ]
144+ pub struct _ThrowInfo {
145+ pub attribues : c_uint ,
146+ pub pnfnUnwind : imp:: ptr_t ,
147+ pub pForwardCompat : imp:: ptr_t ,
148+ pub pCatchableTypeArray : imp:: ptr_t ,
149+ }
150+
151+ #[ repr( C ) ]
152+ pub struct _CatchableTypeArray {
153+ pub nCatchableTypes : c_int ,
154+ pub arrayOfCatchableTypes : [ imp:: ptr_t ; 2 ] ,
155+ }
66156
67- // A code which indicates panics that originate from Rust. Note that some of the
68- // upper bits are used by the system so we just set them to 0 and ignore them.
69- // 0x 0 R S T
70- const RUST_PANIC : c:: DWORD = 0x00525354 ;
157+ #[ repr( C ) ]
158+ pub struct _CatchableType {
159+ pub properties : c_uint ,
160+ pub pType : imp:: ptr_t ,
161+ pub thisDisplacement : _PMD ,
162+ pub sizeOrOffset : c_int ,
163+ pub copy_function : imp:: ptr_t ,
164+ }
165+
166+ #[ repr( C ) ]
167+ pub struct _PMD {
168+ pub mdisp : c_int ,
169+ pub pdisp : c_int ,
170+ pub vdisp : c_int ,
171+ }
172+
173+ #[ repr( C ) ]
174+ pub struct _TypeDescriptor {
175+ pub pVFTable : * const u8 ,
176+ pub spare : * mut u8 ,
177+ pub name : [ u8 ; 7 ] ,
178+ }
179+
180+ static mut THROW_INFO : _ThrowInfo = _ThrowInfo {
181+ attribues : 0 ,
182+ pnfnUnwind : ptr ! ( 0 ) ,
183+ pForwardCompat : ptr ! ( 0 ) ,
184+ pCatchableTypeArray : ptr ! ( 0 ) ,
185+ } ;
186+
187+ static mut CATCHABLE_TYPE_ARRAY : _CatchableTypeArray = _CatchableTypeArray {
188+ nCatchableTypes : 2 ,
189+ arrayOfCatchableTypes : [
190+ ptr ! ( 0 ) ,
191+ ptr ! ( 0 ) ,
192+ ] ,
193+ } ;
194+
195+ static mut CATCHABLE_TYPE1 : _CatchableType = _CatchableType {
196+ properties : 1 ,
197+ pType : ptr ! ( 0 ) ,
198+ thisDisplacement : _PMD {
199+ mdisp : 0 ,
200+ pdisp : -1 ,
201+ vdisp : 0 ,
202+ } ,
203+ sizeOrOffset : imp:: OFFSET ,
204+ copy_function : ptr ! ( 0 ) ,
205+ } ;
206+
207+ static mut CATCHABLE_TYPE2 : _CatchableType = _CatchableType {
208+ properties : 1 ,
209+ pType : ptr ! ( 0 ) ,
210+ thisDisplacement : _PMD {
211+ mdisp : 0 ,
212+ pdisp : -1 ,
213+ vdisp : 0 ,
214+ } ,
215+ sizeOrOffset : imp:: OFFSET ,
216+ copy_function : ptr ! ( 0 ) ,
217+ } ;
218+
219+ extern {
220+ // The leading `\x01` byte here is actually a magical signal to LLVM to
221+ // *not* apply any other mangling like prefixing with a `_` character.
222+ //
223+ // This symbol is the vtable used by C++'s `std::type_info`. Objects of type
224+ // `std::type_info`, type descriptors, have a pointer to this table. Type
225+ // descriptors are referenced by the C++ EH structures defined above and
226+ // that we construct below.
227+ #[ link_name = "\x01 ??_7type_info@@6B@" ]
228+ static TYPE_INFO_VTABLE : * const u8 ;
229+ }
230+
231+ // We use #[lang = "msvc_try_filter"] here as this is the type descriptor which
232+ // we'll use in LLVM's `catchpad` instruction which ends up also being passed as
233+ // an argument to the C++ personality function.
234+ //
235+ // Again, I'm not entirely sure what this is describing, it just seems to work.
236+ #[ cfg_attr( all( not( test) , not( stage0) ) ,
237+ lang = "msvc_try_filter" ) ]
238+ static mut TYPE_DESCRIPTOR1 : _TypeDescriptor = _TypeDescriptor {
239+ pVFTable : & TYPE_INFO_VTABLE as * const _ as * const _ ,
240+ spare : 0 as * mut _ ,
241+ name : imp:: NAME1 ,
242+ } ;
243+
244+ static mut TYPE_DESCRIPTOR2 : _TypeDescriptor = _TypeDescriptor {
245+ pVFTable : & TYPE_INFO_VTABLE as * const _ as * const _ ,
246+ spare : 0 as * mut _ ,
247+ name : imp:: NAME2 ,
248+ } ;
71249
72250pub unsafe fn panic ( data : Box < Any + Send > ) -> u32 {
73- // As mentioned above, the call stack here is preserved while the filter
74- // functions are running, so it's ok to pass stack-local arrays into
75- // `RaiseException`.
251+ use core:: intrinsics:: atomic_store;
252+
253+ // _CxxThrowException executes entirely on this stack frame, so there's no
254+ // need to otherwise transfer `data` to the heap. We just pass a stack
255+ // pointer to this function.
76256 //
77- // The two pointers of the `data` trait object are written to the stack,
78- // passed to `RaiseException`, and they're later extracted by the filter
79- // function below in the "custom exception information" section of the
80- // `EXCEPTION_RECORD` type.
257+ // The first argument is the payload being thrown (our two pointers), and
258+ // the second argument is the type information object describing the
259+ // exception (constructed above).
81260 let ptrs = mem:: transmute :: < _ , raw:: TraitObject > ( data) ;
82- let ptrs = [ ptrs. data , ptrs. vtable ] ;
83- c:: RaiseException ( RUST_PANIC , 0 , 2 , ptrs. as_ptr ( ) as * mut _ ) ;
261+ let mut ptrs = [ ptrs. data as u64 , ptrs. vtable as u64 ] ;
262+ let mut ptrs_ptr = ptrs. as_mut_ptr ( ) ;
263+
264+ // This... may seems surprising, and justifiably so. On 32-bit MSVC the
265+ // pointers between these structure are just that, pointers. On 64-bit MSVC,
266+ // however, the pointers between structures are rather expressed as 32-bit
267+ // offsets from `__ImageBase`.
268+ //
269+ // Consequently, on 32-bit MSVC we can declare all these pointers in the
270+ // `static`s above. On 64-bit MSVC, we would have to express subtraction of
271+ // pointers in statics, which Rust does not currently allow, so we can't
272+ // actually do that.
273+ //
274+ // The next best thing, then is to fill in these structures at runtime
275+ // (panicking is already the "slow path" anyway). So here we reinterpret all
276+ // of these pointer fields as 32-bit integers and then store the
277+ // relevant value into it (atomically, as concurrent panics may be
278+ // happening). Technically the runtime will probably do a nonatomic read of
279+ // these fields, but in theory they never read the *wrong* value so it
280+ // shouldn't be too bad...
281+ //
282+ // In any case, we basically need to do something like this until we can
283+ // express more operations in statics (and we may never be able to).
284+ atomic_store ( & mut THROW_INFO . pCatchableTypeArray as * mut _ as * mut u32 ,
285+ ptr ! ( & CATCHABLE_TYPE_ARRAY as * const _) as u32 ) ;
286+ atomic_store ( & mut CATCHABLE_TYPE_ARRAY . arrayOfCatchableTypes [ 0 ] as * mut _ as * mut u32 ,
287+ ptr ! ( & CATCHABLE_TYPE1 as * const _) as u32 ) ;
288+ atomic_store ( & mut CATCHABLE_TYPE_ARRAY . arrayOfCatchableTypes [ 1 ] as * mut _ as * mut u32 ,
289+ ptr ! ( & CATCHABLE_TYPE2 as * const _) as u32 ) ;
290+ atomic_store ( & mut CATCHABLE_TYPE1 . pType as * mut _ as * mut u32 ,
291+ ptr ! ( & TYPE_DESCRIPTOR1 as * const _) as u32 ) ;
292+ atomic_store ( & mut CATCHABLE_TYPE2 . pType as * mut _ as * mut u32 ,
293+ ptr ! ( & TYPE_DESCRIPTOR2 as * const _) as u32 ) ;
294+
295+ c:: _CxxThrowException ( & mut ptrs_ptr as * mut _ as * mut _ ,
296+ & mut THROW_INFO as * mut _ as * mut _ ) ;
84297 u32:: max_value ( )
85298}
86299
87- pub fn payload ( ) -> [ usize ; 2 ] {
300+ pub fn payload ( ) -> [ u64 ; 2 ] {
88301 [ 0 ; 2 ]
89302}
90303
91- pub unsafe fn cleanup ( payload : [ usize ; 2 ] ) -> Box < Any + Send > {
304+ pub unsafe fn cleanup ( payload : [ u64 ; 2 ] ) -> Box < Any + Send > {
92305 mem:: transmute ( raw:: TraitObject {
93306 data : payload[ 0 ] as * mut _ ,
94307 vtable : payload[ 1 ] as * mut _ ,
95308 } )
96309}
97310
98- // This is quite a special function, and it's not literally passed in as the
99- // filter function for the `catchpad` of the `try` intrinsic. The compiler
100- // actually generates its own filter function wrapper which will delegate to
101- // this for the actual execution logic for whether the exception should be
102- // caught. The reasons for this are:
103- //
104- // * Each architecture has a slightly different ABI for the filter function
105- // here. For example on x86 there are no arguments but on x86_64 there are
106- // two.
107- // * This function needs access to the stack frame of the `try` intrinsic
108- // which is using this filter as a catch pad. This is because the payload
109- // of this exception, `Box<Any>`, needs to be transmitted to that
110- // location.
111- //
112- // Both of these differences end up using a ton of weird llvm-specific
113- // intrinsics, so it's actually pretty difficult to express the entire
114- // filter function in Rust itself. As a compromise, the compiler takes care
115- // of all the weird LLVM-specific and platform-specific stuff, getting to
116- // the point where this function makes the actual decision about what to
117- // catch given two parameters.
118- //
119- // The first parameter is `*mut EXCEPTION_POINTERS` which is some contextual
120- // information about the exception being filtered, and the second pointer is
121- // `*mut *mut [usize; 2]` (the payload here). This value points directly
122- // into the stack frame of the `try` intrinsic itself, and we use it to copy
123- // information from the exception onto the stack.
124311#[ lang = "msvc_try_filter" ]
125- #[ cfg( not( test) ) ]
126- unsafe extern fn __rust_try_filter ( eh_ptrs : * mut u8 ,
127- payload : * mut u8 ) -> i32 {
128- let eh_ptrs = eh_ptrs as * mut c:: EXCEPTION_POINTERS ;
129- let payload = payload as * mut * mut [ usize ; 2 ] ;
130- let record = & * ( * eh_ptrs) . ExceptionRecord ;
131- if record. ExceptionCode != RUST_PANIC {
132- return 0
133- }
134- ( * * payload) [ 0 ] = record. ExceptionInformation [ 0 ] as usize ;
135- ( * * payload) [ 1 ] = record. ExceptionInformation [ 1 ] as usize ;
136- return 1
312+ #[ cfg( stage0) ]
313+ unsafe extern fn __rust_try_filter ( _eh_ptrs : * mut u8 ,
314+ _payload : * mut u8 ) -> i32 {
315+ return 0
137316}
138317
139318// This is required by the compiler to exist (e.g. it's a lang item), but
@@ -143,5 +322,5 @@ unsafe extern fn __rust_try_filter(eh_ptrs: *mut u8,
143322#[ lang = "eh_personality" ]
144323#[ cfg( not( test) ) ]
145324fn rust_eh_personality ( ) {
146- unsafe { intrinsics:: abort ( ) }
325+ unsafe { :: core :: intrinsics:: abort ( ) }
147326}
0 commit comments