Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 91f128b

Browse files
committedAug 28, 2022
Auto merge of #92845 - Amanieu:std_personality, r=Mark-Simulacrum
Move EH personality functions to std These were previously in the panic_unwind crate with dummy stubs in the panic_abort crate. However it turns out that this is insufficient: we still need a proper personality function even with -C panic=abort to handle the following cases: 1) `extern "C-unwind"` still needs to catch foreign exceptions with -C panic=abort to turn them into aborts. This requires landing pads and a personality function. 2) ARM EHABI uses the personality function when creating backtraces. The dummy personality function in panic_abort was causing backtrace generation to get stuck in a loop since the personality function is responsible for advancing the unwind state to the next frame. Fixes #41004
2 parents 1e978a3 + a7e4794 commit 91f128b

File tree

16 files changed

+379
-339
lines changed

16 files changed

+379
-339
lines changed
 

‎compiler/rustc_codegen_ssa/src/back/symbol_export.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -103,18 +103,14 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap<
103103
}
104104
})
105105
.map(|def_id| {
106-
let (export_level, used) = if special_runtime_crate {
107-
let name = tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())).name;
108-
// We won't link right if these symbols are stripped during LTO.
109-
let used = match name {
110-
"rust_eh_personality"
111-
| "rust_eh_register_frames"
112-
| "rust_eh_unregister_frames" => true,
113-
_ => false,
114-
};
115-
(SymbolExportLevel::Rust, used)
106+
// We won't link right if this symbol is stripped during LTO.
107+
let name = tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())).name;
108+
let used = name == "rust_eh_personality";
109+
110+
let export_level = if special_runtime_crate {
111+
SymbolExportLevel::Rust
116112
} else {
117-
(symbol_export_level(tcx, def_id.to_def_id()), false)
113+
symbol_export_level(tcx, def_id.to_def_id())
118114
};
119115
let codegen_attrs = tcx.codegen_fn_attrs(def_id.to_def_id());
120116
debug!(

‎library/panic_abort/src/lib.rs

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -113,27 +113,11 @@ pub unsafe fn __rust_start_panic(_payload: *mut &mut dyn BoxMeUp) -> u32 {
113113
// binaries, but it should never be called as we don't link in an unwinding
114114
// runtime at all.
115115
pub mod personalities {
116-
#[rustc_std_internal_symbol]
117-
#[cfg(not(any(
118-
all(target_family = "wasm", not(target_os = "emscripten")),
119-
all(target_os = "windows", target_env = "gnu", target_arch = "x86_64",),
120-
)))]
121-
pub extern "C" fn rust_eh_personality() {}
122-
123-
// On x86_64-pc-windows-gnu we use our own personality function that needs
124-
// to return `ExceptionContinueSearch` as we're passing on all our frames.
125-
#[rustc_std_internal_symbol]
126-
#[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86_64"))]
127-
pub extern "C" fn rust_eh_personality(
128-
_record: usize,
129-
_frame: usize,
130-
_context: usize,
131-
_dispatcher: usize,
132-
) -> u32 {
133-
1 // `ExceptionContinueSearch`
134-
}
116+
// In the past this module used to contain stubs for the personality
117+
// functions of various platforms, but these where removed when personality
118+
// functions were moved to std.
135119

136-
// Similar to above, this corresponds to the `eh_catch_typeinfo` lang item
120+
// This corresponds to the `eh_catch_typeinfo` lang item
137121
// that's only used on Emscripten currently.
138122
//
139123
// Since panics don't generate exceptions and foreign exceptions are
@@ -143,13 +127,4 @@ pub mod personalities {
143127
#[allow(non_upper_case_globals)]
144128
#[cfg(target_os = "emscripten")]
145129
static rust_eh_catch_typeinfo: [usize; 2] = [0; 2];
146-
147-
// These two are called by our startup objects on i686-pc-windows-gnu, but
148-
// they don't need to do anything so the bodies are nops.
149-
#[rustc_std_internal_symbol]
150-
#[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))]
151-
pub extern "C" fn rust_eh_register_frames() {}
152-
#[rustc_std_internal_symbol]
153-
#[cfg(all(target_os = "windows", target_env = "gnu", target_arch = "x86"))]
154-
pub extern "C" fn rust_eh_unregister_frames() {}
155130
}

‎library/panic_unwind/src/emcc.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use core::intrinsics;
1212
use core::mem;
1313
use core::ptr;
1414
use core::sync::atomic::{AtomicBool, Ordering};
15-
use libc::{self, c_int};
1615
use unwind as uw;
1716

