@@ -61,6 +61,8 @@ use core::ptr;
6161use alloc:: boxed:: Box ;
6262
6363use unwind as uw;
64+ use libc:: { c_int, uintptr_t} ;
65+ use dwarf:: eh:: { self , EHContext , EHAction } ;
6466
6567#[ repr( C ) ]
6668struct Exception {
@@ -106,139 +108,184 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class {
106108 0x4d4f5a_00_52555354
107109}
108110
109- // All targets, except ARM which uses a slightly different ABI (however, iOS goes here as it uses
110- // SjLj unwinding). Also, 64-bit Windows implementation lives in seh64_gnu.rs
111- #[ cfg( all( any( target_os = "ios" , not( target_arch = "arm" ) ) ) ) ]
112- pub mod eabi {
113- use unwind as uw;
114- use libc:: { c_int, uintptr_t} ;
115- use dwarf:: eh:: { EHContext , EHAction , find_eh_action} ;
116111
117- // Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister()
118- // and TargetLowering::getExceptionSelectorRegister() for each architecture,
119- // then mapped to DWARF register numbers via register definition tables
120- // (typically <arch>RegisterInfo.td, search for "DwarfRegNum").
121- // See also http://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register.
112+ // Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister()
113+ // and TargetLowering::getExceptionSelectorRegister() for each architecture,
114+ // then mapped to DWARF register numbers via register definition tables
115+ // (typically <arch>RegisterInfo.td, search for "DwarfRegNum").
116+ // See also http://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register.
122117
123- #[ cfg( target_arch = "x86" ) ]
124- const UNWIND_DATA_REG : ( i32 , i32 ) = ( 0 , 2 ) ; // EAX, EDX
118+ #[ cfg( target_arch = "x86" ) ]
119+ const UNWIND_DATA_REG : ( i32 , i32 ) = ( 0 , 2 ) ; // EAX, EDX
125120
126- #[ cfg( target_arch = "x86_64" ) ]
127- const UNWIND_DATA_REG : ( i32 , i32 ) = ( 0 , 1 ) ; // RAX, RDX
121+ #[ cfg( target_arch = "x86_64" ) ]
122+ const UNWIND_DATA_REG : ( i32 , i32 ) = ( 0 , 1 ) ; // RAX, RDX
128123
129- #[ cfg( any( target_arch = "arm" , target_arch = "aarch64" ) ) ]
130- const UNWIND_DATA_REG : ( i32 , i32 ) = ( 0 , 1 ) ; // R0, R1 / X0, X1
124+ #[ cfg( any( target_arch = "arm" , target_arch = "aarch64" ) ) ]
125+ const UNWIND_DATA_REG : ( i32 , i32 ) = ( 0 , 1 ) ; // R0, R1 / X0, X1
131126
132- #[ cfg( any( target_arch = "mips" , target_arch = "mipsel" ) ) ]
133- const UNWIND_DATA_REG : ( i32 , i32 ) = ( 4 , 5 ) ; // A0, A1
127+ #[ cfg( any( target_arch = "mips" , target_arch = "mipsel" ) ) ]
128+ const UNWIND_DATA_REG : ( i32 , i32 ) = ( 4 , 5 ) ; // A0, A1
134129
135- #[ cfg( any( target_arch = "powerpc" , target_arch = "powerpc64" ) ) ]
136- const UNWIND_DATA_REG : ( i32 , i32 ) = ( 3 , 4 ) ; // R3, R4 / X3, X4
130+ #[ cfg( any( target_arch = "powerpc" , target_arch = "powerpc64" ) ) ]
131+ const UNWIND_DATA_REG : ( i32 , i32 ) = ( 3 , 4 ) ; // R3, R4 / X3, X4
137132
138- // Based on GCC's C and C++ personality routines. For reference, see:
139- // https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
140- // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
141- #[ lang = "eh_personality" ]
142- #[ no_mangle]
143- #[ allow( unused) ]
144- unsafe extern "C" fn rust_eh_personality ( version : c_int ,
145- actions : uw:: _Unwind_Action ,
146- exception_class : uw:: _Unwind_Exception_Class ,
147- exception_object : * mut uw:: _Unwind_Exception ,
148- context : * mut uw:: _Unwind_Context )
149- -> uw:: _Unwind_Reason_Code {
150- if version != 1 {
151- return uw:: _URC_FATAL_PHASE1_ERROR;
152- }
153- let lsda = uw:: _Unwind_GetLanguageSpecificData ( context) as * const u8 ;
154- let mut ip_before_instr: c_int = 0 ;
155- let ip = uw:: _Unwind_GetIPInfo ( context, & mut ip_before_instr) ;
156- let eh_context = EHContext {
157- // The return address points 1 byte past the call instruction,
158- // which could be in the next IP range in LSDA range table.
159- ip : if ip_before_instr != 0 { ip } else { ip - 1 } ,
160- func_start : uw:: _Unwind_GetRegionStart ( context) ,
161- get_text_start : & || uw:: _Unwind_GetTextRelBase ( context) ,
162- get_data_start : & || uw:: _Unwind_GetDataRelBase ( context) ,
163- } ;
164- let eh_action = find_eh_action ( lsda, & eh_context) ;
133+ // The following code is based on GCC's C and C++ personality routines. For reference, see:
134+ // https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
135+ // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
165136
166- if actions as i32 & uw:: _UA_SEARCH_PHASE as i32 != 0 {
167- match eh_action {
168- EHAction :: None | EHAction :: Cleanup ( _) => return uw:: _URC_CONTINUE_UNWIND,
169- EHAction :: Catch ( _) => return uw:: _URC_HANDLER_FOUND,
170- EHAction :: Terminate => return uw:: _URC_FATAL_PHASE1_ERROR,
171- }
172- } else {
173- match eh_action {
174- EHAction :: None => return uw:: _URC_CONTINUE_UNWIND,
175- EHAction :: Cleanup ( lpad) | EHAction :: Catch ( lpad) => {
176- uw:: _Unwind_SetGR ( context, UNWIND_DATA_REG . 0 , exception_object as uintptr_t ) ;
177- uw:: _Unwind_SetGR ( context, UNWIND_DATA_REG . 1 , 0 ) ;
178- uw:: _Unwind_SetIP ( context, lpad) ;
179- return uw:: _URC_INSTALL_CONTEXT;
180- }
181- EHAction :: Terminate => return uw:: _URC_FATAL_PHASE2_ERROR,
137+ // The personality routine for most of our targets, except ARM, which has a slightly different ABI
138+ // (however, iOS goes here as it uses SjLj unwinding). Also, the 64-bit Windows implementation
139+ // lives in seh64_gnu.rs
140+ #[ cfg( all( any( target_os = "ios" , not( target_arch = "arm" ) ) ) ) ]
141+ #[ lang = "eh_personality" ]
142+ #[ no_mangle]
143+ #[ allow( unused) ]
144+ unsafe extern "C" fn rust_eh_personality ( version : c_int ,
145+ actions : uw:: _Unwind_Action ,
146+ exception_class : uw:: _Unwind_Exception_Class ,
147+ exception_object : * mut uw:: _Unwind_Exception ,
148+ context : * mut uw:: _Unwind_Context )
149+ -> uw:: _Unwind_Reason_Code {
150+ if version != 1 {
151+ return uw:: _URC_FATAL_PHASE1_ERROR;
152+ }
153+ let eh_action = find_eh_action ( context) ;
154+ if actions as i32 & uw:: _UA_SEARCH_PHASE as i32 != 0 {
155+ match eh_action {
156+ EHAction :: None | EHAction :: Cleanup ( _) => return uw:: _URC_CONTINUE_UNWIND,
157+ EHAction :: Catch ( _) => return uw:: _URC_HANDLER_FOUND,
158+ EHAction :: Terminate => return uw:: _URC_FATAL_PHASE1_ERROR,
159+ }
160+ } else {
161+ match eh_action {
162+ EHAction :: None => return uw:: _URC_CONTINUE_UNWIND,
163+ EHAction :: Cleanup ( lpad) | EHAction :: Catch ( lpad) => {
164+ uw:: _Unwind_SetGR ( context, UNWIND_DATA_REG . 0 , exception_object as uintptr_t ) ;
165+ uw:: _Unwind_SetGR ( context, UNWIND_DATA_REG . 1 , 0 ) ;
166+ uw:: _Unwind_SetIP ( context, lpad) ;
167+ return uw:: _URC_INSTALL_CONTEXT;
182168 }
169+ EHAction :: Terminate => return uw:: _URC_FATAL_PHASE2_ERROR,
183170 }
184171 }
185-
186- #[ cfg( stage0) ]
187- #[ lang = "eh_personality_catch" ]
188- #[ no_mangle]
189- pub unsafe extern "C" fn rust_eh_personality_catch ( version : c_int ,
190- actions : uw:: _Unwind_Action ,
191- exception_class : uw:: _Unwind_Exception_Class ,
192- ue_header : * mut uw:: _Unwind_Exception ,
193- context : * mut uw:: _Unwind_Context )
194- -> uw:: _Unwind_Reason_Code {
195- rust_eh_personality ( version, actions, exception_class, ue_header, context)
196- }
197172}
198173
199- // ARM EHABI uses a slightly different personality routine signature,
200- // but otherwise works the same.
174+ // ARM EHABI personality routine.
175+ // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf
201176#[ cfg( all( target_arch = "arm" , not( target_os = "ios" ) ) ) ]
202- pub mod eabi {
203- use unwind as uw;
204- use libc:: c_int;
177+ #[ lang = "eh_personality" ]
178+ #[ no_mangle]
179+ unsafe extern "C" fn rust_eh_personality ( state : uw:: _Unwind_State ,
180+ exception_object : * mut uw:: _Unwind_Exception ,
181+ context : * mut uw:: _Unwind_Context )
182+ -> uw:: _Unwind_Reason_Code {
183+ let state = state as c_int ;
184+ let action = state & uw:: _US_ACTION_MASK as c_int ;
185+ let search_phase = if action == uw:: _US_VIRTUAL_UNWIND_FRAME as c_int {
186+ // Backtraces on ARM will call the personality routine with
187+ // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases
188+ // we want to continue unwinding the stack, otherwise all our backtraces
189+ // would end at __rust_try
190+ if state & uw:: _US_FORCE_UNWIND as c_int != 0 {
191+ return continue_unwind ( exception_object, context)
192+ }
193+ true
194+ } else if action == uw:: _US_UNWIND_FRAME_STARTING as c_int {
195+ false
196+ } else if action == uw:: _US_UNWIND_FRAME_RESUME as c_int {
197+ return continue_unwind ( exception_object, context) ;
198+ } else {
199+ return uw:: _URC_FAILURE;
200+ } ;
205201
206- extern "C" {
207- fn __gcc_personality_v0 ( state : uw:: _Unwind_State ,
208- ue_header : * mut uw:: _Unwind_Exception ,
209- context : * mut uw:: _Unwind_Context )
210- -> uw:: _Unwind_Reason_Code ;
211- }
202+ // The DWARF unwinder assumes that _Unwind_Context holds things like the function
203+ // and LSDA pointers, however ARM EHABI places them into the exception object.
204+ // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which
205+ // take only the context pointer, GCC personality routines stash a pointer to exception_object
206+ // in the context, using location reserved for ARM's "scratch register" (r12).
207+ uw:: _Unwind_SetGR ( context, uw:: UNWIND_POINTER_REG , exception_object as uw:: _Unwind_Ptr ) ;
208+ // ...A more principled approach would be to provide the full definition of ARM's
209+ // _Unwind_Context in our libunwind bindings and fetch the required data from there directly,
210+ // bypassing DWARF compatibility functions.
212211
213- #[ lang = "eh_personality" ]
214- #[ no_mangle]
215- extern "C" fn rust_eh_personality ( state : uw:: _Unwind_State ,
216- ue_header : * mut uw:: _Unwind_Exception ,
217- context : * mut uw:: _Unwind_Context )
218- -> uw:: _Unwind_Reason_Code {
219- unsafe { __gcc_personality_v0 ( state, ue_header, context) }
212+ let eh_action = find_eh_action ( context) ;
213+ if search_phase {
214+ match eh_action {
215+ EHAction :: None |
216+ EHAction :: Cleanup ( _) => return continue_unwind ( exception_object, context) ,
217+ EHAction :: Catch ( _) => return uw:: _URC_HANDLER_FOUND,
218+ EHAction :: Terminate => return uw:: _URC_FAILURE,
219+ }
220+ } else {
221+ match eh_action {
222+ EHAction :: None => return continue_unwind ( exception_object, context) ,
223+ EHAction :: Cleanup ( lpad) | EHAction :: Catch ( lpad) => {
224+ uw:: _Unwind_SetGR ( context, UNWIND_DATA_REG . 0 , exception_object as uintptr_t ) ;
225+ uw:: _Unwind_SetGR ( context, UNWIND_DATA_REG . 1 , 0 ) ;
226+ uw:: _Unwind_SetIP ( context, lpad) ;
227+ return uw:: _URC_INSTALL_CONTEXT;
228+ }
229+ EHAction :: Terminate => return uw:: _URC_FAILURE,
230+ }
220231 }
221232
222- #[ lang = "eh_personality_catch" ]
223- #[ no_mangle]
224- pub extern "C" fn rust_eh_personality_catch ( state : uw:: _Unwind_State ,
225- ue_header : * mut uw:: _Unwind_Exception ,
226- context : * mut uw:: _Unwind_Context )
227- -> uw:: _Unwind_Reason_Code {
228- // Backtraces on ARM will call the personality routine with
229- // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases
230- // we want to continue unwinding the stack, otherwise all our backtraces
231- // would end at __rust_try.
232- if ( state as c_int & uw:: _US_ACTION_MASK as c_int ) ==
233- uw:: _US_VIRTUAL_UNWIND_FRAME as c_int &&
234- ( state as c_int & uw:: _US_FORCE_UNWIND as c_int ) == 0 {
235- // search phase
236- uw:: _URC_HANDLER_FOUND // catch!
233+ // On ARM EHABI the personality routine is responsible for actually
234+ // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
235+ unsafe fn continue_unwind ( exception_object : * mut uw:: _Unwind_Exception ,
236+ context : * mut uw:: _Unwind_Context )
237+ -> uw:: _Unwind_Reason_Code {
238+ if __gnu_unwind_frame ( exception_object, context) == uw:: _URC_NO_REASON {
239+ uw:: _URC_CONTINUE_UNWIND
237240 } else {
238- // cleanup phase
239- unsafe { __gcc_personality_v0 ( state, ue_header, context) }
241+ uw:: _URC_FAILURE
240242 }
241243 }
244+ // defined in libgcc
245+ extern "C" {
246+ fn __gnu_unwind_frame ( exception_object : * mut uw:: _Unwind_Exception ,
247+ context : * mut uw:: _Unwind_Context )
248+ -> uw:: _Unwind_Reason_Code ;
249+ }
250+ }
251+
252+ unsafe fn find_eh_action ( context : * mut uw:: _Unwind_Context ) -> EHAction {
253+ let lsda = uw:: _Unwind_GetLanguageSpecificData ( context) as * const u8 ;
254+ let mut ip_before_instr: c_int = 0 ;
255+ let ip = uw:: _Unwind_GetIPInfo ( context, & mut ip_before_instr) ;
256+ let eh_context = EHContext {
257+ // The return address points 1 byte past the call instruction,
258+ // which could be in the next IP range in LSDA range table.
259+ ip : if ip_before_instr != 0 { ip } else { ip - 1 } ,
260+ func_start : uw:: _Unwind_GetRegionStart ( context) ,
261+ get_text_start : & || uw:: _Unwind_GetTextRelBase ( context) ,
262+ get_data_start : & || uw:: _Unwind_GetDataRelBase ( context) ,
263+ } ;
264+ eh:: find_eh_action ( lsda, & eh_context)
265+ }
266+
267+ // *** Delete after a new snapshot ***
268+ #[ cfg( all( stage0, any( target_os = "ios" , not( target_arch = "arm" ) ) ) ) ]
269+ #[ lang = "eh_personality_catch" ]
270+ #[ no_mangle]
271+ pub unsafe extern "C" fn rust_eh_personality_catch ( version : c_int ,
272+ actions : uw:: _Unwind_Action ,
273+ exception_class : uw:: _Unwind_Exception_Class ,
274+ ue_header : * mut uw:: _Unwind_Exception ,
275+ context : * mut uw:: _Unwind_Context )
276+ -> uw:: _Unwind_Reason_Code {
277+ rust_eh_personality ( version, actions, exception_class, ue_header, context)
278+ }
279+
280+ // *** Delete after a new snapshot ***
281+ #[ cfg( all( stage0, target_arch = "arm" , not( target_os = "ios" ) ) ) ]
282+ #[ lang = "eh_personality_catch" ]
283+ #[ no_mangle]
284+ pub unsafe extern "C" fn rust_eh_personality_catch ( state : uw:: _Unwind_State ,
285+ ue_header : * mut uw:: _Unwind_Exception ,
286+ context : * mut uw:: _Unwind_Context )
287+ -> uw:: _Unwind_Reason_Code {
288+ rust_eh_personality ( state, ue_header, context)
242289}
243290
244291// See docs in the `unwind` module.
0 commit comments