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]
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)

View File

@ -61,39 +61,39 @@ impl ops::IndexMut<gimli::Register> 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)
);
}

View File

@ -61,39 +61,39 @@ impl ops::IndexMut<gimli::Register> 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)
);
}

View File

@ -56,28 +56,40 @@ impl ops::IndexMut<gimli::Register> 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)
);

View File

@ -58,27 +58,35 @@ impl ops::IndexMut<gimli::Register> 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)

View File

@ -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: 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,
@ -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;
}
}
}
})
}