1817
// This matches the layout of std::type_info in C++
@@ -105,21 +104,6 @@ extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> *mut libc::c_void {
105104
}
106105
}
107106

108-
// This is required by the compiler to exist (e.g., it's a lang item), but it's
109-
// never actually called by the compiler. Emscripten EH doesn't use a
110-
// personality function at all, it instead uses __cxa_find_matching_catch.
111-
// Wasm error handling would use __gxx_personality_wasm0.
112-
#[lang = "eh_personality"]
113-
unsafe extern "C" fn rust_eh_personality(
114-
_version: c_int,
115-
_actions: uw::_Unwind_Action,
116-
_exception_class: uw::_Unwind_Exception_Class,
117-
_exception_object: *mut uw::_Unwind_Exception,
118-
_context: *mut uw::_Unwind_Context,
119-
) -> uw::_Unwind_Reason_Code {
120-
core::intrinsics::abort()
121-
}
122-
123107
extern "C" {
124108
fn __cxa_allocate_exception(thrown_size: libc::size_t) -> *mut libc::c_void;
125109
fn __cxa_begin_catch(thrown_exception: *mut libc::c_void) -> *mut libc::c_void;

‎library/panic_unwind/src/gcc.rs

Lines changed: 0 additions & 262 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@
3939
use alloc::boxed::Box;
4040
use core::any::Any;
4141

42-
use crate::dwarf::eh::{self, EHAction, EHContext};
43-
use libc::{c_int, uintptr_t};
4442
use unwind as uw;
4543

4644
#[repr(C)]
@@ -89,263 +87,3 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class {
8987
// M O Z \0 R U S T -- vendor, language
9088
0x4d4f5a_00_52555354
9189
}
92-
93-
// Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister()
94-
// and TargetLowering::getExceptionSelectorRegister() for each architecture,
95-
// then mapped to DWARF register numbers via register definition tables
96-
// (typically <arch>RegisterInfo.td, search for "DwarfRegNum").
97-
// See also https://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register.
98-
99-
#[cfg(target_arch = "x86")]
100-
const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX
101-
102-
#[cfg(target_arch = "x86_64")]
103-
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
104-
105-
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
106-
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 / X0, X1
107-
108-
#[cfg(target_arch = "m68k")]
109-
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // D0, D1
110-
111-
#[cfg(any(target_arch = "mips", target_arch = "mips64"))]
112-
const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1
113-
114-
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
115-
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4 / X3, X4
116-
117-
#[cfg(target_arch = "s390x")]
118-
const UNWIND_DATA_REG: (i32, i32) = (6, 7); // R6, R7
119-
120-
#[cfg(any(target_arch = "sparc", target_arch = "sparc64"))]
121-
const UNWIND_DATA_REG: (i32, i32) = (24, 25); // I0, I1
122-
123-
#[cfg(target_arch = "hexagon")]
124-
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1
125-
126-
#[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))]
127-
const UNWIND_DATA_REG: (i32, i32) = (10, 11); // x10, x11
128-
129-
// The following code is based on GCC's C and C++ personality routines. For reference, see:
130-
// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
131-
// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
132-
133-
cfg_if::cfg_if! {
134-
if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "watchos"), not(target_os = "netbsd")))] {
135-
// ARM EHABI personality routine.
136-
// https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf
137-
//
138-
// iOS uses the default routine instead since it uses SjLj unwinding.
139-
#[lang = "eh_personality"]
140-
unsafe extern "C" fn rust_eh_personality(state: uw::_Unwind_State,
141-
exception_object: *mut uw::_Unwind_Exception,
142-
context: *mut uw::_Unwind_Context)
143-
-> uw::_Unwind_Reason_Code {
144-
let state = state as c_int;
145-
let action = state & uw::_US_ACTION_MASK as c_int;
146-
let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int {
147-
// Backtraces on ARM will call the personality routine with
148-
// state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases
149-
// we want to continue unwinding the stack, otherwise all our backtraces
150-
// would end at __rust_try
151-
if state & uw::_US_FORCE_UNWIND as c_int != 0 {
152-
return continue_unwind(exception_object, context);
153-
}
154-
true
155-
} else if action == uw::_US_UNWIND_FRAME_STARTING as c_int {
156-
false
157-
} else if action == uw::_US_UNWIND_FRAME_RESUME as c_int {
158-
return continue_unwind(exception_object, context);
159-
} else {
160-
return uw::_URC_FAILURE;
161-
};
162-
163-
// The DWARF unwinder assumes that _Unwind_Context holds things like the function
164-
// and LSDA pointers, however ARM EHABI places them into the exception object.
165-
// To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which
166-
// take only the context pointer, GCC personality routines stash a pointer to
167-
// exception_object in the context, using location reserved for ARM's
168-
// "scratch register" (r12).
169-
uw::_Unwind_SetGR(context,
170-
uw::UNWIND_POINTER_REG,
171-
exception_object as uw::_Unwind_Ptr);
172-
// ...A more principled approach would be to provide the full definition of ARM's
173-
// _Unwind_Context in our libunwind bindings and fetch the required data from there
174-
// directly, bypassing DWARF compatibility functions.
175-
176-
let eh_action = match find_eh_action(context) {
177-
Ok(action) => action,
178-
Err(_) => return uw::_URC_FAILURE,
179-
};
180-
if search_phase {
181-
match eh_action {
182-
EHAction::None |
183-
EHAction::Cleanup(_) => return continue_unwind(exception_object, context),
184-
EHAction::Catch(_) => {
185-
// EHABI requires the personality routine to update the
186-
// SP value in the barrier cache of the exception object.
187-
(*exception_object).private[5] =
188-
uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG);
189-
return uw::_URC_HANDLER_FOUND;
190-
}
191-
EHAction::Terminate => return uw::_URC_FAILURE,
192-
}
193-
} else {
194-
match eh_action {
195-
EHAction::None => return continue_unwind(exception_object, context),
196-
EHAction::Cleanup(lpad) |
197-
EHAction::Catch(lpad) => {
198-
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
199-
exception_object as uintptr_t);
200-
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
201-
uw::_Unwind_SetIP(context, lpad);
202-
return uw::_URC_INSTALL_CONTEXT;
203-
}
204-
EHAction::Terminate => return uw::_URC_FAILURE,
205-
}
206-
}
207-
208-
// On ARM EHABI the personality routine is responsible for actually
209-
// unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
210-
unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception,
211-
context: *mut uw::_Unwind_Context)
212-
-> uw::_Unwind_Reason_Code {
213-
if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON {
214-
uw::_URC_CONTINUE_UNWIND
215-
} else {
216-
uw::_URC_FAILURE
217-
}
218-
}
219-
// defined in libgcc
220-
extern "C" {
221-
fn __gnu_unwind_frame(exception_object: *mut uw::_Unwind_Exception,
222-
context: *mut uw::_Unwind_Context)
223-
-> uw::_Unwind_Reason_Code;
224-
}
225-
}
226-
} else {
227-
// Default personality routine, which is used directly on most targets
228-
// and indirectly on Windows x86_64 via SEH.
229-
unsafe extern "C" fn rust_eh_personality_impl(version: c_int,
230-
actions: uw::_Unwind_Action,
231-
_exception_class: uw::_Unwind_Exception_Class,
232-
exception_object: *mut uw::_Unwind_Exception,
233-
context: *mut uw::_Unwind_Context)
234-
-> uw::_Unwind_Reason_Code {
235-
if version != 1 {
236-
return uw::_URC_FATAL_PHASE1_ERROR;
237-
}
238-
let eh_action = match find_eh_action(context) {
239-
Ok(action) => action,
240-
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
241-
};
242-
if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 {
243-
match eh_action {
244-
EHAction::None |
245-
EHAction::Cleanup(_) => uw::_URC_CONTINUE_UNWIND,
246-
EHAction::Catch(_) => uw::_URC_HANDLER_FOUND,
247-
EHAction::Terminate => uw::_URC_FATAL_PHASE1_ERROR,
248-
}
249-
} else {
250-
match eh_action {
251-
EHAction::None => uw::_URC_CONTINUE_UNWIND,
252-
EHAction::Cleanup(lpad) |
253-
EHAction::Catch(lpad) => {
254-
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0,
255-
exception_object as uintptr_t);
256-
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
257-
uw::_Unwind_SetIP(context, lpad);
258-
uw::_URC_INSTALL_CONTEXT
259-
}
260-
EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR,
261-
}
262-
}
263-
}
264-
265-
cfg_if::cfg_if! {
266-
if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] {
267-
// On x86_64 MinGW targets, the unwinding mechanism is SEH however the unwind
268-
// handler data (aka LSDA) uses GCC-compatible encoding.
269-
#[lang = "eh_personality"]
270-
#[allow(nonstandard_style)]
271-
unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut uw::EXCEPTION_RECORD,
272-
establisherFrame: uw::LPVOID,
273-
contextRecord: *mut uw::CONTEXT,
274-
dispatcherContext: *mut uw::DISPATCHER_CONTEXT)
275-
-> uw::EXCEPTION_DISPOSITION {
276-
uw::_GCC_specific_handler(exceptionRecord,
277-
establisherFrame,
278-
contextRecord,
279-
dispatcherContext,
280-
rust_eh_personality_impl)
281-
}
282-
} else {
283-
// The personality routine for most of our targets.
284-
#[lang = "eh_personality"]
285-
unsafe extern "C" fn rust_eh_personality(version: c_int,
286-
actions: uw::_Unwind_Action,
287-
exception_class: uw::_Unwind_Exception_Class,
288-
exception_object: *mut uw::_Unwind_Exception,
289-
context: *mut uw::_Unwind_Context)
290-
-> uw::_Unwind_Reason_Code {
291-
rust_eh_personality_impl(version,
292-
actions,
293-
exception_class,
294-
exception_object,
295-
context)
296-
}
297-
}
298-
}
299-
}
300-
}
301-
302-
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction, ()> {
303-
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
304-
let mut ip_before_instr: c_int = 0;
305-
let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr);
306-
let eh_context = EHContext {
307-
// The return address points 1 byte past the call instruction,
308-
// which could be in the next IP range in LSDA range table.
309-
//
310-
// `ip = -1` has special meaning, so use wrapping sub to allow for that
311-
ip: if ip_before_instr != 0 { ip } else { ip.wrapping_sub(1) },
312-
func_start: uw::_Unwind_GetRegionStart(context),
313-
get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
314-
get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
315-
};
316-
eh::find_eh_action(lsda, &eh_context)
317-
}
318-
319-
// Frame unwind info registration
320-
//
321-
// Each module's image contains a frame unwind info section (usually
322-
// ".eh_frame"). When a module is loaded/unloaded into the process, the
323-
// unwinder must be informed about the location of this section in memory. The
324-
// methods of achieving that vary by the platform. On some (e.g., Linux), the
325-
// unwinder can discover unwind info sections on its own (by dynamically
326-
// enumerating currently loaded modules via the dl_iterate_phdr() API and
327-
// finding their ".eh_frame" sections); Others, like Windows, require modules
328-
// to actively register their unwind info sections via unwinder API.
329-
//
330-
// This module defines two symbols which are referenced and called from
331-
// rsbegin.rs to register our information with the GCC runtime. The
332-
// implementation of stack unwinding is (for now) deferred to libgcc_eh, however
333-
// Rust crates use these Rust-specific entry points to avoid potential clashes
334-
// with any GCC runtime.
335-
#[cfg(all(target_os = "windows", target_arch = "x86", target_env = "gnu"))]
336-
pub mod eh_frame_registry {
337-
extern "C" {
338-
fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8);
339-
fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8);
340-
}
341-
342-
#[rustc_std_internal_symbol]
343-
pub unsafe extern "C" fn rust_eh_register_frames(eh_frame_begin: *const u8, object: *mut u8) {
344-
__register_frame_info(eh_frame_begin, object);
345-
}
346-
347-
#[rustc_std_internal_symbol]
348-
pub unsafe extern "C" fn rust_eh_unregister_frames(eh_frame_begin: *const u8, object: *mut u8) {
349-
__deregister_frame_info(eh_frame_begin, object);
350-
}
351-
}

