Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix various issues #33

Merged
merged 4 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions ci/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ export RUST_TEST_THREADS=1
"${CARGO}" test $CARGO_TEST_FLAGS --target "${TARGET}" --all-targets --release

# asm-unwind
if [ "${CHANNEL}" = "nightly" ]; then
"${CARGO}" test $CARGO_TEST_FLAGS --target "${TARGET}" --all-targets --features asm-unwind
"${CARGO}" test $CARGO_TEST_FLAGS --target "${TARGET}" --all-targets --features asm-unwind --release
fi
# Currently disabled because of LLVM issues.
#if [ "${CHANNEL}" = "nightly" ]; then
# "${CARGO}" test $CARGO_TEST_FLAGS --target "${TARGET}" --all-targets --features asm-unwind
# "${CARGO}" test $CARGO_TEST_FLAGS --target "${TARGET}" --all-targets --features asm-unwind --release
#fi
10 changes: 5 additions & 5 deletions src/arch/x86.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ pub unsafe fn switch_and_link(
// Push a return address onto our stack and then jump to the return
// address at the top of the coroutine stack.
//
// From here on execution continues in stack_init_trampoline or the 0:
// From here on execution continues in stack_init_trampoline or the 3:
// label in switch_yield.
"call [eax]",

Expand Down Expand Up @@ -291,7 +291,7 @@ pub unsafe fn switch_yield(arg: EncodedValue, parent_link: *mut StackPointer) ->
// point to "0". We use an intermediate constant here to work around a
// limitation of LLVM's Intel syntax parser which doesn't support 2
// symbols in an expression.
".equ .Loffset_yield, 0f - 2b",
".equ .Loffset_yield, 3f - 2b",
"add dword ptr [esp], offset .Loffset_yield",

// Save our stack pointer to EDX, which is then returned out of
Expand Down Expand Up @@ -325,7 +325,7 @@ pub unsafe fn switch_yield(arg: EncodedValue, parent_link: *mut StackPointer) ->
// - EAX points to the top of our stack.
// - EDX points to the base of our stack.
// - ECX contains the argument passed from switch_and_link.
"0:",
"3:",

// Save the EBP of the parent context to the parent stack.
"push ebp",
Expand Down Expand Up @@ -402,7 +402,7 @@ pub unsafe fn switch_and_throw(
// about how this code works.
"call 2f",
"2:",
".equ .Loffset_throw, 0f - 2b",
".equ .Loffset_throw, 3f - 2b",
"add dword ptr [esp], offset .Loffset_throw",

// Save EBP of the parent context.
Expand Down Expand Up @@ -437,7 +437,7 @@ pub unsafe fn switch_and_throw(

// Upon returning, our register state is just like a normal return into
// switch_and_link().
"0:",
"3:",

// Restore registers just like the second half of switch_and_link.
"pop esi",
Expand Down
10 changes: 5 additions & 5 deletions src/arch/x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ pub unsafe fn switch_and_link(
// Push a return address onto our stack and then jump to the return
// address at the top of the coroutine stack.
//
// From here on execution continues in stack_init_trampoline or the 0:
// From here on execution continues in stack_init_trampoline or the 2:
// label in switch_yield.
"call [rdx]",

Expand Down Expand Up @@ -423,7 +423,7 @@ pub unsafe fn switch_yield(arg: EncodedValue, parent_link: *mut StackPointer) ->

// Push a return address on the stack. This is the address that will be
// called by switch_and_link() the next time this context is resumed.
"lea rax, [rip + 0f]",
"lea rax, [rip + 2f]",
"push rax",

// Save our stack pointer to RSI, which is then returned out of
Expand Down Expand Up @@ -458,7 +458,7 @@ pub unsafe fn switch_yield(arg: EncodedValue, parent_link: *mut StackPointer) ->
// - RDX points to the top of our stack, including the return address.
// - RSI points to the base of our stack.
// - RDI contains the argument passed from switch_and_link.
"0:",
"2:",

// Save the RBP of the parent context to the parent stack. When combined
// with the return address this forms a valid frame record (RBP & RIP)
Expand Down Expand Up @@ -554,7 +554,7 @@ pub unsafe fn switch_and_throw(
"push rbx",

// Push a return address to the stack.
"lea rax, [rip + 0f]",
"lea rax, [rip + 2f]",
"push rax",

// Save RBP of the parent context.
Expand Down Expand Up @@ -589,7 +589,7 @@ pub unsafe fn switch_and_throw(

// Upon returning, our register state is just like a normal return into
// switch_and_link().
"0:",
"2:",

// Restore registers just like the second half of switch_and_link.
"pop rbx",
Expand Down
14 changes: 7 additions & 7 deletions src/arch/x86_64_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ pub unsafe fn switch_and_link(
asm_may_unwind_root!(
// Set up a secondary copy of the return address. This is only used by
// the unwinder, not by actual returns.
"lea rax, [rip + 0f]",
"lea rax, [rip + 2f]",
"push rax",

// Save the TEB fields to the stack.
Expand All @@ -345,7 +345,7 @@ pub unsafe fn switch_and_link(
// Push a return address onto our stack and then jump to the return
// address at the top of the coroutine stack.
//
// From here on execution continues in stack_init_trampoline or the 0:
// From here on execution continues in stack_init_trampoline or the 2:
// label in switch_yield.
"call [rdx]",

Expand All @@ -354,7 +354,7 @@ pub unsafe fn switch_and_link(
// - RSI: The top of the coroutine stack, or 0 if coming from
// switch_and_reset.
// - RDI: The argument passed from the coroutine.
"0:",
"2:",

"pop rbx",

Expand Down Expand Up @@ -405,7 +405,7 @@ pub unsafe fn switch_yield(arg: EncodedValue, parent_link: *mut StackPointer) ->

// Push a return address on the stack. This is the address that will be
// called by switch_and_link() the next time this context is resumed.
"lea rax, [rip + 0f]",
"lea rax, [rip + 2f]",
"push rax",

// Save our stack pointer to RSI, which is then returned out of
Expand All @@ -429,7 +429,7 @@ pub unsafe fn switch_yield(arg: EncodedValue, parent_link: *mut StackPointer) ->
// - RDX points to the top of our stack, including the return address.
// - RSI points to the base of our stack.
// - RDI contains the argument passed from switch_and_link.
"0:",
"2:",

// Save RBP from the parent context last to create a valid frame record.
"push rbp",
Expand Down Expand Up @@ -513,7 +513,7 @@ pub unsafe fn switch_and_throw(

asm_may_unwind_root!(
// Save state just like the first half of switch_and_link().
"lea rax, [rip + 0f]",
"lea rax, [rip + 2f]",
"push rax",
"push qword ptr gs:[0x1748]", // GuaranteedStackBytes
"push qword ptr gs:[0x1478]", // DeallocationStack
Expand Down Expand Up @@ -556,7 +556,7 @@ pub unsafe fn switch_and_throw(

// Upon returning, our register state is just like a normal return into
// switch_and_link().
"0:",
"2",

// This is copied from the second half of switch_and_link().
"pop rbx",
Expand Down
62 changes: 41 additions & 21 deletions src/arch/x86_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,13 +282,38 @@ global_asm!(
asm_function_end!("stack_call_trampoline"),
);

// Special trampoline to reset the SEH exception chain before calling
// trap_handler.
global_asm!(
// See stack_init_trampoline for an explanation of the assembler directives
// used here.
".balign 16",
asm_function_begin!("trap_handler_trampoline"),
// At this point our register state contains the following:
// - ESP points to the coroutine stack and holds a return address pointing
// to stack_init_trampoline_return.
// - EBP points to parent_link on the coroutine stack, forming a valid frame
// pointer chain.
// - EAX points to the initial SEH exception chain entry.
// - EBX holds the address of the trap_handler function.
// - EDX and ECX hold the arguments to trap_handler.
//
// Reset the SEH exception chain to just the initial entry pointing to
// FinalExceptionHandler.
"mov fs:[0x0], eax",
// Jump to trap_handler.
"jmp ebx",
asm_function_end!("trap_handler_trampoline"),
);

// These trampolines use a custom calling convention and should only be called
// with inline assembly.
extern "C" {
fn stack_init_trampoline(arg: EncodedValue, stack_base: StackPointer, stack_ptr: StackPointer);
fn stack_init_trampoline_return();
#[allow(dead_code)]
fn stack_call_trampoline(arg: *mut u8, sp: StackPointer, f: StackCallFunc);
fn trap_handler_trampoline();
}

/// The end of the exception handler chain is marked with 0xffffffff.
Expand Down Expand Up @@ -411,7 +436,7 @@ pub unsafe fn switch_and_link(
// Push a return address onto our stack and then jump to the return
// address at the top of the coroutine stack.
//
// From here on execution continues in stack_init_trampoline or the 0:
// From here on execution continues in stack_init_trampoline or the 3:
// label in switch_yield.
"call [eax]",

Expand All @@ -420,7 +445,6 @@ pub unsafe fn switch_and_link(
// - EDX: The top of the coroutine stack, or 0 if coming from
// switch_and_reset.
// - ECX: The argument passed from the coroutine.
"0:",

"pop esi",

Expand Down Expand Up @@ -480,7 +504,7 @@ pub unsafe fn switch_yield(arg: EncodedValue, parent_link: *mut StackPointer) ->
// point to "0". We use an intermediate constant here to work around a
// limitation of LLVM's Intel syntax parser which doesn't support 2
// symbols in an expression.
".equ .Loffset_yield, 0f - 2b",
".equ .Loffset_yield, 3f - 2b",
"add dword ptr [esp], offset .Loffset_yield",

// Save our stack pointer to EDX, which is then returned out of
Expand Down Expand Up @@ -514,7 +538,7 @@ pub unsafe fn switch_yield(arg: EncodedValue, parent_link: *mut StackPointer) ->
// - EAX points to the top of our stack.
// - EDX points to the base of our stack.
// - ECX contains the argument passed from switch_and_link.
"0:",
"3:",

// Save the EBP of the parent context to the parent stack.
"push ebp",
Expand Down Expand Up @@ -611,7 +635,7 @@ pub unsafe fn switch_and_throw(
// about how this code works.
"call 2f",
"2:",
".equ .Loffset_throw, 0f - 2b",
".equ .Loffset_throw, 3f - 2b",
"add dword ptr [esp], offset .Loffset_throw",

// Save EBP of the parent context.
Expand Down Expand Up @@ -645,7 +669,7 @@ pub unsafe fn switch_and_throw(

// Upon returning, our register state is just like a normal return into
// switch_and_link().
"0:",
"3:",

// This is copied from the second half of switch_and_link().
"pop esi",
Expand Down Expand Up @@ -753,6 +777,8 @@ pub struct TrapHandlerRegs {
pub eip: u32,
pub esp: u32,
pub ebp: u32,
pub eax: u32,
pub ebx: u32,
pub ecx: u32,
pub edx: u32,
}
Expand All @@ -776,10 +802,18 @@ pub unsafe fn setup_trap_trampoline<T>(
// Set up a return address which returns to stack_init_trampoline.
push(&mut sp, Some(stack_init_trampoline_return as StackWord));

// Since we reset the stack offset back to the base of the coroutine stack,
// we also need to reset the SEH ExceptionList chain in the TIB to just the
// initial FinalExceptionHandler. This is done by pointing to a small
// trampoline that runs before any other code is executed.
let initial_seh_handler = parent_link - 8;

// Set up registers for entry into the function.
TrapHandlerRegs {
eip: handler as u32,
eip: trap_handler_trampoline as u32,
esp: sp as u32,
eax: initial_seh_handler as u32,
ebx: handler as u32,
ecx: val_ptr as u32,
edx: parent_link as u32,
ebp: parent_link as u32,
Expand Down Expand Up @@ -824,17 +858,3 @@ pub unsafe fn on_stack<S: Stack>(arg: *mut u8, stack: S, f: StackCallFunc) {
clobber_abi("fastcall"),
);
}

/// The trap handler will have reset our stack offset back to the base of the
/// coroutine stack, but it won't have reset the SEH ExceptionList chain in the
/// TIB. We need to manually reset it here before executing any user code which
/// might raise an exception.
pub unsafe fn reset_seh_handler(parent_link: *mut StackPointer) {
// The initial exception record is conveniently located just below the
// parent link.
let exception_record = parent_link as usize - 8;

// Write to the ExceptionList field in the TIB, just like on entry to the
// coroutine.
asm!("mov fs:[0x0], {}", in(reg) exception_record, options(nostack, preserves_flags));
}
9 changes: 6 additions & 3 deletions src/tests/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ fn panics_propagated() {
let a = Rc::new(Cell::new(false));
let b = SetOnDrop(a.clone());
let mut coroutine = Coroutine::<(), (), ()>::new(move |_, ()| {
drop(&b);
drop(b);
panic!("foobar");
});
let result = panic::catch_unwind(AssertUnwindSafe(|| coroutine.resume(())));
Expand All @@ -121,7 +121,7 @@ fn panics_propagated_via_parent() {
let a = Rc::new(Cell::new(false));
let b = SetOnDrop(a.clone());
let mut coroutine = Coroutine::<(), (), ()>::new(move |y, ()| {
drop(&b);
drop(b);
y.on_parent_stack(|| {
panic!("foobar");
});
Expand Down Expand Up @@ -157,6 +157,7 @@ fn suspend_and_resume_values() {
#[test]
fn stateful() {
#[repr(align(128))]
#[allow(dead_code)]
struct Aligned(u8);
let state = [41, 42, 43, 44, 45];
let aligned = Aligned(100);
Expand Down Expand Up @@ -641,10 +642,12 @@ mod trap_handler {
(*(*exception_info).ContextRecord).Rdi = rdi;
(*(*exception_info).ContextRecord).Rsi = rsi;
} else if #[cfg(target_arch = "x86")] {
let TrapHandlerRegs { eip, esp, ebp, ecx, edx } = regs;
let TrapHandlerRegs { eip, esp, ebp,eax, ebx, ecx, edx } = regs;
(*(*exception_info).ContextRecord).Eip = eip;
(*(*exception_info).ContextRecord).Esp = esp;
(*(*exception_info).ContextRecord).Ebp = ebp;
(*(*exception_info).ContextRecord).Eax = eax;
(*(*exception_info).ContextRecord).Ebx = ebx;
(*(*exception_info).ContextRecord).Ecx = ecx;
(*(*exception_info).ContextRecord).Edx = edx;
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/tests/on_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ fn panics_propagated() {
let b = SetOnDrop(a.clone());
let result = panic::catch_unwind(AssertUnwindSafe(move || {
on_stack(DefaultStack::default(), move || {
drop(&b);
drop(b);
panic!("foobar");
})
}));
Expand Down
6 changes: 0 additions & 6 deletions src/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,6 @@ impl<Return> CoroutineTrapHandler<Return> {
f: *mut F,
parent_link: *mut StackPointer
) -> ! {
// After returning from the exception handler we may have an
// invalid SEH exception chain. We need to reset it to the
// exception record at the root of the stack.
#[cfg(all(windows, target_arch = "x86"))]
arch::reset_seh_handler(parent_link);

// This must be called after a stack overflow exception, but it
// doesn't hurt to call it for other exception types as well.
#[cfg(windows)]
Expand Down
Loading