blob: 5bcc38323ea13293fb89d76df5afde2f4b2e2626 [file] [log] [blame] [edit]
mod arch;
mod find_fde;
mod frame;
use core::ffi::c_void;
use core::ptr;
use gimli::Register;
use crate::abi::*;
use crate::arch::*;
use crate::util::*;
use arch::*;
use find_fde::FDEFinder;
use frame::Frame;
#[cfg(feature = "fde-custom")]
pub use find_fde::custom_eh_frame_finder;
// Helper function to turn `save_context` which takes function pointer to a closure-taking function.
fn with_context<T, F: FnOnce(&mut Context) -> T>(f: F) -> T {
use core::mem::ManuallyDrop;
union Data<T, F> {
f: ManuallyDrop<F>,
t: ManuallyDrop<T>,
}
extern "C" fn delegate<T, F: FnOnce(&mut Context) -> T>(ctx: &mut Context, ptr: *mut ()) {
// SAFETY: This function is called exactly once; it extracts the function, call it and
// store the return value. This function is `extern "C"` so we don't need to worry about
// unwinding past it.
unsafe {
let data = &mut *ptr.cast::<Data<T, F>>();
let t = ManuallyDrop::take(&mut data.f)(ctx);
data.t = ManuallyDrop::new(t);
}
}
let mut data = Data {
f: ManuallyDrop::new(f),
};
save_context(delegate::<T, F>, ptr::addr_of_mut!(data).cast());
unsafe { ManuallyDrop::into_inner(data.t) }
}
#[repr(C)]
pub struct UnwindException {
pub exception_class: u64,
pub exception_cleanup: Option<UnwindExceptionCleanupFn>,
private_1: Option<UnwindStopFn>,
private_2: usize,
private_unused: [usize; Arch::UNWIND_PRIVATE_DATA_SIZE - 2],
}
pub struct UnwindContext<'a> {
frame: Option<&'a Frame>,
ctx: &'a mut Context,
signal: bool,
}
#[no_mangle]
pub extern "C" fn _Unwind_GetGR(unwind_ctx: &UnwindContext<'_>, index: c_int) -> usize {
unwind_ctx.ctx[Register(index as u16)]
}
#[no_mangle]
pub extern "C" fn _Unwind_GetCFA(unwind_ctx: &UnwindContext<'_>) -> usize {
unwind_ctx.ctx[Arch::SP]
}
#[no_mangle]
pub extern "C" fn _Unwind_SetGR(unwind_ctx: &mut UnwindContext<'_>, index: c_int, value: usize) {
unwind_ctx.ctx[Register(index as u16)] = value;
}
#[no_mangle]
pub extern "C" fn _Unwind_GetIP(unwind_ctx: &UnwindContext<'_>) -> usize {
unwind_ctx.ctx[Arch::RA]
}
#[no_mangle]
pub extern "C" fn _Unwind_GetIPInfo(
unwind_ctx: &UnwindContext<'_>,
ip_before_insn: &mut c_int,
) -> usize {
*ip_before_insn = unwind_ctx.signal as _;
unwind_ctx.ctx[Arch::RA]
}
#[no_mangle]
pub extern "C" fn _Unwind_SetIP(unwind_ctx: &mut UnwindContext<'_>, value: usize) {
unwind_ctx.ctx[Arch::RA] = value;
}
#[no_mangle]
pub extern "C" fn _Unwind_GetLanguageSpecificData(unwind_ctx: &UnwindContext<'_>) -> *mut c_void {
unwind_ctx
.frame
.map(|f| f.lsda() as *mut c_void)
.unwrap_or(ptr::null_mut())
}
#[no_mangle]
pub extern "C" fn _Unwind_GetRegionStart(unwind_ctx: &UnwindContext<'_>) -> usize {
unwind_ctx.frame.map(|f| f.initial_address()).unwrap_or(0)
}
#[no_mangle]
pub extern "C" fn _Unwind_GetTextRelBase(unwind_ctx: &UnwindContext<'_>) -> usize {
unwind_ctx
.frame
.map(|f| f.bases().eh_frame.text.unwrap() as _)
.unwrap_or(0)
}
#[no_mangle]
pub extern "C" fn _Unwind_GetDataRelBase(unwind_ctx: &UnwindContext<'_>) -> usize {
unwind_ctx
.frame
.map(|f| f.bases().eh_frame.data.unwrap() as _)
.unwrap_or(0)
}
#[no_mangle]
pub extern "C" fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
find_fde::get_finder()
.find_fde(pc as usize - 1)
.map(|r| r.fde.initial_address() as usize as _)
.unwrap_or(ptr::null_mut())
}
macro_rules! try1 {
($e: expr) => {{
match $e {
Ok(v) => v,
Err(_) => return UnwindReasonCode::FATAL_PHASE1_ERROR,
}
}};
}
macro_rules! try2 {
($e: expr) => {{
match $e {
Ok(v) => v,
Err(_) => return UnwindReasonCode::FATAL_PHASE2_ERROR,
}
}};
}
#[inline(never)]
#[no_mangle]
pub unsafe extern "C-unwind" fn _Unwind_RaiseException(
exception: *mut UnwindException,
) -> UnwindReasonCode {
with_context(|saved_ctx| {
// Phase 1: Search for handler
let mut ctx = saved_ctx.clone();
let mut signal = false;
loop {
if let Some(frame) = try1!(Frame::from_context(&ctx, signal)) {
if let Some(personality) = frame.personality() {
let result = unsafe {
personality(
1,
UnwindAction::SEARCH_PHASE,
(*exception).exception_class,
exception,
&mut UnwindContext {
frame: Some(&frame),
ctx: &mut ctx,
signal,
},
)
};
match result {
UnwindReasonCode::CONTINUE_UNWIND => (),
UnwindReasonCode::HANDLER_FOUND => {
break;
}
_ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
}
}
ctx = try1!(frame.unwind(&ctx));
signal = frame.is_signal_trampoline();
} else {
return UnwindReasonCode::END_OF_STACK;
}
}
// Disambiguate normal frame and signal frame.
let handler_cfa = ctx[Arch::SP] - signal as usize;
unsafe {
(*exception).private_1 = None;
(*exception).private_2 = handler_cfa;
}
let code = raise_exception_phase2(exception, saved_ctx, handler_cfa);
match code {
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(saved_ctx) },
_ => code,
}
})
}
fn raise_exception_phase2(
exception: *mut UnwindException,
ctx: &mut Context,
handler_cfa: usize,
) -> UnwindReasonCode {
let mut signal = false;
loop {
if let Some(frame) = try2!(Frame::from_context(ctx, signal)) {
let frame_cfa = ctx[Arch::SP] - signal as usize;
if let Some(personality) = frame.personality() {
let code = unsafe {
personality(
1,
UnwindAction::CLEANUP_PHASE
| if frame_cfa == handler_cfa {
UnwindAction::HANDLER_FRAME
} else {
UnwindAction::empty()
},
(*exception).exception_class,
exception,
&mut UnwindContext {
frame: Some(&frame),
ctx,
signal,
},
)
};
match code {
UnwindReasonCode::CONTINUE_UNWIND => (),
UnwindReasonCode::INSTALL_CONTEXT => {
frame.adjust_stack_for_args(ctx);
return UnwindReasonCode::INSTALL_CONTEXT;
}
_ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
}
}
*ctx = try2!(frame.unwind(ctx));
signal = frame.is_signal_trampoline();
} else {
return UnwindReasonCode::FATAL_PHASE2_ERROR;
}
}
}
#[inline(never)]
#[no_mangle]
pub unsafe extern "C-unwind" fn _Unwind_ForcedUnwind(
exception: *mut UnwindException,
stop: UnwindStopFn,
stop_arg: *mut c_void,
) -> UnwindReasonCode {
with_context(|ctx| {
unsafe {
(*exception).private_1 = Some(stop);
(*exception).private_2 = stop_arg as _;
}
let code = force_unwind_phase2(exception, ctx, stop, stop_arg);
match code {
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(ctx) },
_ => code,
}
})
}
fn force_unwind_phase2(
exception: *mut UnwindException,
ctx: &mut Context,
stop: UnwindStopFn,
stop_arg: *mut c_void,
) -> UnwindReasonCode {
let mut signal = false;
loop {
let frame = try2!(Frame::from_context(ctx, signal));
let code = unsafe {
stop(
1,
UnwindAction::FORCE_UNWIND
| UnwindAction::END_OF_STACK
| if frame.is_none() {
UnwindAction::END_OF_STACK
} else {
UnwindAction::empty()
},
(*exception).exception_class,
exception,
&mut UnwindContext {
frame: frame.as_ref(),
ctx,
signal,
},
stop_arg,
)
};
match code {
UnwindReasonCode::NO_REASON => (),
_ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
}
if let Some(frame) = frame {
if let Some(personality) = frame.personality() {
let code = unsafe {
personality(
1,
UnwindAction::FORCE_UNWIND | UnwindAction::CLEANUP_PHASE,
(*exception).exception_class,
exception,
&mut UnwindContext {
frame: Some(&frame),
ctx,
signal,
},
)
};
match code {
UnwindReasonCode::CONTINUE_UNWIND => (),
UnwindReasonCode::INSTALL_CONTEXT => {
frame.adjust_stack_for_args(ctx);
return UnwindReasonCode::INSTALL_CONTEXT;
}
_ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
}
}
*ctx = try2!(frame.unwind(ctx));
signal = frame.is_signal_trampoline();
} else {
return UnwindReasonCode::END_OF_STACK;
}
}
}
#[inline(never)]
#[no_mangle]
pub unsafe extern "C-unwind" fn _Unwind_Resume(exception: *mut UnwindException) -> ! {
with_context(|ctx| {
let code = match unsafe { (*exception).private_1 } {
None => {
let handler_cfa = unsafe { (*exception).private_2 };
raise_exception_phase2(exception, ctx, handler_cfa)
}
Some(stop) => {
let stop_arg = unsafe { (*exception).private_2 as _ };
force_unwind_phase2(exception, ctx, stop, stop_arg)
}
};
assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
unsafe { restore_context(ctx) }
})
}
#[inline(never)]
#[no_mangle]
pub unsafe extern "C-unwind" fn _Unwind_Resume_or_Rethrow(
exception: *mut UnwindException,
) -> UnwindReasonCode {
let stop = match unsafe { (*exception).private_1 } {
None => return unsafe { _Unwind_RaiseException(exception) },
Some(v) => v,
};
with_context(|ctx| {
let stop_arg = unsafe { (*exception).private_2 as _ };
let code = force_unwind_phase2(exception, ctx, stop, stop_arg);
assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
unsafe { restore_context(ctx) }
})
}
#[no_mangle]
pub unsafe extern "C" fn _Unwind_DeleteException(exception: *mut UnwindException) {
if let Some(cleanup) = unsafe { (*exception).exception_cleanup } {
unsafe { cleanup(UnwindReasonCode::FOREIGN_EXCEPTION_CAUGHT, exception) };
}
}
#[inline(never)]
#[no_mangle]
pub extern "C-unwind" fn _Unwind_Backtrace(
trace: UnwindTraceFn,
trace_argument: *mut c_void,
) -> UnwindReasonCode {
with_context(|ctx| {
let mut ctx = ctx.clone();
let mut signal = false;
let mut skipping = cfg!(feature = "hide-trace");
loop {
let frame = try1!(Frame::from_context(&ctx, signal));
if !skipping {
let code = trace(
&UnwindContext {
frame: frame.as_ref(),
ctx: &mut ctx,
signal,
},
trace_argument,
);
match code {
UnwindReasonCode::NO_REASON => (),
_ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
}
}
if let Some(frame) = frame {
if skipping {
if frame.initial_address() == _Unwind_Backtrace as usize {
skipping = false;
}
}
ctx = try1!(frame.unwind(&ctx));
signal = frame.is_signal_trampoline();
} else {
return UnwindReasonCode::END_OF_STACK;
}
}
})
}