Skip to content

Commit 2ad98a0

Browse files
authored
Auto merge of #35032 - vadimcn:rusty-ehabi, r=alexcrichton
Implement ARM personality routine in Rust. Remove the `eh_personality_catch` lang item. Use a simplified version of `cfg_if!` in libunwind. Closes #34786
2 parents f164cf5 + 6cef93d commit 2ad98a0

File tree

5 files changed

+314
-270
lines changed

5 files changed

+314
-270
lines changed

Diff for: src/libpanic_unwind/gcc.rs

+159-112
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ use core::ptr;
6161
use alloc::boxed::Box;
6262

6363
use unwind as uw;
64+
use libc::{c_int, uintptr_t};
65+
use dwarf::eh::{self, EHContext, EHAction};
6466

6567
#[repr(C)]
6668
struct 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.

Diff for: src/libpanic_unwind/seh64_gnu.rs

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send> {
8181
// This is considered acceptable, because the behavior of throwing exceptions
8282
// through a C ABI boundary is undefined.
8383

84+
// *** Delete after a new snapshot ***
8485
#[cfg(stage0)]
8586
#[lang = "eh_personality_catch"]
8687
#[cfg(not(test))]

Diff for: src/librustc/middle/lang_items.rs

-1
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,6 @@ language_item_table! {
359359
StartFnLangItem, "start", start_fn;
360360

361361
EhPersonalityLangItem, "eh_personality", eh_personality;
362-
EhPersonalityCatchLangItem, "eh_personality_catch", eh_personality_catch;
363362
EhUnwindResumeLangItem, "eh_unwind_resume", eh_unwind_resume;
364363
MSVCTryFilterLangItem, "msvc_try_filter", msvc_try_filter;
365364

Diff for: src/librustc_trans/intrinsic.rs

+1-18
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use rustc::ty::subst;
1919
use rustc::ty::subst::FnSpace;
2020
use abi::{Abi, FnType};
2121
use adt;
22-
use attributes;
2322
use base::*;
2423
use build::*;
2524
use callee::{self, Callee};
@@ -37,7 +36,6 @@ use machine;
3736
use type_::Type;
3837
use rustc::ty::{self, Ty};
3938
use Disr;
40-
use rustc::ty::subst::Substs;
4139
use rustc::hir;
4240
use syntax::ast;
4341
use syntax::ptr::P;
@@ -1173,7 +1171,6 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
11731171
dloc: DebugLoc) -> Block<'blk, 'tcx> {
11741172
let llfn = get_rust_try_fn(bcx.fcx, &mut |bcx| {
11751173
let ccx = bcx.ccx();
1176-
let tcx = ccx.tcx();
11771174
let dloc = DebugLoc::None;
11781175

11791176
// Translates the shims described above:
@@ -1193,20 +1190,6 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
11931190
// expected to be `*mut *mut u8` for this to actually work, but that's
11941191
// managed by the standard library.
11951192

1196-
attributes::emit_uwtable(bcx.fcx.llfn, true);
1197-
let target = &bcx.sess().target.target;
1198-
let catch_pers = if target.arch == "arm" && target.target_os != "ios" {
1199-
// Only ARM still uses a separate catch personality (for now)
1200-
match tcx.lang_items.eh_personality_catch() {
1201-
Some(did) => {
1202-
Callee::def(ccx, did, tcx.mk_substs(Substs::empty())).reify(ccx).val
1203-
}
1204-
None => bug!("eh_personality_catch not defined"),
1205-
}
1206-
} else {
1207-
bcx.fcx.eh_personality()
1208-
};
1209-
12101193
let then = bcx.fcx.new_temp_block("then");
12111194
let catch = bcx.fcx.new_temp_block("catch");
12121195

@@ -1224,7 +1207,7 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
12241207
// rust_try ignores the selector.
12251208
let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)],
12261209
false);
1227-
let vals = LandingPad(catch, lpad_ty, catch_pers, 1);
1210+
let vals = LandingPad(catch, lpad_ty, bcx.fcx.eh_personality(), 1);
12281211
AddClause(catch, vals, C_null(Type::i8p(ccx)));
12291212
let ptr = ExtractValue(catch, vals, 0);
12301213
Store(catch, ptr, BitCast(catch, local_ptr, Type::i8p(ccx).ptr_to()));

0 commit comments

Comments
 (0)