18
18
//!
19
19
//! In a nutshell, what happens here is:
20
20
//!
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.
23
24
//! 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.
27
28
//! 3. All compiler-generated calls to `invoke` have a landing pad set as a
28
29
//! `cleanuppad` LLVM instruction, which indicates the start of the cleanup
29
30
//! routine. The personality (in step 2, defined in the CRT) is responsible
30
31
//! for running the cleanup routines.
31
32
//! 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
34
34
//! Rust. This is done via a `catchswitch` plus a `catchpad` instruction in
35
35
//! 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.
39
37
//!
40
38
//! Some specific differences from the gcc-based exception handling are:
41
39
//!
42
40
//! * 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 .
47
45
//! * We've got some data to transmit across the unwinding boundary,
48
46
//! specifically a `Box<Any + Send>`. Like with Dwarf exceptions
49
47
//! 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.
55
53
//!
56
54
//! [win64]: http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx
57
55
//! [llvm]: http://llvm.org/docs/ExceptionHandling.html#background-on-windows-exceptions
58
56
57
+ #![ allow( bad_style) ]
58
+ #![ allow( private_no_mangle_fns) ]
59
+
59
60
use alloc:: boxed:: Box ;
60
61
use core:: any:: Any ;
61
- use core:: intrinsics;
62
62
use core:: mem;
63
63
use core:: raw;
64
64
65
65
use 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
+ }
66
156
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
+ } ;
71
249
72
250
pub 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.
76
256
//
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).
81
260
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 _ ) ;
84
297
u32:: max_value ( )
85
298
}
86
299
87
- pub fn payload ( ) -> [ usize ; 2 ] {
300
+ pub fn payload ( ) -> [ u64 ; 2 ] {
88
301
[ 0 ; 2 ]
89
302
}
90
303
91
- pub unsafe fn cleanup ( payload : [ usize ; 2 ] ) -> Box < Any + Send > {
304
+ pub unsafe fn cleanup ( payload : [ u64 ; 2 ] ) -> Box < Any + Send > {
92
305
mem:: transmute ( raw:: TraitObject {
93
306
data : payload[ 0 ] as * mut _ ,
94
307
vtable : payload[ 1 ] as * mut _ ,
95
308
} )
96
309
}
97
310
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.
124
311
#[ 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
137
316
}
138
317
139
318
// 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,
143
322
#[ lang = "eh_personality" ]
144
323
#[ cfg( not( test) ) ]
145
324
fn rust_eh_personality ( ) {
146
- unsafe { intrinsics:: abort ( ) }
325
+ unsafe { :: core :: intrinsics:: abort ( ) }
147
326
}
0 commit comments