Full unwinding implementation
This commit is contained in:
parent
874abe87d2
commit
855a3d3a9f
273
src/abi.rs
273
src/abi.rs
@ -51,8 +51,8 @@ pub type UnwindStopFn = unsafe extern "C" fn(
|
||||
c_int,
|
||||
UnwindAction,
|
||||
u64,
|
||||
*mut UnwindException,
|
||||
*mut UnwindContext<'_>,
|
||||
&mut UnwindException,
|
||||
&mut UnwindContext<'_>,
|
||||
*mut c_void,
|
||||
) -> UnwindReasonCode;
|
||||
|
||||
@ -60,6 +60,8 @@ pub type UnwindStopFn = unsafe extern "C" fn(
|
||||
pub struct UnwindException {
|
||||
pub exception_class: u64,
|
||||
pub exception_cleanup: Option<UnwindExceptionCleanupFn>,
|
||||
private_1: Option<UnwindStopFn>,
|
||||
private_2: usize,
|
||||
}
|
||||
|
||||
pub type UnwindTraceFn =
|
||||
@ -149,5 +151,272 @@ pub extern "C" fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void
|
||||
.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::NONE
|
||||
},
|
||||
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 unsafe 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 = unsafe { force_unwind_phase2(exception, &mut ctx, stop, stop_arg) };
|
||||
match code {
|
||||
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(&ctx) },
|
||||
_ => code,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe 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 = unsafe {
|
||||
stop(
|
||||
1,
|
||||
UnwindAction::FORCE_UNWIND
|
||||
| UnwindAction::END_OF_STACK
|
||||
| if frame.is_none() {
|
||||
UnwindAction::END_OF_STACK
|
||||
} else {
|
||||
UnwindAction::NONE
|
||||
},
|
||||
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 _;
|
||||
unsafe { 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 = unsafe { 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) };
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C-unwind" fn _Unwind_Backtrace(
|
||||
trace: UnwindTraceFn,
|
||||
trace_argument: *mut c_void,
|
||||
) -> UnwindReasonCode {
|
||||
let mut ctx = save_context();
|
||||
let mut skipping = true;
|
||||
|
||||
loop {
|
||||
let frame = try1!(Frame::from_context(&ctx));
|
||||
if !skipping {
|
||||
let code = unsafe {
|
||||
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…
Reference in New Issue
Block a user