From a9bec9e719298f3961dae89aa55dd22d144bffaf Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sun, 7 May 2023 01:00:25 +0100 Subject: [PATCH] Change save_context to invoke callback instead --- src/unwinder/arch/aarch64.rs | 35 ++++-- src/unwinder/arch/riscv32.rs | 107 +++++++++++------ src/unwinder/arch/riscv64.rs | 107 +++++++++++------ src/unwinder/arch/x86.rs | 42 ++++--- src/unwinder/arch/x86_64.rs | 36 +++--- src/unwinder/mod.rs | 222 ++++++++++++++++++++--------------- 6 files changed, 342 insertions(+), 207 deletions(-) diff --git a/src/unwinder/arch/aarch64.rs b/src/unwinder/arch/aarch64.rs index abe588c..f65332c 100644 --- a/src/unwinder/arch/aarch64.rs +++ b/src/unwinder/arch/aarch64.rs @@ -59,25 +59,34 @@ impl ops::IndexMut for Context { } #[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. unsafe { asm!( " - stp d8, d9, [x8, 0x140] - stp d10, d11, [x8, 0x150] - stp d12, d13, [x8, 0x160] - 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] + stp x29, x30, [sp, -16]! + sub sp, sp, 512 + mov x8, x0 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 ", options(noreturn) diff --git a/src/unwinder/arch/riscv32.rs b/src/unwinder/arch/riscv32.rs index e845bdd..1e8709e 100644 --- a/src/unwinder/arch/riscv32.rs +++ b/src/unwinder/arch/riscv32.rs @@ -61,39 +61,39 @@ impl ops::IndexMut for Context { macro_rules! code { (save_gp) => { " - sw x0, 0x00(a0) - sw ra, 0x04(a0) - sw sp, 0x08(a0) - sw gp, 0x0C(a0) - sw tp, 0x10(a0) - sw s0, 0x20(a0) - sw s1, 0x24(a0) - sw s2, 0x48(a0) - sw s3, 0x4C(a0) - sw s4, 0x50(a0) - sw s5, 0x54(a0) - sw s6, 0x58(a0) - sw s7, 0x5C(a0) - sw s8, 0x60(a0) - sw s9, 0x64(a0) - sw s10, 0x68(a0) - sw s11, 0x6C(a0) + sw x0, 0x00(sp) + sw ra, 0x04(sp) + sw t0, 0x08(sp) + sw gp, 0x0C(sp) + sw tp, 0x10(sp) + sw s0, 0x20(sp) + sw s1, 0x24(sp) + sw s2, 0x48(sp) + sw s3, 0x4C(sp) + sw s4, 0x50(sp) + sw s5, 0x54(sp) + sw s6, 0x58(sp) + sw s7, 0x5C(sp) + sw s8, 0x60(sp) + sw s9, 0x64(sp) + sw s10, 0x68(sp) + sw s11, 0x6C(sp) " }; (save_fp) => { " - fsd fs0, 0xC0(a0) - fsd fs1, 0xC8(a0) - fsd fs2, 0x110(a0) - fsd fs3, 0x118(a0) - fsd fs4, 0x120(a0) - fsd fs5, 0x128(a0) - fsd fs6, 0x130(a0) - fsd fs7, 0x138(a0) - fsd fs8, 0x140(a0) - fsd fs9, 0x148(a0) - fsd fs10, 0x150(a0) - fsd fs11, 0x158(a0) + fsd fs0, 0xC0(sp) + fsd fs1, 0xC8(sp) + fsd fs2, 0x110(sp) + fsd fs3, 0x118(sp) + fsd fs4, 0x120(sp) + fsd fs5, 0x128(sp) + fsd fs6, 0x130(sp) + fsd fs7, 0x138(sp) + fsd fs8, 0x140(sp) + fsd fs9, 0x148(sp) + fsd fs10, 0x150(sp) + fsd fs11, 0x158(sp) " }; (restore_gp) => { @@ -169,18 +169,48 @@ macro_rules! code { } #[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. #[cfg(target_feature = "d")] unsafe { 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) ); } #[cfg(not(target_feature = "d"))] 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")] unsafe { 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) ); } #[cfg(not(target_feature = "d"))] unsafe { asm!( - concat!(code!(restore_gp), "lw a0, 0x28(a0)\nret"), + code!(restore_gp), + " + lw a0, 0x28(a0) + ret + ", options(noreturn) ); } diff --git a/src/unwinder/arch/riscv64.rs b/src/unwinder/arch/riscv64.rs index 8a2413c..a680d70 100644 --- a/src/unwinder/arch/riscv64.rs +++ b/src/unwinder/arch/riscv64.rs @@ -61,39 +61,39 @@ impl ops::IndexMut for Context { macro_rules! code { (save_gp) => { " - sd x0, 0x00(a0) - sd ra, 0x08(a0) - sd sp, 0x10(a0) - sd gp, 0x18(a0) - sd tp, 0x20(a0) - sd s0, 0x40(a0) - sd s1, 0x48(a0) - sd s2, 0x90(a0) - sd s3, 0x98(a0) - sd s4, 0xA0(a0) - sd s5, 0xA8(a0) - sd s6, 0xB0(a0) - sd s7, 0xB8(a0) - sd s8, 0xC0(a0) - sd s9, 0xC8(a0) - sd s10, 0xD0(a0) - sd s11, 0xD8(a0) + sd x0, 0x00(sp) + sd ra, 0x08(sp) + sd t0, 0x10(sp) + sd gp, 0x18(sp) + sd tp, 0x20(sp) + sd s0, 0x40(sp) + sd s1, 0x48(sp) + sd s2, 0x90(sp) + sd s3, 0x98(sp) + sd s4, 0xA0(sp) + sd s5, 0xA8(sp) + sd s6, 0xB0(sp) + sd s7, 0xB8(sp) + sd s8, 0xC0(sp) + sd s9, 0xC8(sp) + sd s10, 0xD0(sp) + sd s11, 0xD8(sp) " }; (save_fp) => { " - fsd fs0, 0x140(a0) - fsd fs1, 0x148(a0) - fsd fs2, 0x190(a0) - fsd fs3, 0x198(a0) - fsd fs4, 0x1A0(a0) - fsd fs5, 0x1A8(a0) - fsd fs6, 0x1B0(a0) - fsd fs7, 0x1B8(a0) - fsd fs8, 0x1C0(a0) - fsd fs9, 0x1C8(a0) - fsd fs10, 0x1D0(a0) - fsd fs11, 0x1D8(a0) + fsd fs0, 0x140(sp) + fsd fs1, 0x148(sp) + fsd fs2, 0x190(sp) + fsd fs3, 0x198(sp) + fsd fs4, 0x1A0(sp) + fsd fs5, 0x1A8(sp) + fsd fs6, 0x1B0(sp) + fsd fs7, 0x1B8(sp) + fsd fs8, 0x1C0(sp) + fsd fs9, 0x1C8(sp) + fsd fs10, 0x1D0(sp) + fsd fs11, 0x1D8(sp) " }; (restore_gp) => { @@ -169,18 +169,48 @@ macro_rules! code { } #[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. #[cfg(target_feature = "d")] unsafe { 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) ); } #[cfg(not(target_feature = "d"))] 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")] unsafe { 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) ); } #[cfg(not(target_feature = "d"))] unsafe { asm!( - concat!(code!(restore_gp), "ld a0, 0x50(a0)\nret"), + code!(restore_gp), + " + ld a0, 0x50(a0) + ret + ", options(noreturn) ); } diff --git a/src/unwinder/arch/x86.rs b/src/unwinder/arch/x86.rs index 181378c..696ad3d 100644 --- a/src/unwinder/arch/x86.rs +++ b/src/unwinder/arch/x86.rs @@ -56,28 +56,40 @@ impl ops::IndexMut for Context { } #[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. unsafe { asm!( " - mov eax, [esp + 4] - mov [eax + 4], ecx - mov [eax + 8], edx - mov [eax + 12], ebx - mov [eax + 20], ebp - mov [eax + 24], esi - mov [eax + 28], edi + sub esp, 44 + + mov [esp + 4], ecx + mov [esp + 8], edx + mov [esp + 12], ebx /* Adjust the stack to account for the return address */ - lea edx, [esp + 4] - mov [eax + 16], edx + lea eax, [esp + 48] + mov [esp + 16], eax - mov edx, [esp] - mov [eax + 32], edx - stmxcsr [eax + 36] - fnstcw [eax + 40] - ret 4 + mov [esp + 20], ebp + mov [esp + 24], esi + mov [esp + 28], edi + + /* 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) ); diff --git a/src/unwinder/arch/x86_64.rs b/src/unwinder/arch/x86_64.rs index 4d0fbc7..4387686 100644 --- a/src/unwinder/arch/x86_64.rs +++ b/src/unwinder/arch/x86_64.rs @@ -58,27 +58,35 @@ impl ops::IndexMut for Context { } #[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. unsafe { asm!( " - mov rax, rdi - mov [rax + 0x18], rbx - mov [rax + 0x30], rbp + sub rsp, 0x98 + mov [rsp + 0x18], rbx + mov [rsp + 0x30], rbp /* Adjust the stack to account for the return address */ - lea rdi, [rsp + 8] - mov [rax + 0x38], rdi + lea rax, [rsp + 0xA0] + mov [rsp + 0x38], rax - mov [rax + 0x60], r12 - mov [rax + 0x68], r13 - mov [rax + 0x70], r14 - mov [rax + 0x78], r15 - mov rdx, [rsp] - mov [rax + 0x80], rdx - stmxcsr [rax + 0x88] - fnstcw [rax + 0x90] + mov [rsp + 0x60], r12 + mov [rsp + 0x68], r13 + mov [rsp + 0x70], r14 + mov [rsp + 0x78], r15 + + /* Return address */ + mov rax, [rsp + 0x98] + mov [rsp + 0x80], rax + + stmxcsr [rsp + 0x88] + fnstcw [rsp + 0x90] + + mov rax, rdi + mov rdi, rsp + call rax + add rsp, 0x98 ret ", options(noreturn) diff --git a/src/unwinder/mod.rs b/src/unwinder/mod.rs index 07ee029..3309f47 100644 --- a/src/unwinder/mod.rs +++ b/src/unwinder/mod.rs @@ -16,6 +16,33 @@ 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: F) -> T { + use core::mem::ManuallyDrop; + + union Data { + f: ManuallyDrop, + t: ManuallyDrop, + } + + extern "C" fn delegate 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::>(); + let t = ManuallyDrop::take(&mut data.f)(ctx); + data.t = ManuallyDrop::new(t); + } + } + + let mut data = Data { + f: ManuallyDrop::new(f), + }; + save_context(delegate::, ptr::addr_of_mut!(data).cast()); + unsafe { ManuallyDrop::into_inner(data.t) } +} + #[repr(C)] pub struct UnwindException { pub exception_class: u64, @@ -125,53 +152,52 @@ macro_rules! try2 { pub extern "C-unwind" fn _Unwind_RaiseException( exception: &mut UnwindException, ) -> 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 - 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, - }, - ); - - match result { - UnwindReasonCode::CONTINUE_UNWIND => (), - UnwindReasonCode::HANDLER_FOUND => { - break; + match result { + UnwindReasonCode::CONTINUE_UNWIND => (), + UnwindReasonCode::HANDLER_FOUND => { + break; + } + _ => return UnwindReasonCode::FATAL_PHASE1_ERROR, } - _ => 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. - let handler_cfa = ctx[Arch::SP] - signal as usize; - exception.private_1 = None; - exception.private_2 = handler_cfa; + // Disambiguate normal frame and signal frame. + let handler_cfa = ctx[Arch::SP] - signal as usize; + exception.private_1 = None; + exception.private_2 = handler_cfa; - 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, - } + 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( @@ -225,16 +251,16 @@ pub extern "C-unwind" fn _Unwind_ForcedUnwind( stop: UnwindStopFn, stop_arg: *mut c_void, ) -> 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); - 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, - } + let code = force_unwind_phase2(exception, ctx, stop, stop_arg); + match code { + UnwindReasonCode::INSTALL_CONTEXT => unsafe { restore_context(ctx) }, + _ => code, + } + }) } fn force_unwind_phase2( @@ -304,21 +330,21 @@ fn force_unwind_phase2( #[inline(never)] #[no_mangle] 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 { - 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) } + unsafe { restore_context(ctx) } + }) } #[inline(never)] @@ -331,13 +357,13 @@ pub extern "C-unwind" fn _Unwind_Resume_or_Rethrow( 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 _; - let code = force_unwind_phase2(exception, &mut ctx, stop, stop_arg); - assert!(code == UnwindReasonCode::INSTALL_CONTEXT); - - unsafe { restore_context(&ctx) } + unsafe { restore_context(ctx) } + }) } #[no_mangle] @@ -353,36 +379,38 @@ pub extern "C-unwind" fn _Unwind_Backtrace( trace: UnwindTraceFn, trace_argument: *mut c_void, ) -> UnwindReasonCode { - let mut ctx = save_context(); - let mut signal = false; - let mut skipping = cfg!(feature = "hide-trace"); + 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( - &mut 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; + loop { + let frame = try1!(Frame::from_context(&ctx, signal)); + if !skipping { + let code = trace( + &mut UnwindContext { + frame: frame.as_ref(), + ctx: &mut ctx, + signal, + }, + trace_argument, + ); + match code { + UnwindReasonCode::NO_REASON => (), + _ => return UnwindReasonCode::FATAL_PHASE1_ERROR, } } - ctx = try1!(frame.unwind(&ctx)); - signal = frame.is_signal_trampoline(); - } else { - return UnwindReasonCode::END_OF_STACK; + 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; + } } - } + }) }