Skip to content

Commit

Permalink
WIP unwinding support
Browse files Browse the repository at this point in the history
  • Loading branch information
Amanieu committed Nov 2, 2016
1 parent e485c38 commit d07564c
Show file tree
Hide file tree
Showing 10 changed files with 403 additions and 61 deletions.
66 changes: 57 additions & 9 deletions src/arch/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@
// unwinding at the swap call site instead of falling off the end of context stack.
use core::mem;
use arch::StackPointer;
use unwind;

pub const STACK_ALIGNMENT: usize = 16;

pub unsafe fn init(stack_base: *mut u8, f: unsafe extern "C" fn(usize, StackPointer)) -> StackPointer {
pub unsafe fn init(stack_base: *mut u8, f: unsafe fn(usize, StackPointer)) -> StackPointer {
#[cfg(not(target_vendor = "apple"))]
#[naked]
unsafe extern "C" fn trampoline_1() {
Expand Down Expand Up @@ -123,13 +124,10 @@ pub unsafe fn init(stack_base: *mut u8, f: unsafe extern "C" fn(usize, StackPoin
# trampoline_2.
nop
# Call the provided function.
ldr x2, [sp, #16]
blr x2
# Clear the stack pointer. We can't call into this context any more once
# the function has returned.
mov x1, #0
# Call unwind_wrapper with the provided function and the stack base address.
add x2, sp, #32
ldr x3, [sp, #16]
bl ${0}
# Restore the stack pointer of the parent context. No CFI adjustments
# are needed since we have the same stack frame as trampoline_1.
Expand All @@ -142,11 +140,22 @@ pub unsafe fn init(stack_base: *mut u8, f: unsafe extern "C" fn(usize, StackPoin
.cfi_restore x29
.cfi_restore x30
# If the returned value is nonzero, trigger an unwind in the parent
# context with the given exception object.
cbnz x0, ${1}
# Clear the stack pointer. We can't call into this context any more once
# the function has returned.
mov x1, #0
# Return into the parent context. Use `br` instead of a `ret` to avoid
# return address mispredictions.
br x30
"#
: : : : "volatile")
:
: "s" (unwind::unwind_wrapper as usize)
"s" (unwind::start_unwind as usize)
: : "volatile")
}

// We set up the stack in a somewhat special way so that to the unwinder it
Expand Down Expand Up @@ -264,3 +273,42 @@ pub unsafe fn swap(arg: usize, new_sp: StackPointer) -> (usize, StackPointer) {
: "volatile", "alignstack");
(ret, mem::transmute(ret_sp))
}

#[inline(always)]
pub unsafe fn unwind(new_sp: StackPointer, new_stack_base: *mut u8) {
// Argument to pass to start_unwind, based on the stack base address.
let arg = unwind::unwind_arg(new_stack_base);

// This is identical to swap_link, except that it performs a tail call to
// start_unwind instead of returning into the target context.
asm!(
r#"
adr x30, 0f
stp x29, x30, [sp, #-16]!
mov x1, sp
str x1, [x3, #-32]
mov sp, x2
ldp x29, x30, [sp], #16
# Jump to the start_unwind function, which will force a stack unwind in
# the target context. This will eventually return to us through the
# stack link.
b ${0}@plt
0:
"#
:
: "s" (unwind::start_unwind as usize)
"{x0}" (arg)
"{x2}" (new_sp.0)
"{x3}" (new_stack_base)
: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
"x24", "x25", "x26", "x27", "x28",/*fp,*/ "lr", /*sp,*/
"v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
"v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15",
"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23",
"v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31",
"cc", "memory"
: "volatile", "alignstack");
}
10 changes: 5 additions & 5 deletions src/arch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ mod tests {

#[test]
fn context() {
unsafe extern "C" fn adder(arg: usize, stack_ptr: StackPointer) {
unsafe fn adder(arg: usize, stack_ptr: StackPointer) {
println!("it's alive! arg: {}", arg);
let (arg, stack_ptr) = arch::swap(arg + 1, stack_ptr);
println!("still alive! arg: {}", arg);
Expand All @@ -68,7 +68,7 @@ mod tests {

#[test]
fn context_simd() {
unsafe extern "C" fn permuter(arg: usize, stack_ptr: StackPointer) {
unsafe fn permuter(arg: usize, stack_ptr: StackPointer) {
// This will crash if the stack is not aligned properly.
let x = simd::i32x4::splat(arg as i32);
let y = x * x;
Expand All @@ -91,7 +91,7 @@ mod tests {
}
}

unsafe extern "C" fn do_panic(arg: usize, stack_ptr: StackPointer) {
unsafe fn do_panic(arg: usize, stack_ptr: StackPointer) {
match arg {
0 => panic!("arg=0"),
1 => {
Expand Down Expand Up @@ -127,7 +127,7 @@ mod tests {

#[test]
fn ret() {
unsafe extern "C" fn ret2(_: usize, _: StackPointer) {}
unsafe fn ret2(_: usize, _: StackPointer) {}

unsafe {
let stack = OsStack::new(4 << 20).unwrap();
Expand All @@ -140,7 +140,7 @@ mod tests {

#[bench]
fn swap(b: &mut test::Bencher) {
unsafe extern "C" fn loopback(mut arg: usize, mut stack_ptr: StackPointer) {
unsafe fn loopback(mut arg: usize, mut stack_ptr: StackPointer) {
// This deliberately does not ignore arg, to measure the time it takes
// to move the return value between registers.
loop {
Expand Down
80 changes: 71 additions & 9 deletions src/arch/or1k.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@
// unwinding at the swap call site instead of falling off the end of context stack.
use core::mem;
use arch::StackPointer;
use unwind;

pub const STACK_ALIGNMENT: usize = 4;

pub unsafe fn init(stack_base: *mut u8, f: unsafe extern "C" fn(usize, StackPointer)) -> StackPointer {
pub unsafe fn init(stack_base: *mut u8, f: unsafe fn(usize, StackPointer)) -> StackPointer {
#[naked]
unsafe extern "C" fn trampoline_1() {
asm!(
Expand Down Expand Up @@ -101,15 +102,12 @@ pub unsafe fn init(stack_base: *mut u8, f: unsafe extern "C" fn(usize, StackPoin
# trampoline_2.
l.nop
# Call the provided function.
l.lwz r5, 8(r1)
l.jalr r5
# Call unwind_wrapper with the provided function and the stack base address.
l.addi r5, r1, 12
l.lwz r6, 8(r1)
l.jal ${0}
l.nop
# Clear the stack pointer. We can't call into this context any more once
# the function has returned.
l.or r4, r0, r0
# Restore the stack pointer of the parent context. No CFI adjustments
# are needed since we have the same stack frame as trampoline_1.
l.lwz r1, 0(r1)
Expand All @@ -118,11 +116,24 @@ pub unsafe fn init(stack_base: *mut u8, f: unsafe extern "C" fn(usize, StackPoin
l.lwz r2, -4(r1)
l.lwz r9, -8(r1)
# If the returned value is nonzero, trigger an unwind in the parent
# context with the given exception object.
l.or r4, r0, r11
l.sfeq r11, r0
l.bf ${1}
# Clear the stack pointer. We can't call into this context any more once
# the function has returned.
l.or r4, r0, r0
# Return into the parent context.
l.jr r9
l.nop
"#
: : : : "volatile")
:
: "s" (unwind::unwind_wrapper as usize)
"s" (unwind::start_unwind as usize)
: : "volatile")
}

// We set up the stack in a somewhat special way so that to the unwinder it
Expand Down Expand Up @@ -253,3 +264,54 @@ pub unsafe fn swap(arg: usize, new_sp: StackPointer) -> (usize, StackPointer) {
: "volatile");
(ret, mem::transmute(ret_sp))
}

#[inline(always)]
pub unsafe fn unwind(new_sp: StackPointer, new_stack_base: *mut u8) {
// Argument to pass to start_unwind, based on the stack base address.
let arg = unwind::unwind_arg(new_stack_base);

// This is identical to swap_link, except that it performs a tail call to
// start_unwind instead of returning into the target context.
#[naked]
unsafe extern "C" fn trampoline() {
asm!(
r#"
l.sw -4(r1), r2
l.sw -8(r1), r9
.cfi_offset r2, -4
.cfi_offset r9, -8
l.addi r7, r1, -8
l.sw -8(r6), r7
l.or r1, r0, r5
l.lwz r2, -4(r1)
l.lwz r9, -8(r1)
# Jump to the start_unwind function, which will force a stack unwind in
# the target context. This will eventually return to us through the
# stack link.
l.j ${0}
l.nop
"#
:
: "s" (unwind::start_unwind as usize)
: : "volatile")
}

asm!(
r#"
# Call the trampoline to switch to the new context.
l.jal ${0}@plt
l.nop
"#
:
: "s" (trampoline as usize)
"{r3}" (arg)
"{r5}" (new_sp.0)
"{r6}" (new_stack_base)
:/*"r0", "r1", "r2",*/"r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
"cc", "memory"
: "volatile");
}
Loading

0 comments on commit d07564c

Please sign in to comment.