‎library/panic_unwind/src/lib.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,6 @@ cfg_if::cfg_if! {
5252
all(target_family = "unix", not(target_os = "espidf")),
5353
all(target_vendor = "fortanix", target_env = "sgx"),
5454
))] {
55-
// Rust runtime's startup objects depend on these symbols, so make them public.
56-
#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
57-
pub use real_imp::eh_frame_registry::*;
5855
#[path = "gcc.rs"]
5956
mod real_imp;
6057
} else {
@@ -92,8 +89,6 @@ extern "C" {
9289
fn __rust_foreign_exception() -> !;
9390
}
9491

95-
mod dwarf;
96-
9792
#[rustc_std_internal_symbol]
9893
#[allow(improper_ctypes_definitions)]
9994
pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static) {

‎library/panic_unwind/src/seh.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -326,13 +326,3 @@ pub unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send> {
326326
exception.data.take().unwrap()
327327
}
328328
}
329-
330-
// This is required by the compiler to exist (e.g., it's a lang item), but
331-
// it's never actually called by the compiler because __C_specific_handler
332-
// or _except_handler3 is the personality function that is always used.
333-
// Hence this is just an aborting stub.
334-
#[lang = "eh_personality"]
335-
#[cfg(not(test))]
336-
fn rust_eh_personality() {
337-
core::intrinsics::abort()
338-
}

