Change save_context to invoke callback instead

This commit is contained in:
Gary Guo 2023-05-07 01:00:25 +01:00
parent 6dc1ed4d80
commit a9bec9e719
6 changed files with 342 additions and 207 deletions

View File

@ -59,25 +59,34 @@ impl ops::IndexMut<gimli::Register> for Context {
} }
#[naked] #[naked]
pub extern "C-unwind" fn save_context() -> Context { pub extern "C-unwind" fn save_context(f: extern "C" fn(&mut Context, *mut ()), ptr: *mut ()) {
// No need to save caller-saved registers here. // No need to save caller-saved registers here.
unsafe { unsafe {
asm!( asm!(
" "
stp d8, d9, [x8, 0x140] stp x29, x30, [sp, -16]!
stp d10, d11, [x8, 0x150] sub sp, sp, 512
stp d12, d13, [x8, 0x160] mov x8, x0
stp d14, d15, [x8, 0x170]
str x19, [x8, 0x98]
stp x20, x21, [x8, 0xA0]
stp x22, x23, [x8, 0xB0]
stp x24, x25, [x8, 0xC0]
stp x26, x27, [x8, 0xD0]
stp x28, x29, [x8, 0xE0]
mov x0, sp mov x0, sp
stp x30, x0, [x8, 0xF0]
stp d8, d9, [sp, 0x140]
stp d10, d11, [sp, 0x150]
stp d12, d13, [sp, 0x160]
stp d14, d15, [sp, 0x170]
str x19, [sp, 0x98]
stp x20, x21, [sp, 0xA0]
stp x22, x23, [sp, 0xB0]
stp x24, x25, [sp, 0xC0]
stp x26, x27, [sp, 0xD0]
stp x28, x29, [sp, 0xE0]
add x2, sp, 528
stp x30, x2, [sp, 0xF0]
blr x8
add sp, sp, 512
ldp x29, x30, [sp], 16
ret ret
", ",
options(noreturn) options(noreturn)

View File

@ -61,39 +61,39 @@ impl ops::IndexMut<gimli::Register> for Context {
macro_rules! code { macro_rules! code {
(save_gp) => { (save_gp) => {
" "
sw x0, 0x00(a0) sw x0, 0x00(sp)
sw ra, 0x04(a0) sw ra, 0x04(sp)
sw sp, 0x08(a0) sw t0, 0x08(sp)
sw gp, 0x0C(a0) sw gp, 0x0C(sp)
sw tp, 0x10(a0) sw tp, 0x10(sp)
sw s0, 0x20(a0) sw s0, 0x20(sp)
sw s1, 0x24(a0) sw s1, 0x24(sp)
sw s2, 0x48(a0) sw s2, 0x48(sp)
sw s3, 0x4C(a0) sw s3, 0x4C(sp)
sw s4, 0x50(a0) sw s4, 0x50(sp)
sw s5, 0x54(a0) sw s5, 0x54(sp)
sw s6, 0x58(a0) sw s6, 0x58(sp)
sw s7, 0x5C(a0) sw s7, 0x5C(sp)
sw s8, 0x60(a0) sw s8, 0x60(sp)
sw s9, 0x64(a0) sw s9, 0x64(sp)
sw s10, 0x68(a0) sw s10, 0x68(sp)
sw s11, 0x6C(a0) sw s11, 0x6C(sp)
" "
}; };
(save_fp) => { (save_fp) => {
" "
fsd fs0, 0xC0(a0) fsd fs0, 0xC0(sp)
fsd fs1, 0xC8(a0) fsd fs1, 0xC8(sp)
fsd fs2, 0x110(a0) fsd fs2, 0x110(sp)
fsd fs3, 0x118(a0) fsd fs3, 0x118(sp)
fsd fs4, 0x120(a0) fsd fs4, 0x120(sp)
fsd fs5, 0x128(a0) fsd fs5, 0x128(sp)
fsd fs6, 0x130(a0) fsd fs6, 0x130(sp)
fsd fs7, 0x138(a0) fsd fs7, 0x138(sp)
fsd fs8, 0x140(a0) fsd fs8, 0x140(sp)
fsd fs9, 0x148(a0) fsd fs9, 0x148(sp)
fsd fs10, 0x150(a0) fsd fs10, 0x150(sp)
fsd fs11, 0x158(a0) fsd fs11, 0x158(sp)
" "
}; };
(restore_gp) => { (restore_gp) => {
@ -169,18 +169,48 @@ macro_rules! code {
} }
#[naked] #[naked]
pub extern "C-unwind" fn save_context() -> Context { pub extern "C-unwind" fn save_context(f: extern "C" fn(&mut Context, *mut ()), ptr: *mut ()) {
// No need to save caller-saved registers here. // No need to save caller-saved registers here.
#[cfg(target_feature = "d")] #[cfg(target_feature = "d")]
unsafe { unsafe {
asm!( asm!(
concat!(code!(save_gp), code!(save_fp), "ret"), "
mv t0, sp
add sp, sp, -0x188
sw ra, 0x180(sp)
",
code!(save_gp),
code!(save_fp),
"
mv t0, a0
mv a0, sp
jalr t0
lw ra, 0x180(sp)
add sp, sp, 0x188
ret
",
options(noreturn) options(noreturn)
); );
} }
#[cfg(not(target_feature = "d"))] #[cfg(not(target_feature = "d"))]
unsafe { unsafe {
asm!(concat!(code!(save_gp), "ret"), options(noreturn)); asm!(
"
mv t0, sp
add sp, sp, -0x88
sw ra, 0x80(sp)
",
code!(save_gp),
"
mv t0, a0
mv a0, sp
jalr t0
lw ra, 0x80(sp)
add sp, sp, 0x88
ret
",
options(noreturn)
);
} }
} }
@ -189,14 +219,23 @@ pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
#[cfg(target_feature = "d")] #[cfg(target_feature = "d")]
unsafe { unsafe {
asm!( asm!(
concat!(code!(restore_fp), code!(restore_gp), "lw a0, 0x28(a0)\nret"), code!(restore_fp),
code!(restore_gp),
"
lw a0, 0x28(a0)
ret
",
options(noreturn) options(noreturn)
); );
} }
#[cfg(not(target_feature = "d"))] #[cfg(not(target_feature = "d"))]
unsafe { unsafe {
asm!( asm!(
concat!(code!(restore_gp), "lw a0, 0x28(a0)\nret"), code!(restore_gp),
"
lw a0, 0x28(a0)
ret
",
options(noreturn) options(noreturn)
); );
} }

View File

@ -61,39 +61,39 @@ impl ops::IndexMut<gimli::Register> for Context {
macro_rules! code { macro_rules! code {
(save_gp) => { (save_gp) => {
" "
sd x0, 0x00(a0) sd x0, 0x00(sp)
sd ra, 0x08(a0) sd ra, 0x08(sp)
sd sp, 0x10(a0) sd t0, 0x10(sp)
sd gp, 0x18(a0) sd gp, 0x18(sp)
sd tp, 0x20(a0) sd tp, 0x20(sp)
sd s0, 0x40(a0) sd s0, 0x40(sp)
sd s1, 0x48(a0) sd s1, 0x48(sp)
sd s2, 0x90(a0) sd s2, 0x90(sp)
sd s3, 0x98(a0) sd s3, 0x98(sp)
sd s4, 0xA0(a0) sd s4, 0xA0(sp)
sd s5, 0xA8(a0) sd s5, 0xA8(sp)
sd s6, 0xB0(a0) sd s6, 0xB0(sp)
sd s7, 0xB8(a0) sd s7, 0xB8(sp)
sd s8, 0xC0(a0) sd s8, 0xC0(sp)
sd s9, 0xC8(a0) sd s9, 0xC8(sp)
sd s10, 0xD0(a0) sd s10, 0xD0(sp)
sd s11, 0xD8(a0) sd s11, 0xD8(sp)
" "
}; };
(save_fp) => { (save_fp) => {
" "
fsd fs0, 0x140(a0) fsd fs0, 0x140(sp)
fsd fs1, 0x148(a0) fsd fs1, 0x148(sp)
fsd fs2, 0x190(a0) fsd fs2, 0x190(sp)
fsd fs3, 0x198(a0) fsd fs3, 0x198(sp)
fsd fs4, 0x1A0(a0) fsd fs4, 0x1A0(sp)
fsd fs5, 0x1A8(a0) fsd fs5, 0x1A8(sp)
fsd fs6, 0x1B0(a0) fsd fs6, 0x1B0(sp)
fsd fs7, 0x1B8(a0) fsd fs7, 0x1B8(sp)
fsd fs8, 0x1C0(a0) fsd fs8, 0x1C0(sp)
fsd fs9, 0x1C8(a0) fsd fs9, 0x1C8(sp)
fsd fs10, 0x1D0(a0) fsd fs10, 0x1D0(sp)
fsd fs11, 0x1D8(a0) fsd fs11, 0x1D8(sp)
" "
}; };
(restore_gp) => { (restore_gp) => {
@ -169,18 +169,48 @@ macro_rules! code {
} }
#[naked] #[naked]
pub extern "C-unwind" fn save_context() -> Context { pub extern "C-unwind" fn save_context(f: extern "C" fn(&mut Context, *mut ()), ptr: *mut ()) {
// No need to save caller-saved registers here. // No need to save caller-saved registers here.
#[cfg(target_feature = "d")] #[cfg(target_feature = "d")]
unsafe { unsafe {
asm!( asm!(
concat!(code!(save_gp), code!(save_fp), "ret"), "
mv t0, sp
add sp, sp, -0x210
sd ra, 0x200(sp)
",
code!(save_gp),
code!(save_fp),
"
mv t0, a0
mv a0, sp
jalr t0
ld ra, 0x200(sp)
add sp, sp, 0x210
ret
",
options(noreturn) options(noreturn)
); );
} }
#[cfg(not(target_feature = "d"))] #[cfg(not(target_feature = "d"))]
unsafe { unsafe {
asm!(concat!(code!(save_gp), "ret"), options(noreturn)); asm!(
"
mv t0, sp
add sp, sp, -0x110
sd ra, 0x100(sp)
",
code!(save_gp),
"
mv t0, a0
mv a0, sp
jalr t0
ld ra, 0x100(sp)
add sp, sp, 0x110
ret
",
options(noreturn)
);
} }
} }
@ -189,14 +219,23 @@ pub unsafe extern "C" fn restore_context(ctx: &Context) -> ! {
#[cfg(target_feature = "d")] #[cfg(target_feature = "d")]
unsafe { unsafe {
asm!( asm!(
concat!(code!(restore_fp), code!(restore_gp), "ld a0, 0x50(a0)\nret"), code!(restore_fp),
code!(restore_gp),
"
ld a0, 0x50(a0)
ret
",
options(noreturn) options(noreturn)
); );
} }
#[cfg(not(target_feature = "d"))] #[cfg(not(target_feature = "d"))]
unsafe { unsafe {
asm!( asm!(
concat!(code!(restore_gp), "ld a0, 0x50(a0)\nret"), code!(restore_gp),
"
ld a0, 0x50(a0)
ret
",
options(noreturn) options(noreturn)
); );
} }

View File

@ -56,28 +56,40 @@ impl ops::IndexMut<gimli::Register> for Context {
} }
#[naked] #[naked]
pub extern "C-unwind" fn save_context() -> Context { pub extern "C-unwind" fn save_context(f: extern "C" fn(&mut Context, *mut ()), ptr: *mut ()) {
// No need to save caller-saved registers here. // No need to save caller-saved registers here.
unsafe { unsafe {
asm!( asm!(
" "
mov eax, [esp + 4] sub esp, 44
mov [eax + 4], ecx
mov [eax + 8], edx mov [esp + 4], ecx
mov [eax + 12], ebx mov [esp + 8], edx
mov [eax + 20], ebp mov [esp + 12], ebx
mov [eax + 24], esi
mov [eax + 28], edi
/* Adjust the stack to account for the return address */ /* Adjust the stack to account for the return address */
lea edx, [esp + 4] lea eax, [esp + 48]
mov [eax + 16], edx mov [esp + 16], eax
mov edx, [esp] mov [esp + 20], ebp
mov [eax + 32], edx mov [esp + 24], esi
stmxcsr [eax + 36] mov [esp + 28], edi
fnstcw [eax + 40]
ret 4 /* Return address */
mov eax, [esp + 44]
mov [esp + 32], eax
stmxcsr [esp + 36]
fnstcw [esp + 40]
mov eax, [esp + 52]
mov ecx, esp
push eax
push ecx
call [esp + 56]
add esp, 52
ret
", ",
options(noreturn) options(noreturn)
); );

View File

@ -58,27 +58,35 @@ impl ops::IndexMut<gimli::Register> for Context {
} }
#[naked] #[naked]
pub extern "C-unwind" fn save_context() -> Context { pub extern "C-unwind" fn save_context(f: extern "C" fn(&mut Context, *mut ()), ptr: *mut ()) {
// No need to save caller-saved registers here. // No need to save caller-saved registers here.
unsafe { unsafe {
asm!( asm!(
" "
mov rax, rdi sub rsp, 0x98
mov [rax + 0x18], rbx mov [rsp + 0x18], rbx
mov [rax + 0x30], rbp mov [rsp + 0x30], rbp
/* Adjust the stack to account for the return address */ /* Adjust the stack to account for the return address */
lea rdi, [rsp + 8] lea rax, [rsp + 0xA0]
mov [rax + 0x38], rdi mov [rsp + 0x38], rax
mov [rax + 0x60], r12 mov [rsp + 0x60], r12
mov [rax + 0x68], r13 mov [rsp + 0x68], r13
mov [rax + 0x70], r14 mov [rsp + 0x70], r14
mov [rax + 0x78], r15 mov [rsp + 0x78], r15
mov rdx, [rsp]
mov [rax + 0x80], rdx /* Return address */
stmxcsr [rax + 0x88] mov rax, [rsp + 0x98]
fnstcw [rax + 0x90] mov [rsp + 0x80], rax
stmxcsr [rsp + 0x88]
fnstcw [rsp + 0x90]
mov rax, rdi
mov rdi, rsp
call rax
add rsp, 0x98
ret ret
", ",
options(noreturn) options(noreturn)

View File

@ -16,6 +16,33 @@ use frame::Frame;
#[cfg(feature = "fde-custom")] #[cfg(feature = "fde-custom")]
pub use find_fde::custom_eh_frame_finder; 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)] #[repr(C)]
pub struct UnwindException { pub struct UnwindException {
pub exception_class: u64, pub exception_class: u64,
@ -125,53 +152,52 @@ macro_rules! try2 {
pub extern "C-unwind" fn _Unwind_RaiseException( pub extern "C-unwind" fn _Unwind_RaiseException(
exception: &mut UnwindException, exception: &mut UnwindException,
) -> UnwindReasonCode { ) -> UnwindReasonCode {
let saved_ctx = save_context(); 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 = personality(
1,
UnwindAction::SEARCH_PHASE,
exception.exception_class,
exception,
&mut UnwindContext {
frame: Some(&frame),
ctx: &mut ctx,
signal,
},
);
// Phase 1: Search for handler match result {
let mut ctx = saved_ctx.clone(); UnwindReasonCode::CONTINUE_UNWIND => (),
let mut signal = false; UnwindReasonCode::HANDLER_FOUND => {
loop { break;
if let Some(frame) = try1!(Frame::from_context(&ctx, signal)) { }
if let Some(personality) = frame.personality() { _ => return UnwindReasonCode::FATAL_PHASE1_ERROR,
let result = 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;
} }
ctx = try1!(frame.unwind(&ctx));
signal = frame.is_signal_trampoline();
} else {
return UnwindReasonCode::END_OF_STACK;
} }
}
// Disambiguate normal frame and signal frame. // Disambiguate normal frame and signal frame.
let handler_cfa = ctx[Arch::SP] - signal as usize; let handler_cfa = ctx[Arch::SP] - signal as usize;
exception.private_1 = None; exception.private_1 = None;
exception.private_2 = handler_cfa; exception.private_2 = handler_cfa;
let mut ctx = saved_ctx; let code = raise_exception_phase2(exception, saved_ctx, handler_cfa);
let code = raise_exception_phase2(exception, &mut ctx, handler_cfa); match code {
match code { UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(saved_ctx) },
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(&ctx) }, _ => code,
_ => code, }
} })
} }
fn raise_exception_phase2( fn raise_exception_phase2(
@ -225,16 +251,16 @@ pub extern "C-unwind" fn _Unwind_ForcedUnwind(
stop: UnwindStopFn, stop: UnwindStopFn,
stop_arg: *mut c_void, stop_arg: *mut c_void,
) -> UnwindReasonCode { ) -> UnwindReasonCode {
let mut ctx = save_context(); with_context(|ctx| {
exception.private_1 = Some(stop);
exception.private_2 = stop_arg as _;
exception.private_1 = Some(stop); let code = force_unwind_phase2(exception, ctx, stop, stop_arg);
exception.private_2 = stop_arg as _; match code {
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(ctx) },
let code = force_unwind_phase2(exception, &mut ctx, stop, stop_arg); _ => code,
match code { }
UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(&ctx) }, })
_ => code,
}
} }
fn force_unwind_phase2( fn force_unwind_phase2(
@ -304,21 +330,21 @@ fn force_unwind_phase2(
#[inline(never)] #[inline(never)]
#[no_mangle] #[no_mangle]
pub extern "C-unwind" fn _Unwind_Resume(exception: &mut UnwindException) -> ! { pub extern "C-unwind" fn _Unwind_Resume(exception: &mut UnwindException) -> ! {
let mut ctx = save_context(); with_context(|ctx| {
let code = match exception.private_1 {
None => {
let handler_cfa = exception.private_2;
raise_exception_phase2(exception, ctx, handler_cfa)
}
Some(stop) => {
let stop_arg = exception.private_2 as _;
force_unwind_phase2(exception, ctx, stop, stop_arg)
}
};
assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
let code = match exception.private_1 { unsafe { restore_context(ctx) }
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) }
} }
#[inline(never)] #[inline(never)]
@ -331,13 +357,13 @@ pub extern "C-unwind" fn _Unwind_Resume_or_Rethrow(
Some(v) => v, Some(v) => v,
}; };
let mut ctx = save_context(); with_context(|ctx| {
let stop_arg = exception.private_2 as _;
let code = force_unwind_phase2(exception, ctx, stop, stop_arg);
assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
let stop_arg = exception.private_2 as _; unsafe { restore_context(ctx) }
let code = force_unwind_phase2(exception, &mut ctx, stop, stop_arg); })
assert!(code == UnwindReasonCode::INSTALL_CONTEXT);
unsafe { restore_context(&ctx) }
} }
#[no_mangle] #[no_mangle]
@ -353,36 +379,38 @@ pub extern "C-unwind" fn _Unwind_Backtrace(
trace: UnwindTraceFn, trace: UnwindTraceFn,
trace_argument: *mut c_void, trace_argument: *mut c_void,
) -> UnwindReasonCode { ) -> UnwindReasonCode {
let mut ctx = save_context(); with_context(|ctx| {
let mut signal = false; let mut ctx = ctx.clone();
let mut skipping = cfg!(feature = "hide-trace"); let mut signal = false;
let mut skipping = cfg!(feature = "hide-trace");
loop { loop {
let frame = try1!(Frame::from_context(&ctx, signal)); let frame = try1!(Frame::from_context(&ctx, signal));
if !skipping { if !skipping {
let code = trace( let code = trace(
&mut UnwindContext { &mut UnwindContext {
frame: frame.as_ref(), frame: frame.as_ref(),
ctx: &mut ctx, ctx: &mut ctx,
signal, signal,
}, },
trace_argument, trace_argument,
); );
match code { match code {
UnwindReasonCode::NO_REASON => (), UnwindReasonCode::NO_REASON => (),
_ => return UnwindReasonCode::FATAL_PHASE1_ERROR, _ => 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)); if let Some(frame) = frame {
signal = frame.is_signal_trampoline(); if skipping {
} else { if frame.initial_address() == _Unwind_Backtrace as usize {
return UnwindReasonCode::END_OF_STACK; skipping = false;
}
}
ctx = try1!(frame.unwind(&ctx));
signal = frame.is_signal_trampoline();
} else {
return UnwindReasonCode::END_OF_STACK;
}
} }
} })
} }