4848
4949use alloc:: boxed:: Box ;
5050use core:: any:: Any ;
51- use core:: ffi:: { c_int , c_uint , c_void} ;
51+ use core:: ffi:: c_void;
5252use core:: mem:: ManuallyDrop ;
5353
5454// NOTE(nbdd0121): The `canary` field is part of stable ABI.
@@ -65,10 +65,8 @@ struct Exception {
6565 data : Option < Box < dyn Any + Send > > ,
6666}
6767
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`.
68+ // The purpose of all this is to implement the `panic` function below through a
69+ // call to `_CxxThrowException`.
7270//
7371// This function takes two arguments. The first is a pointer to the data we're
7472// passing in, which in this case is our trait object. Pretty easy to find! The
@@ -79,8 +77,9 @@ struct Exception {
7977// Currently the definition of this type [1] is a little hairy, and the main
8078// oddity (and difference from the online article) is that on 32-bit the
8179// 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.
80+ // offsets from the image base. It's not currently possible to create a relative
81+ // offset in const Rust code, so this is done using assembly with the `@IMGREL`
82+ // relocation.
8483//
8584// The maze of type definitions also closely follows what LLVM emits for this
8685// sort of operation. For example, if you compile this C++ code on MSVC and emit
@@ -108,128 +107,22 @@ struct Exception {
108107//
109108// [1]: https://www.geoffchappell.com/studies/msvc/language/predefined/
110109
111- #[ cfg( target_arch = "x86" ) ]
112- mod imp {
113- #[ repr( transparent) ]
114- #[ derive( Copy , Clone ) ]
115- pub ( super ) struct ptr_t ( * mut u8 ) ;
116-
117- impl ptr_t {
118- pub ( super ) const fn null ( ) -> Self {
119- Self ( core:: ptr:: null_mut ( ) )
120- }
121-
122- pub ( super ) const fn new ( ptr : * mut u8 ) -> Self {
123- Self ( ptr)
124- }
125-
126- pub ( super ) const fn raw ( self ) -> * mut u8 {
127- self . 0
128- }
129- }
130- }
131-
132- #[ cfg( not( target_arch = "x86" ) ) ]
133- mod imp {
134- // On 64-bit systems, SEH represents pointers as 32-bit offsets from `__ImageBase`.
135- #[ repr( transparent) ]
136- #[ derive( Copy , Clone ) ]
137- pub ( super ) struct ptr_t ( u32 ) ;
138-
139- unsafe extern "C" {
140- static __ImageBase: u8 ;
141- }
142-
143- impl ptr_t {
144- pub ( super ) const fn null ( ) -> Self {
145- Self ( 0 )
146- }
147-
148- pub ( super ) fn new ( ptr : * mut u8 ) -> Self {
149- // We need to expose the provenance of the pointer because it is not carried by
150- // the `u32`, while the FFI needs to have this provenance to excess our statics.
151- //
152- // NOTE(niluxv): we could use `MaybeUninit<u32>` instead to leak the provenance
153- // into the FFI. In theory then the other side would need to do some processing
154- // to get a pointer with correct provenance, but these system functions aren't
155- // going to be cross-lang LTOed anyway. However, using expose is shorter and
156- // requires less unsafe.
157- let addr: usize = ptr. expose_provenance ( ) ;
158- let image_base = ( & raw const __ImageBase) . addr ( ) ;
159- let offset: usize = addr - image_base;
160- Self ( offset as u32 )
161- }
162-
163- pub ( super ) const fn raw ( self ) -> u32 {
164- self . 0
165- }
166- }
167- }
168-
169- use imp:: ptr_t;
170-
171- #[ repr( C ) ]
172- struct _ThrowInfo {
173- pub attributes : c_uint ,
174- pub pmfnUnwind : ptr_t ,
175- pub pForwardCompat : ptr_t ,
176- pub pCatchableTypeArray : ptr_t ,
177- }
178-
179- #[ repr( C ) ]
180- struct _CatchableTypeArray {
181- pub nCatchableTypes : c_int ,
182- pub arrayOfCatchableTypes : [ ptr_t ; 1 ] ,
183- }
184-
185- #[ repr( C ) ]
186- struct _CatchableType {
187- pub properties : c_uint ,
188- pub pType : ptr_t ,
189- pub thisDisplacement : _PMD ,
190- pub sizeOrOffset : c_int ,
191- pub copyFunction : ptr_t ,
192- }
193-
194- #[ repr( C ) ]
195- struct _PMD {
196- pub mdisp : c_int ,
197- pub pdisp : c_int ,
198- pub vdisp : c_int ,
199- }
200-
201110#[ repr( C ) ]
202111struct _TypeDescriptor {
203112 pub pVFTable : * const u8 ,
204113 pub spare : * mut u8 ,
205114 pub name : [ u8 ; 11 ] ,
206115}
207116
117+ unsafe impl Sync for _TypeDescriptor { }
118+
208119// Note that we intentionally ignore name mangling rules here: we don't want C++
209120// to be able to catch Rust panics by simply declaring a `struct rust_panic`.
210121//
211122// When modifying, make sure that the type name string exactly matches
212123// the one used in `compiler/rustc_codegen_llvm/src/intrinsic.rs`.
213124const TYPE_NAME : [ u8 ; 11 ] = * b"rust_panic\0 " ;
214125
215- static mut THROW_INFO : _ThrowInfo = _ThrowInfo {
216- attributes : 0 ,
217- pmfnUnwind : ptr_t:: null ( ) ,
218- pForwardCompat : ptr_t:: null ( ) ,
219- pCatchableTypeArray : ptr_t:: null ( ) ,
220- } ;
221-
222- static mut CATCHABLE_TYPE_ARRAY : _CatchableTypeArray =
223- _CatchableTypeArray { nCatchableTypes : 1 , arrayOfCatchableTypes : [ ptr_t:: null ( ) ] } ;
224-
225- static mut CATCHABLE_TYPE : _CatchableType = _CatchableType {
226- properties : 0 ,
227- pType : ptr_t:: null ( ) ,
228- thisDisplacement : _PMD { mdisp : 0 , pdisp : -1 , vdisp : 0 } ,
229- sizeOrOffset : size_of :: < Exception > ( ) as c_int ,
230- copyFunction : ptr_t:: null ( ) ,
231- } ;
232-
233126unsafe extern "C" {
234127 // The leading `\x01` byte here is actually a magical signal to LLVM to
235128 // *not* apply any other mangling like prefixing with a `_` character.
@@ -239,16 +132,16 @@ unsafe extern "C" {
239132 // descriptors are referenced by the C++ EH structures defined above and
240133 // that we construct below.
241134 #[ link_name = "\x01 ??_7type_info@@6B@" ]
242- static TYPE_INFO_VTABLE : * const u8 ;
135+ static TYPE_INFO_VTABLE : u8 ;
243136}
244137
245138// This type descriptor is only used when throwing an exception. The catch part
246139// is handled by the try intrinsic, which generates its own TypeDescriptor.
247140//
248141// This is fine since the MSVC runtime uses string comparison on the type name
249142// to match TypeDescriptors rather than pointer equality.
250- static mut TYPE_DESCRIPTOR : _TypeDescriptor = _TypeDescriptor {
251- pVFTable : ( & raw const TYPE_INFO_VTABLE ) as * const _ ,
143+ static TYPE_DESCRIPTOR : _TypeDescriptor = _TypeDescriptor {
144+ pVFTable : & raw const TYPE_INFO_VTABLE ,
252145 spare : core:: ptr:: null_mut ( ) ,
253146 name : TYPE_NAME ,
254147} ;
@@ -303,69 +196,77 @@ pub(crate) unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
303196}
304197
305198unsafe fn throw_exception ( data : Option < Box < dyn Any + Send > > ) -> ! {
306- use core:: intrinsics:: { AtomicOrdering , atomic_store} ;
307-
308199 // _CxxThrowException executes entirely on this stack frame, so there's no
309200 // need to otherwise transfer `data` to the heap. We just pass a stack
310201 // pointer to this function.
311202 //
312203 // The ManuallyDrop is needed here since we don't want Exception to be
313204 // dropped when unwinding. Instead it will be dropped by exception_cleanup
314205 // which is invoked by the C++ runtime.
315- let mut exception = ManuallyDrop :: new ( Exception { canary : ( & raw const TYPE_DESCRIPTOR ) , data } ) ;
316- let throw_ptr = ( & raw mut exception) as * mut _ ;
206+ let mut exception = ManuallyDrop :: new ( Exception { canary : & raw const TYPE_DESCRIPTOR , data } ) ;
317207
318- // This... may seems surprising, and justifiably so. On 32-bit MSVC the
319- // pointers between these structure are just that, pointers. On 64-bit MSVC,
320- // however, the pointers between structures are rather expressed as 32-bit
321- // offsets from `__ImageBase`.
322- //
323- // Consequently, on 32-bit MSVC we can declare all these pointers in the
324- // `static`s above. On 64-bit MSVC, we would have to express subtraction of
325- // pointers in statics, which Rust does not currently allow, so we can't
326- // actually do that.
327- //
328- // The next best thing, then is to fill in these structures at runtime
329- // (panicking is already the "slow path" anyway). So here we reinterpret all
330- // of these pointer fields as 32-bit integers and then store the
331- // relevant value into it (atomically, as concurrent panics may be
332- // happening). Technically the runtime will probably do a nonatomic read of
333- // these fields, but in theory they never read the *wrong* value so it
334- // shouldn't be too bad...
335- //
336- // In any case, we basically need to do something like this until we can
337- // express more operations in statics (and we may never be able to).
338- unsafe {
339- #[ allow( function_casts_as_integer) ]
340- atomic_store :: < _ , { AtomicOrdering :: SeqCst } > (
341- ( & raw mut THROW_INFO . pmfnUnwind ) . cast ( ) ,
342- ptr_t:: new ( exception_cleanup as * mut u8 ) . raw ( ) ,
343- ) ;
344- atomic_store :: < _ , { AtomicOrdering :: SeqCst } > (
345- ( & raw mut THROW_INFO . pCatchableTypeArray ) . cast ( ) ,
346- ptr_t:: new ( ( & raw mut CATCHABLE_TYPE_ARRAY ) . cast ( ) ) . raw ( ) ,
347- ) ;
348- atomic_store :: < _ , { AtomicOrdering :: SeqCst } > (
349- ( & raw mut CATCHABLE_TYPE_ARRAY . arrayOfCatchableTypes [ 0 ] ) . cast ( ) ,
350- ptr_t:: new ( ( & raw mut CATCHABLE_TYPE ) . cast ( ) ) . raw ( ) ,
351- ) ;
352- atomic_store :: < _ , { AtomicOrdering :: SeqCst } > (
353- ( & raw mut CATCHABLE_TYPE . pType ) . cast ( ) ,
354- ptr_t:: new ( ( & raw mut TYPE_DESCRIPTOR ) . cast ( ) ) . raw ( ) ,
355- ) ;
356- #[ allow( function_casts_as_integer) ]
357- atomic_store :: < _ , { AtomicOrdering :: SeqCst } > (
358- ( & raw mut CATCHABLE_TYPE . copyFunction ) . cast ( ) ,
359- ptr_t:: new ( exception_copy as * mut u8 ) . raw ( ) ,
360- ) ;
208+ unsafe extern "system-unwind" {
209+ fn _CxxThrowException ( pExceptionObject : * mut c_void , pThrowInfo : * const u8 ) -> !;
361210 }
362211
363- unsafe extern "system-unwind" {
364- fn _CxxThrowException ( pExceptionObject : * mut c_void , pThrowInfo : * mut u8 ) -> !;
212+ #[ cfg( target_arch = "x86" ) ]
213+ macro_rules! imgrel {
214+ ( $s: literal) => {
215+ concat!( ".long " , $s)
216+ } ;
217+ }
218+ #[ cfg( not( target_arch = "x86" ) ) ]
219+ macro_rules! imgrel {
220+ ( $s: literal) => {
221+ concat!( ".long " , $s, "@IMGREL" )
222+ } ;
365223 }
366224
225+ let throw_info: * const u8 ;
367226 unsafe {
368- _CxxThrowException ( throw_ptr, ( & raw mut THROW_INFO ) as * mut _ ) ;
227+ core:: arch:: asm!(
228+ cfg_select! { // let throw_info = &THROW_INFO;
229+ target_arch = "x86" => {
230+ "lea {}, [2f]"
231+ }
232+ target_arch = "x86_64" => {
233+ "lea {}, [rip + 2f]"
234+ }
235+ any( target_arch = "arm" , target_arch = "aarch64" , target_arch = "arm64ec" ) => {
236+ "adr {}, 2f"
237+ }
238+ } ,
239+ ".pushsection .rdata,\" dr\" " ,
240+ ".p2align 2" ,
241+ "2:" , // static THROW_INFO = _ThrowInfo {
242+ ".long 0" , // attributes: 0,
243+ imgrel!( "{cleanup}" ) , // pmfnUnwind: exception_cleanup,
244+ ".long 0" , // pForwardCompat: ptr::null_mut(),
245+ imgrel!( "3f" ) , // pCatchableTypeArray: &CATCHABLE_TYPE_ARRAY,
246+ // }
247+ "3:" , // static CATCHABLE_TYPE_ARRAY = _CatchableTypeArray {
248+ ".long 1" , // nCatchableTypes: 1,
249+ imgrel!( "4f" ) , // arrayOfCatchableTypes: [&CATCHABLE_TYPE],
250+ // }
251+ "4:" , // static CATCHABLE_TYPE = _CatchableType {
252+ ".long 0" , // properties: 0,
253+ imgrel!( "{type_desc}" ) , // pType: &TYPE_DESCRIPTOR,
254+ // thisDisplacement: _PMD {
255+ ".long 0" , // mdisp: 0,
256+ ".long -1" , // pdisp: -1,
257+ ".long 0" , // vdisp: 0,
258+ // }
259+ ".long {exception_size}" , // sizeOrOffset: size_of::<Exception>(),
260+ imgrel!( "{copy}" ) , // copyFunction: exception_copy,
261+ ".popsection" , // }
262+ out( reg) throw_info,
263+ cleanup = sym exception_cleanup,
264+ type_desc = sym TYPE_DESCRIPTOR ,
265+ exception_size = const size_of:: <Exception >( ) ,
266+ copy = sym exception_copy,
267+ options( readonly, nostack) ,
268+ ) ;
269+ _CxxThrowException ( ( & raw mut exception) . cast ( ) , throw_info) ;
369270 }
370271}
371272
0 commit comments