‎library/rtstartup/rsbegin.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
3535
drop_in_place(to_drop);
3636
}
3737

38+
// Frame unwind info registration
39+
//
40+
// Each module's image contains a frame unwind info section (usually
41+
// ".eh_frame"). When a module is loaded/unloaded into the process, the
42+
// unwinder must be informed about the location of this section in memory. The
43+
// methods of achieving that vary by the platform. On some (e.g., Linux), the
44+
// unwinder can discover unwind info sections on its own (by dynamically
45+
// enumerating currently loaded modules via the dl_iterate_phdr() API and
46+
// finding their ".eh_frame" sections); Others, like Windows, require modules
47+
// to actively register their unwind info sections via unwinder API.
3848
#[cfg(all(target_os = "windows", target_arch = "x86", target_env = "gnu"))]
3949
pub mod eh_frames {
4050
#[no_mangle]
@@ -62,20 +72,19 @@ pub mod eh_frames {
6272
}
6373

6474
// Unwind info registration/deregistration routines.
65-
// See the docs of libpanic_unwind.
6675
extern "C" {
67-
fn rust_eh_register_frames(eh_frame_begin: *const u8, object: *mut u8);
68-
fn rust_eh_unregister_frames(eh_frame_begin: *const u8, object: *mut u8);
76+
fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8);
77+
fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8);
6978
}
7079

