extern crate unwind;
use super::dwarf::eh::{self, EHAction, EHContext};
use core::ffi::c_int;
use unwind as uw;
#[cfg(target_arch = "x86")]
const UNWIND_DATA_REG: (i32, i32) = (0, 2);
#[cfg(target_arch = "x86_64")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1);
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
const UNWIND_DATA_REG: (i32, i32) = (0, 1);
#[cfg(target_arch = "m68k")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1);
#[cfg(any(
target_arch = "mips",
target_arch = "mips32r6",
target_arch = "mips64",
target_arch = "mips64r6"
))]
const UNWIND_DATA_REG: (i32, i32) = (4, 5);
#[cfg(target_arch = "csky")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1);
#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
const UNWIND_DATA_REG: (i32, i32) = (3, 4);
#[cfg(target_arch = "s390x")]
const UNWIND_DATA_REG: (i32, i32) = (6, 7);
#[cfg(any(target_arch = "sparc", target_arch = "sparc64"))]
const UNWIND_DATA_REG: (i32, i32) = (24, 25);
#[cfg(target_arch = "hexagon")]
const UNWIND_DATA_REG: (i32, i32) = (0, 1);
#[cfg(any(target_arch = "riscv64", target_arch = "riscv32"))]
const UNWIND_DATA_REG: (i32, i32) = (10, 11);
#[cfg(target_arch = "loongarch64")]
const UNWIND_DATA_REG: (i32, i32) = (4, 5);
cfg_if::cfg_if! {
if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "visionos"), not(target_os = "netbsd")))] {
#[lang = "eh_personality"]
unsafe extern "C" fn rust_eh_personality(
state: uw::_Unwind_State,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
) -> uw::_Unwind_Reason_Code {
let state = state as c_int;
let action = state & uw::_US_ACTION_MASK as c_int;
let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int {
if state & uw::_US_FORCE_UNWIND as c_int != 0 {
return continue_unwind(exception_object, context);
}
true
} else if action == uw::_US_UNWIND_FRAME_STARTING as c_int {
false
} else if action == uw::_US_UNWIND_FRAME_RESUME as c_int {
return continue_unwind(exception_object, context);
} else {
return uw::_URC_FAILURE;
};
uw::_Unwind_SetGR(context, uw::UNWIND_POINTER_REG, exception_object as uw::_Unwind_Ptr);
let eh_action = match find_eh_action(context) {
Ok(action) => action,
Err(_) => return uw::_URC_FAILURE,
};
if search_phase {
match eh_action {
EHAction::None | EHAction::Cleanup(_) => {
return continue_unwind(exception_object, context);
}
EHAction::Catch(_) | EHAction::Filter(_) => {
(*exception_object).private[5] =
uw::_Unwind_GetGR(context, uw::UNWIND_SP_REG);
return uw::_URC_HANDLER_FOUND;
}
EHAction::Terminate => return uw::_URC_FAILURE,
}
} else {
match eh_action {
EHAction::None => return continue_unwind(exception_object, context),
EHAction::Filter(_) if state & uw::_US_FORCE_UNWIND as c_int != 0 => return continue_unwind(exception_object, context),
EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => {
uw::_Unwind_SetGR(
context,
UNWIND_DATA_REG.0,
exception_object as uw::_Unwind_Ptr,
);
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null());
uw::_Unwind_SetIP(context, lpad);
return uw::_URC_INSTALL_CONTEXT;
}
EHAction::Terminate => return uw::_URC_FAILURE,
}
}
unsafe fn continue_unwind(
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
) -> uw::_Unwind_Reason_Code {
if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON {
uw::_URC_CONTINUE_UNWIND
} else {
uw::_URC_FAILURE
}
}
extern "C" {
fn __gnu_unwind_frame(
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
) -> uw::_Unwind_Reason_Code;
}
}
} else {
unsafe extern "C" fn rust_eh_personality_impl(
version: c_int,
actions: uw::_Unwind_Action,
_exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
) -> uw::_Unwind_Reason_Code {
if version != 1 {
return uw::_URC_FATAL_PHASE1_ERROR;
}
let eh_action = match find_eh_action(context) {
Ok(action) => action,
Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
};
if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 {
match eh_action {
EHAction::None | EHAction::Cleanup(_) => uw::_URC_CONTINUE_UNWIND,
EHAction::Catch(_) | EHAction::Filter(_) => uw::_URC_HANDLER_FOUND,
EHAction::Terminate => uw::_URC_FATAL_PHASE1_ERROR,
}
} else {
match eh_action {
EHAction::None => uw::_URC_CONTINUE_UNWIND,
EHAction::Filter(_) if actions as i32 & uw::_UA_FORCE_UNWIND as i32 != 0 => uw::_URC_CONTINUE_UNWIND,
EHAction::Cleanup(lpad) | EHAction::Catch(lpad) | EHAction::Filter(lpad) => {
uw::_Unwind_SetGR(
context,
UNWIND_DATA_REG.0,
exception_object.cast(),
);
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null());
uw::_Unwind_SetIP(context, lpad);
uw::_URC_INSTALL_CONTEXT
}
EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR,
}
}
}
cfg_if::cfg_if! {
if #[cfg(all(windows, any(target_arch = "aarch64", target_arch = "x86_64"), target_env = "gnu"))] {
#[lang = "eh_personality"]
#[allow(nonstandard_style)]
unsafe extern "C" fn rust_eh_personality(
exceptionRecord: *mut uw::EXCEPTION_RECORD,
establisherFrame: uw::LPVOID,
contextRecord: *mut uw::CONTEXT,
dispatcherContext: *mut uw::DISPATCHER_CONTEXT,
) -> uw::EXCEPTION_DISPOSITION {
uw::_GCC_specific_handler(
exceptionRecord,
establisherFrame,
contextRecord,
dispatcherContext,
rust_eh_personality_impl,
)
}
} else {
#[lang = "eh_personality"]
unsafe extern "C" fn rust_eh_personality(
version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
exception_object: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context,
) -> uw::_Unwind_Reason_Code {
rust_eh_personality_impl(
version,
actions,
exception_class,
exception_object,
context,
)
}
}
}
}
}
unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction, ()> {
let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
let mut ip_before_instr: c_int = 0;
let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr);
let eh_context = EHContext {
ip: if ip_before_instr != 0 { ip } else { ip.wrapping_sub(1) },
func_start: uw::_Unwind_GetRegionStart(context),
get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
};
eh::find_eh_action(lsda, &eh_context)
}