Refactor
This commit is contained in:
parent
210e6def4c
commit
e1c7c1ae60
@ -14,6 +14,7 @@ spin = { version = "0.9", default-features = false, features = ["mutex", "spin_m
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = []
|
alloc = []
|
||||||
|
unwinder = []
|
||||||
fde-phdr = ["libc"]
|
fde-phdr = ["libc"]
|
||||||
fde-registry = ["alloc"]
|
fde-registry = ["alloc"]
|
||||||
dwarf-expr = []
|
dwarf-expr = []
|
||||||
@ -25,7 +26,7 @@ panic = ["alloc"]
|
|||||||
panic-handler = ["print", "panic"]
|
panic-handler = ["print", "panic"]
|
||||||
panic-handler-dummy = []
|
panic-handler-dummy = []
|
||||||
system-alloc = []
|
system-alloc = []
|
||||||
default = ["dwarf-expr", "hide-trace", "fde-phdr", "fde-registry"]
|
default = ["unwinder", "dwarf-expr", "hide-trace", "fde-phdr", "fde-registry"]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
# Must be turned on due to Rust bug https://github.com/rust-lang/rust/issues/50007
|
# Must be turned on due to Rust bug https://github.com/rust-lang/rust/issues/50007
|
||||||
|
361
src/abi.rs
361
src/abi.rs
@ -1,13 +1,12 @@
|
|||||||
use core::ffi::c_void;
|
use core::ffi::c_void;
|
||||||
use core::ops;
|
use core::ops;
|
||||||
use core::ptr;
|
|
||||||
use gimli::Register;
|
|
||||||
|
|
||||||
use crate::arch::*;
|
use crate::arch::*;
|
||||||
use crate::find_fde::{self, FDEFinder};
|
|
||||||
use crate::frame::Frame;
|
|
||||||
use crate::util::*;
|
use crate::util::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "unwinder")]
|
||||||
|
use crate::unwinder::*;
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct UnwindReasonCode(pub c_int);
|
pub struct UnwindReasonCode(pub c_int);
|
||||||
@ -69,34 +68,28 @@ pub type UnwindStopFn = extern "C" fn(
|
|||||||
*mut c_void,
|
*mut c_void,
|
||||||
) -> UnwindReasonCode;
|
) -> UnwindReasonCode;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unwinder"))]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct UnwindException {
|
pub struct UnwindException {
|
||||||
pub exception_class: u64,
|
pub exception_class: u64,
|
||||||
pub exception_cleanup: Option<UnwindExceptionCleanupFn>,
|
pub exception_cleanup: Option<UnwindExceptionCleanupFn>,
|
||||||
private_1: Option<UnwindStopFn>,
|
private: [usize; Arch::UNWIND_PRIVATE_DATA_SIZE],
|
||||||
private_2: usize,
|
|
||||||
private_unused: [usize; Arch::UNWIND_PRIVATE_DATA_SIZE - 2],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnwindException {
|
impl UnwindException {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> UnwindException {
|
pub fn new() -> UnwindException {
|
||||||
UnwindException {
|
unsafe { core::mem::zeroed() }
|
||||||
exception_class: 0,
|
|
||||||
exception_cleanup: None,
|
|
||||||
private_1: None,
|
|
||||||
private_2: 0,
|
|
||||||
private_unused: [0; Arch::UNWIND_PRIVATE_DATA_SIZE - 2],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type UnwindTraceFn =
|
pub type UnwindTraceFn =
|
||||||
extern "C" fn(ctx: &mut UnwindContext<'_>, arg: *mut c_void) -> UnwindReasonCode;
|
extern "C" fn(ctx: &mut UnwindContext<'_>, arg: *mut c_void) -> UnwindReasonCode;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unwinder"))]
|
||||||
pub struct UnwindContext<'a> {
|
pub struct UnwindContext<'a> {
|
||||||
frame: Option<&'a Frame>,
|
opaque: usize,
|
||||||
ctx: &'a mut Context,
|
phantom: core::marker::PhantomData<&'a ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PersonalityRoutine = extern "C" fn(
|
pub type PersonalityRoutine = extern "C" fn(
|
||||||
@ -106,339 +99,3 @@ pub type PersonalityRoutine = extern "C" fn(
|
|||||||
&mut UnwindException,
|
&mut UnwindException,
|
||||||
&mut UnwindContext<'_>,
|
&mut UnwindContext<'_>,
|
||||||
) -> UnwindReasonCode;
|
) -> UnwindReasonCode;
|
||||||
|
|
||||||
#[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 = 0;
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C-unwind" fn _Unwind_RaiseException(
|
|
||||||
exception: &mut UnwindException,
|
|
||||||
) -> UnwindReasonCode {
|
|
||||||
let saved_ctx = save_context();
|
|
||||||
|
|
||||||
// Phase 1: Search for handler
|
|
||||||
let mut ctx = saved_ctx.clone();
|
|
||||||
let handler_cfa = loop {
|
|
||||||
if let Some(frame) = try1!(Frame::from_context(&ctx)) {
|
|
||||||
if let Some(personality) = frame.personality() {
|
|
||||||
let result = personality(
|
|
||||||
1,
|
|
||||||
UnwindAction::SEARCH_PHASE,
|
|
||||||
exception.exception_class,
|
|
||||||
exception,
|
|
||||||
&mut UnwindContext {
|
|
||||||
frame: Some(&frame),
|
|
||||||
ctx: &mut ctx,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
match result {
|
|
||||||
UnwindReasonCode::CONTINUE_UNWIND => (),
|
|
||||||
UnwindReasonCode::HANDLER_FOUND => {
|
|
||||||
exception.private_1 = None;
|
|
||||||
exception.private_2 = ctx[Arch::SP];
|
|
||||||
break ctx[Arch::SP];
|
|
||||||
}
|
|
||||||
_ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = try1!(frame.unwind(&ctx));
|
|
||||||
} else {
|
|
||||||
return UnwindReasonCode::END_OF_STACK;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut ctx = saved_ctx;
|
|
||||||
let code = raise_exception_phase2(exception, &mut ctx, handler_cfa);
|
|
||||||
match code {
|
|
||||||
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(&ctx) },
|
|
||||||
_ => code,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn raise_exception_phase2(
|
|
||||||
exception: &mut UnwindException,
|
|
||||||
ctx: &mut Context,
|
|
||||||
handler_cfa: usize,
|
|
||||||
) -> UnwindReasonCode {
|
|
||||||
loop {
|
|
||||||
if let Some(frame) = try2!(Frame::from_context(ctx)) {
|
|
||||||
let is_handler = ctx[Arch::SP] == handler_cfa;
|
|
||||||
if let Some(personality) = frame.personality() {
|
|
||||||
let code = personality(
|
|
||||||
1,
|
|
||||||
UnwindAction::CLEANUP_PHASE
|
|
||||||
| if is_handler {
|
|
||||||
UnwindAction::HANDLER_FRAME
|
|
||||||
} else {
|
|
||||||
UnwindAction::empty()
|
|
||||||
},
|
|
||||||
exception.exception_class,
|
|
||||||
exception,
|
|
||||||
&mut UnwindContext {
|
|
||||||
frame: Some(&frame),
|
|
||||||
ctx,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
match code {
|
|
||||||
UnwindReasonCode::CONTINUE_UNWIND => (),
|
|
||||||
UnwindReasonCode::INSTALL_CONTEXT => break,
|
|
||||||
_ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*ctx = try2!(frame.unwind(ctx));
|
|
||||||
} else {
|
|
||||||
return UnwindReasonCode::FATAL_PHASE2_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UnwindReasonCode::INSTALL_CONTEXT
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C-unwind" fn _Unwind_ForceUnwind(
|
|
||||||
exception: &mut UnwindException,
|
|
||||||
stop: UnwindStopFn,
|
|
||||||
stop_arg: *mut c_void,
|
|
||||||
) -> UnwindReasonCode {
|
|
||||||
let mut ctx = save_context();
|
|
||||||
|
|
||||||
exception.private_1 = Some(stop);
|
|
||||||
exception.private_2 = stop_arg as _;
|
|
||||||
|
|
||||||
let code = force_unwind_phase2(exception, &mut 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 {
|
|
||||||
loop {
|
|
||||||
let frame = try2!(Frame::from_context(ctx));
|
|
||||||
|
|
||||||
let code = 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,
|
|
||||||
},
|
|
||||||
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 = personality(
|
|
||||||
1,
|
|
||||||
UnwindAction::FORCE_UNWIND | UnwindAction::CLEANUP_PHASE,
|
|
||||||
exception.exception_class,
|
|
||||||
exception,
|
|
||||||
&mut UnwindContext {
|
|
||||||
frame: Some(&frame),
|
|
||||||
ctx,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
match code {
|
|
||||||
UnwindReasonCode::CONTINUE_UNWIND => (),
|
|
||||||
UnwindReasonCode::INSTALL_CONTEXT => break,
|
|
||||||
_ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*ctx = try2!(frame.unwind(ctx));
|
|
||||||
} else {
|
|
||||||
return UnwindReasonCode::END_OF_STACK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UnwindReasonCode::INSTALL_CONTEXT
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C-unwind" fn _Unwind_Resume(exception: &mut UnwindException) -> ! {
|
|
||||||
let mut ctx = save_context();
|
|
||||||
|
|
||||||
let code = match exception.private_1 {
|
|
||||||
None => {
|
|
||||||
let handler_cfa = exception.private_2;
|
|
||||||
raise_exception_phase2(exception, &mut ctx, handler_cfa)
|
|
||||||
}
|
|
||||||
Some(stop) => {
|
|
||||||
let stop_arg = exception.private_2 as _;
|
|
||||||
force_unwind_phase2(exception, &mut ctx, stop, stop_arg)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
|
|
||||||
|
|
||||||
unsafe { restore_context(&ctx) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C-unwind" fn _Unwind_Resume_or_Rethrow(
|
|
||||||
exception: &mut UnwindException,
|
|
||||||
) -> UnwindReasonCode {
|
|
||||||
let stop = match exception.private_1 {
|
|
||||||
None => return _Unwind_RaiseException(exception),
|
|
||||||
Some(v) => v,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut ctx = save_context();
|
|
||||||
|
|
||||||
let stop_arg = exception.private_2 as _;
|
|
||||||
let code = force_unwind_phase2(exception, &mut 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 {
|
|
||||||
let mut ctx = save_context();
|
|
||||||
let mut skipping = cfg!(feature = "hide-trace");
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let frame = try1!(Frame::from_context(&ctx));
|
|
||||||
if !skipping {
|
|
||||||
let code = trace(
|
|
||||||
&mut UnwindContext {
|
|
||||||
frame: frame.as_ref(),
|
|
||||||
ctx: &mut ctx,
|
|
||||||
},
|
|
||||||
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));
|
|
||||||
} else {
|
|
||||||
return UnwindReasonCode::END_OF_STACK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -17,11 +17,12 @@
|
|||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
#[cfg(feature = "unwinder")]
|
||||||
|
mod unwinder;
|
||||||
|
|
||||||
pub mod abi;
|
pub mod abi;
|
||||||
|
|
||||||
mod arch;
|
mod arch;
|
||||||
mod find_fde;
|
|
||||||
mod frame;
|
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
#[cfg(feature = "print")]
|
#[cfg(feature = "print")]
|
||||||
|
@ -65,7 +65,11 @@ fn stack_trace() {
|
|||||||
) -> UnwindReasonCode {
|
) -> UnwindReasonCode {
|
||||||
let data = unsafe { &mut *(arg as *mut CallbackData) };
|
let data = unsafe { &mut *(arg as *mut CallbackData) };
|
||||||
data.counter += 1;
|
data.counter += 1;
|
||||||
eprintln!("{:4}:{:#19x} - <unknown>", data.counter, _Unwind_GetIP(unwind_ctx));
|
eprintln!(
|
||||||
|
"{:4}:{:#19x} - <unknown>",
|
||||||
|
data.counter,
|
||||||
|
_Unwind_GetIP(unwind_ctx)
|
||||||
|
);
|
||||||
UnwindReasonCode::NO_REASON
|
UnwindReasonCode::NO_REASON
|
||||||
}
|
}
|
||||||
let mut data = CallbackData { counter: 0 };
|
let mut data = CallbackData { counter: 0 };
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::find_fde::FDESearchResult;
|
use super::FDESearchResult;
|
||||||
use crate::util::*;
|
use crate::util::*;
|
||||||
|
|
||||||
use core::ffi::c_void;
|
use core::ffi::c_void;
|
@ -3,9 +3,9 @@ use gimli::{
|
|||||||
UninitializedUnwindContext, UnwindTableRow, Value,
|
UninitializedUnwindContext, UnwindTableRow, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::find_fde::{self, FDEFinder, FDESearchResult};
|
||||||
use crate::abi::PersonalityRoutine;
|
use crate::abi::PersonalityRoutine;
|
||||||
use crate::arch::*;
|
use crate::arch::*;
|
||||||
use crate::find_fde::{self, FDEFinder, FDESearchResult};
|
|
||||||
use crate::util::*;
|
use crate::util::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
362
src/unwinder/mod.rs
Normal file
362
src/unwinder/mod.rs
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
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 find_fde::FDEFinder;
|
||||||
|
use frame::Frame;
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 = 0;
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C-unwind" fn _Unwind_RaiseException(
|
||||||
|
exception: &mut UnwindException,
|
||||||
|
) -> UnwindReasonCode {
|
||||||
|
let saved_ctx = save_context();
|
||||||
|
|
||||||
|
// Phase 1: Search for handler
|
||||||
|
let mut ctx = saved_ctx.clone();
|
||||||
|
let handler_cfa = loop {
|
||||||
|
if let Some(frame) = try1!(Frame::from_context(&ctx)) {
|
||||||
|
if let Some(personality) = frame.personality() {
|
||||||
|
let result = personality(
|
||||||
|
1,
|
||||||
|
UnwindAction::SEARCH_PHASE,
|
||||||
|
exception.exception_class,
|
||||||
|
exception,
|
||||||
|
&mut UnwindContext {
|
||||||
|
frame: Some(&frame),
|
||||||
|
ctx: &mut ctx,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
UnwindReasonCode::CONTINUE_UNWIND => (),
|
||||||
|
UnwindReasonCode::HANDLER_FOUND => {
|
||||||
|
exception.private_1 = None;
|
||||||
|
exception.private_2 = ctx[Arch::SP];
|
||||||
|
break ctx[Arch::SP];
|
||||||
|
}
|
||||||
|
_ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = try1!(frame.unwind(&ctx));
|
||||||
|
} else {
|
||||||
|
return UnwindReasonCode::END_OF_STACK;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut ctx = saved_ctx;
|
||||||
|
let code = raise_exception_phase2(exception, &mut ctx, handler_cfa);
|
||||||
|
match code {
|
||||||
|
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(&ctx) },
|
||||||
|
_ => code,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn raise_exception_phase2(
|
||||||
|
exception: &mut UnwindException,
|
||||||
|
ctx: &mut Context,
|
||||||
|
handler_cfa: usize,
|
||||||
|
) -> UnwindReasonCode {
|
||||||
|
loop {
|
||||||
|
if let Some(frame) = try2!(Frame::from_context(ctx)) {
|
||||||
|
let is_handler = ctx[Arch::SP] == handler_cfa;
|
||||||
|
if let Some(personality) = frame.personality() {
|
||||||
|
let code = personality(
|
||||||
|
1,
|
||||||
|
UnwindAction::CLEANUP_PHASE
|
||||||
|
| if is_handler {
|
||||||
|
UnwindAction::HANDLER_FRAME
|
||||||
|
} else {
|
||||||
|
UnwindAction::empty()
|
||||||
|
},
|
||||||
|
exception.exception_class,
|
||||||
|
exception,
|
||||||
|
&mut UnwindContext {
|
||||||
|
frame: Some(&frame),
|
||||||
|
ctx,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
match code {
|
||||||
|
UnwindReasonCode::CONTINUE_UNWIND => (),
|
||||||
|
UnwindReasonCode::INSTALL_CONTEXT => break,
|
||||||
|
_ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*ctx = try2!(frame.unwind(ctx));
|
||||||
|
} else {
|
||||||
|
return UnwindReasonCode::FATAL_PHASE2_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UnwindReasonCode::INSTALL_CONTEXT
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C-unwind" fn _Unwind_ForceUnwind(
|
||||||
|
exception: &mut UnwindException,
|
||||||
|
stop: UnwindStopFn,
|
||||||
|
stop_arg: *mut c_void,
|
||||||
|
) -> UnwindReasonCode {
|
||||||
|
let mut ctx = save_context();
|
||||||
|
|
||||||
|
exception.private_1 = Some(stop);
|
||||||
|
exception.private_2 = stop_arg as _;
|
||||||
|
|
||||||
|
let code = force_unwind_phase2(exception, &mut 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 {
|
||||||
|
loop {
|
||||||
|
let frame = try2!(Frame::from_context(ctx));
|
||||||
|
|
||||||
|
let code = 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,
|
||||||
|
},
|
||||||
|
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 = personality(
|
||||||
|
1,
|
||||||
|
UnwindAction::FORCE_UNWIND | UnwindAction::CLEANUP_PHASE,
|
||||||
|
exception.exception_class,
|
||||||
|
exception,
|
||||||
|
&mut UnwindContext {
|
||||||
|
frame: Some(&frame),
|
||||||
|
ctx,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
match code {
|
||||||
|
UnwindReasonCode::CONTINUE_UNWIND => (),
|
||||||
|
UnwindReasonCode::INSTALL_CONTEXT => break,
|
||||||
|
_ => return UnwindReasonCode::FATAL_PHASE2_ERROR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*ctx = try2!(frame.unwind(ctx));
|
||||||
|
} else {
|
||||||
|
return UnwindReasonCode::END_OF_STACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UnwindReasonCode::INSTALL_CONTEXT
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C-unwind" fn _Unwind_Resume(exception: &mut UnwindException) -> ! {
|
||||||
|
let mut ctx = save_context();
|
||||||
|
|
||||||
|
let code = match exception.private_1 {
|
||||||
|
None => {
|
||||||
|
let handler_cfa = exception.private_2;
|
||||||
|
raise_exception_phase2(exception, &mut ctx, handler_cfa)
|
||||||
|
}
|
||||||
|
Some(stop) => {
|
||||||
|
let stop_arg = exception.private_2 as _;
|
||||||
|
force_unwind_phase2(exception, &mut ctx, stop, stop_arg)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
|
||||||
|
|
||||||
|
unsafe { restore_context(&ctx) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C-unwind" fn _Unwind_Resume_or_Rethrow(
|
||||||
|
exception: &mut UnwindException,
|
||||||
|
) -> UnwindReasonCode {
|
||||||
|
let stop = match exception.private_1 {
|
||||||
|
None => return _Unwind_RaiseException(exception),
|
||||||
|
Some(v) => v,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut ctx = save_context();
|
||||||
|
|
||||||
|
let stop_arg = exception.private_2 as _;
|
||||||
|
let code = force_unwind_phase2(exception, &mut 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 {
|
||||||
|
let mut ctx = save_context();
|
||||||
|
let mut skipping = cfg!(feature = "hide-trace");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let frame = try1!(Frame::from_context(&ctx));
|
||||||
|
if !skipping {
|
||||||
|
let code = trace(
|
||||||
|
&mut UnwindContext {
|
||||||
|
frame: frame.as_ref(),
|
||||||
|
ctx: &mut ctx,
|
||||||
|
},
|
||||||
|
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));
|
||||||
|
} else {
|
||||||
|
return UnwindReasonCode::END_OF_STACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user