7180
unsafe extern "C" fn init() {
7281
// register unwind info on module startup
73-
rust_eh_register_frames(&__EH_FRAME_BEGIN__ as *const u8, &mut OBJ as *mut _ as *mut u8);
82+
__register_frame_info(&__EH_FRAME_BEGIN__ as *const u8, &mut OBJ as *mut _ as *mut u8);
7483
}
7584

7685
unsafe extern "C" fn uninit() {
7786
// unregister on shutdown
78-
rust_eh_unregister_frames(&__EH_FRAME_BEGIN__ as *const u8, &mut OBJ as *mut _ as *mut u8);
87+
__deregister_frame_info(&__EH_FRAME_BEGIN__ as *const u8, &mut OBJ as *mut _ as *mut u8);
7988
}
8089

8190
// MinGW-specific init/uninit routine registration

‎library/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ pub mod alloc;
594594

595595
// Private support modules
596596
mod panicking;
597+
mod personality;
597598

598599
#[path = "../../backtrace/src/lib.rs"]
599600
#[allow(dead_code, unused_attributes)]

‎library/std/src/personality.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//! This module contains the implementation of the `eh_personality` lang item.
2+
//!
3+
//! The actual implementation is heavily dependent on the target since Rust
4+
//! tries to use the native stack unwinding mechanism whenever possible.
5+
//!
6+
//! This personality function is still required with `-C panic=abort` because
7+
//! it is used to catch foreign exceptions from `extern "C-unwind"` and turn
8+
//! them into aborts.
9+
//!
10+
//! Additionally, ARM EHABI uses the personality function when generating
11+
//! backtraces.
12+
13+
mod dwarf;
14+
15+
#[cfg(not(test))]
16+
cfg_if::cfg_if! {
17+
if #[cfg(target_os = "emscripten")] {
18+
mod emcc;
19+
} else if #[cfg(target_env = "msvc")] {
20+
// This is required by the compiler to exist (e.g., it's a lang item),
21+
// but it's never actually called by the compiler because
22+
// _CxxFrameHandler3 is the personality function that is always used.
23+
// Hence this is just an aborting stub.
24+
#[lang = "eh_personality"]
25+
fn rust_eh_personality() {
26+
core::intrinsics::abort()
27+
}
28+
} else if #[cfg(any(
29+
all(target_family = "windows", target_env = "gnu"),
30+
target_os = "psp",
31+
target_os = "solid_asp3",
32+
all(target_family = "unix", not(target_os = "espidf")),
33+
all(target_vendor = "fortanix", target_env = "sgx"),
34+
))] {
35+
mod gcc;
36+
} else {
37+
// Targets that don't support unwinding.
38+
// - family=wasm
39+
// - os=none ("bare metal" targets)
40+
// - os=uefi
41+
// - os=espidf
42+
// - os=hermit
43+
// - nvptx64-nvidia-cuda
44+
// - arch=avr
45+
}
46+
}

‎library/panic_unwind/src/dwarf/eh.rs renamed to ‎library/std/src/personality/dwarf/eh.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#![allow(non_upper_case_globals)]
1212
#![allow(unused)]
1313

14-
use crate::dwarf::DwarfReader;
14+
use super::DwarfReader;
1515
use core::mem;
1616

1717
pub const DW_EH_PE_omit: u8 = 0xFF;

‎library/std/src/personality/emcc.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//! On Emscripten Rust panics are wrapped in C++ exceptions, so we just forward
2+
//! to `__gxx_personality_v0` which is provided by Emscripten.
3+
4+
use libc::c_int;
5+
use unwind as uw;
6+
7+
// This is required by the compiler to exist (e.g., it's a lang item), but it's
8+
// never actually called by the compiler. Emscripten EH doesn't use a
9+
// personality function at all, it instead uses __cxa_find_matching_catch.
10+
// Wasm error handling would use __gxx_personality_wasm0.
11+
#[lang = "eh_personality"]
12+
unsafe extern "C" fn rust_eh_personality(
13+
_version: c_int,
14+
_actions: uw::_Unwind_Action,
15+
_exception_class: uw::_Unwind_Exception_Class,
16+
_exception_object: *mut uw::_Unwind_Exception,
17+
_context: *mut uw::_Unwind_Context,
18+
) -> uw::_Unwind_Reason_Code {
19+
core::intrinsics::abort()
20+
}

‎library/std/src/personality/gcc.rs

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
//! Implementation of panics backed by libgcc/libunwind (in some form).
2+
//!
3+
//! For background on exception handling and stack unwinding please see
4+
//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
5+
//! documents linked from it.
6+
//! These are also good reads:
7+
//! * <https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html>
8+
//! * <https://monoinfinito.wordpress.com/series/exception-handling-in-c/>
9+
//! * <https://www.airs.com/blog/index.php?s=exception+frames>
10+
//!
11+
//! ## A brief summary
12+
//!
13+
//! Exception handling happens in two phases: a search phase and a cleanup
14+
//! phase.
15+
//!
16+
//! In both phases the unwinder walks stack frames from top to bottom using
17+
//! information from the stack frame unwind sections of the current process's
18+
//! modules ("module" here refers to an OS module, i.e., an executable or a
19+
//! dynamic library).
20+
//!
21+
//! For each stack frame, it invokes the associated "personality routine", whose
22+
//! address is also stored in the unwind info section.
23+
//!
24+
//! In the search phase, the job of a personality routine is to examine
25+
//! exception object being thrown, and to decide whether it should be caught at
26+
//! that stack frame. Once the handler frame has been identified, cleanup phase
27+
//! begins.
28+
//!
29+
//! In the cleanup phase, the unwinder invokes each personality routine again.
30+
//! This time it decides which (if any) cleanup code needs to be run for
31+
//! the current stack frame. If so, the control is transferred to a special
32+
//! branch in the function body, the "landing pad", which invokes destructors,
33+
//! frees memory, etc. At the end of the landing pad, control is transferred
34+
//! back to the unwinder and unwinding resumes.
35+
//!
36+
//! Once stack has been unwound down to the handler frame level, unwinding stops
37+
//! and the last personality routine transfers control to the catch block.
38+
39+
use super::dwarf::eh::{self, EHAction, EHContext};
40+
use libc::{c_int, uintptr_t};
41+
use unwind as uw;
42+
43+
// Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister()
44+
// and TargetLowering::getExceptionSelectorRegister() for each architecture,
45+
// then mapped to DWARF register numbers via register definition tables
46+
// (typically <arch>RegisterInfo.td, search for "DwarfRegNum").
47+
// See also https://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register.
48+
49+
#[cfg(target_arch = "x86")]
50+
const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX
51+
52+
#[cfg(target_arch = "x86_64")]
53+
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
54+
55+
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
56+
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 / X0, X1
57+
58+
#[cfg(target_arch = "m68k")]
59+
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // D0, D1
60+
61+
#[cfg(any(target_arch = "mips", target_arch = "mips64"))]
62+
const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1
63+
64+
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
65+
const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4 / X3, X4
66+
67+
#[cfg(target_arch = "s390x")]
68+
const UNWIND_DATA_REG: (i32, i32) = (6, 7); // R6, R7
69+
70+
#[cfg(any(target_arch = "sparc", target_arch = "sparc64"))]
71+
const UNWIND_DATA_REG: (i32, i32) = (24, 25); // I0, I1
72+
73+
#[cfg(target_arch = "hexagon")]
74+
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1
75+
76+
#[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))]
77+
const UNWIND_DATA_REG: (i32, i32) = (10, 11); // x10, x11
78+
79+
// The following code is based on GCC's C and C++ personality routines. For reference, see:
80+
// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
81+
// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
82+
83+
cfg_if::cfg_if! {
84+
if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "watchos"), not(target_os = "netbsd")))] {
85+
// ARM EHABI personality routine.
86+
// https://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf
87+
//
88+
// iOS uses the default routine instead since it uses SjLj unwinding.
89+
#[lang = "eh_personality"]
90+
unsafe extern "C" fn rust_eh_personality(
91+
state: uw::_Unwind_State,
92+
exception_object: *mut uw::_Unwind_Exception,
93+
context: *mut uw::_Unwind_Context,
94+
) -> uw::_Unwind_Reason_Code {
95+
let state = state as c_int;
96+
let action = state & uw::_US_ACTION_MASK as c_int;
97+
let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int {
98+
// Backtraces on ARM will call the personality routine with
99+
// state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases
100+
// we want to continue unwinding the stack, otherwise all our backtraces
101+
// would end at __rust_try
102+
if state & uw::_US_FORCE_UNWIND as c_int != 0 {
103+
return continue_unwind(exception_object, context);
104+
}
105+
true
106+
} else if action == uw::_US_UNWIND_FRAME_STARTING as c_int {
107+
false
108+
} else if action == uw::_US_UNWIND_FRAME_RESUME as c_int {
109+
return continue_unwind(exception_object, context);
110+
} else {
111+
return uw::_URC_FAILURE;
112+
};
113+
114+
// The DWARF unwinder assumes that _Unwind_Context holds things like the function
115+
// and LSDA pointers, however ARM EHABI places them into the exception object.
116+
// To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which
117+
// take only the context pointer, GCC personality routines stash a pointer to
118+
// exception_object in the context, using location reserved for ARM's
119+
// "scratch register" (r12).
120+
uw::_Unwind_SetGR(context, uw::UNWIND_POINTER_REG, exception_object as uw::_Unwind_Ptr);
121+
// ...A more principled approach would be to provide the full definition of ARM's
122+
// _Unwind_Context in our libunwind bindings and fetch the required data from there
123+
// directly, bypassing DWARF compatibility functions.
124+
125+
let eh_action = match find_eh_action(context) {
126+
Ok(action) => action,
127+
Err(_) => return uw::_URC_FAILURE,
128+
};
129+
if search_phase {
130+
match eh_action {
131+
EHAction::None | EHAction::Cleanup(_) => {
132+
return continue_unwind(exception_object, context);
133+
}
134+
EHAction::Catch(_) => {
135+
// EHABI requires the personality routine to update the
136+
// SP value in the barrier cache of the exception object.
137+
(*exception_object).private[5] =
138+
uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG);
139+
return uw::_URC_HANDLER_FOUND;
140+
}
141+
EHAction::Terminate => return uw::_URC_FAILURE,
142+
}
143+
} else {
144+
match eh_action {
145+
EHAction::None => return continue_unwind(exception_object, context),
146+
EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => {
147+
uw::_Unwind_SetGR(
148+
context,
149+
UNWIND_DATA_REG.0,
150+
exception_object as uintptr_t,
151+
);
152+
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
153+
uw::_Unwind_SetIP(context, lpad);
154+
return uw::_URC_INSTALL_CONTEXT;
155+
}
156+
EHAction::Terminate => return uw::_URC_FAILURE,
157+
}
158+
}
159+
160+
// On ARM EHABI the personality routine is responsible for actually
161+
// unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
162+
unsafe fn continue_unwind(
163+
exception_object: *mut uw::_Unwind_Exception,
164+
context: *mut uw::_Unwind_Context,
165+
) -> uw::_Unwind_Reason_Code {
166+
if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON {
167+
uw::_URC_CONTINUE_UNWIND
168+
} else {
169+
uw::_URC_FAILURE
170+
}
171+
}
172+
// defined in libgcc
173+
extern "C" {
174+
fn __gnu_unwind_frame(
175+
exception_object: *mut uw::_Unwind_Exception,
176+
context: *mut uw::_Unwind_Context,
177+
) -> uw::_Unwind_Reason_Code;
178+
}
179+
}
180+
} else {
181+
// Default personality routine, which is used directly on most targets
182+
// and indirectly on Windows x86_64 via SEH.
183+
unsafe extern "C" fn rust_eh_personality_impl(
184+
version: c_int,
185+
actions: uw::_Unwind_Action,
186+
_exception_class: uw::_Unwind_Exception_Class,
187+
exception_object: *mut uw::_Unwind_Exception,
188+
context: *mut uw::_Unwind_Context,
189+
) -> uw::_Unwind_Reason_Code {
190+
if version != 1 {
191+
return uw::_URC_FATAL_PHASE1_ERROR;
192+
}
193+
let eh_action = match find_eh_action(context) {
194+
Ok(action) => action,
195+
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
196+
};
197+
if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 {
198+
match eh_action {
199+
EHAction::None | EHAction::Cleanup(_) => uw::_URC_CONTINUE_UNWIND,
200+
EHAction::Catch(_) => uw::_URC_HANDLER_FOUND,
201+
EHAction::Terminate => uw::_URC_FATAL_PHASE1_ERROR,
202+
}
203+
} else {
204+
match eh_action {
205+
EHAction::None => uw::_URC_CONTINUE_UNWIND,
206+
EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => {
207+
uw::_Unwind_SetGR(
208+
context,
209+
UNWIND_DATA_REG.0,
210+
exception_object as uintptr_t,
211+
);
212+
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
213+
uw::_Unwind_SetIP(context, lpad);
214+
uw::_URC_INSTALL_CONTEXT
215+
}
216+
EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR,
217+
}
218+
}
219+
}
220+
221+
cfg_if::cfg_if! {
222+
if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] {
223+
// On x86_64 MinGW targets, the unwinding mechanism is SEH however the unwind
224+
// handler data (aka LSDA) uses GCC-compatible encoding.
225+
#[lang = "eh_personality"]
226+
#[allow(nonstandard_style)]
227+
unsafe extern "C" fn rust_eh_personality(
228+
exceptionRecord: *mut uw::EXCEPTION_RECORD,
229+
establisherFrame: uw::LPVOID,
230+
contextRecord: *mut uw::CONTEXT,
231+
dispatcherContext: *mut uw::DISPATCHER_CONTEXT,
232+
) -> uw::EXCEPTION_DISPOSITION {
233+
uw::_GCC_specific_handler(
234+
exceptionRecord,
235+
establisherFrame,
236+
contextRecord,
237+
dispatcherContext,
238+
rust_eh_personality_impl,
239+
)
240+
}
241+
} else {
242+
// The personality routine for most of our targets.
243+
#[lang = "eh_personality"]
244+
unsafe extern "C" fn rust_eh_personality(
245+
version: c_int,
246+
actions: uw::_Unwind_Action,
247+
exception_class: uw::_Unwind_Exception_Class,
248+
exception_object: *mut uw::_Unwind_Exception,
249+
context: *mut uw::_Unwind_Context,
250+
) -> uw::_Unwind_Reason_Code {
251+
rust_eh_personality_impl(
252+
version,
253+
actions,
254+
exception_class,
255+
exception_object,
256+
context,
257+
)
258+
}
259+
}
260+
}
261+
}
262+
}
263+
264+
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction, ()> {
265+
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
266+
let mut ip_before_instr: c_int = 0;
267+
let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr);
268+
let eh_context = EHContext {
269+
// The return address points 1 byte past the call instruction,
270+
// which could be in the next IP range in LSDA range table.
271+
//
272+
// `ip = -1` has special meaning, so use wrapping sub to allow for that
273+
ip: if ip_before_instr != 0 { ip } else { ip.wrapping_sub(1) },
274+
func_start: uw::_Unwind_GetRegionStart(context),
275+
get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
276+
get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
277+
};
278+
eh::find_eh_action(lsda, &eh_context)
279+
}

‎src/test/run-make-fulldeps/issue-69368/a.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,8 @@ extern "C" fn __rust_drop_panic() -> ! {
1919
extern "C" fn __rust_foreign_exception() -> ! {
2020
loop {}
2121
}
22+
23+
#[lang = "eh_personality"]
24+
fn eh_personality() {
25+
loop {}
26+
}

‎src/tools/tidy/src/pal.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ const EXCEPTION_PATHS: &[&str] = &[
5959
"library/std/src/sys_common", // Should only contain abstractions over platforms
6060
"library/std/src/net/test.rs", // Utility helpers for tests
6161
"library/std/src/panic.rs", // fuchsia-specific panic backtrace handling
62+
"library/std/src/personality.rs",
63+
"library/std/src/personality/",
6264
];
6365

6466
pub fn check(path: &Path, bad: &mut bool) {

0 commit comments

Comments
 (0)
Please sign in to comment.