Skip to content
13 changes: 7 additions & 6 deletions unwind/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ use std::env;

fn main() {
match env::var("CARGO_FEATURE_NIGHTLY") {
Err(env::VarError::NotPresent) => {
gcc::Build::new()
.file("src/unwind_helper.c")
.compile("unwind_helper");
},
_ => ()
Err(env::VarError::NotPresent) => (),
_ => return
}

gcc::Build::new()
.file(format!("src/glue/{}_helper.S", env::var("CARGO_CFG_TARGET_ARCH").expect("Didn't run with cargo")))
.include("src/glue")
.compile("unwind_helper");
}
8 changes: 4 additions & 4 deletions unwind/src/find_cfi/baremetal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use super::EhRef;
extern "C" {
static __text_start: usize;
static __text_end: usize;
static __ehframehdr_start: usize;
static __ehframehdr_end: usize;
static __ehframe_end: usize;
static __eh_frame_hdr_start: usize;
static __eh_frame_hdr_end: usize;
static __eh_frame_end: usize;
}

pub fn find_cfi_sections() -> Vec<EhRef> {
Expand All @@ -18,7 +18,7 @@ pub fn find_cfi_sections() -> Vec<EhRef> {
let text_end = &__text_end as *const _ as u64;
let cfi_start = &__ehframehdr_start as *const _ as u64;
let cfi_end = &__ehframehdr_end as *const _ as u64;
let ehframe_end = &__ehframe_end as *const _ as u64;
let ehframe_end = &__eh_frame_end as *const _ as u64;

cfi.push(EhRef {
obj_base: 0,
Expand Down
138 changes: 138 additions & 0 deletions unwind/src/glue/aarch64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use {UnwindPayload, StackFrames};
use registers::{Registers, DwarfRegisterAArch64};

#[allow(improper_ctypes)] // trampoline just forwards the ptr
extern "C" {
pub fn unwind_trampoline(payload: *mut UnwindPayload);
fn unwind_lander(regs: *const LandingRegisters);
}

#[cfg(feature = "nightly")]
global_asm! {
r#"
.global unwind_trampoline
unwind_trampoline:
.cfi_startproc
mov x1, sp
sub sp, sp, 0xA0
.cfi_adjust_cfa_offset 0xA0
stp x19, x20, [sp, #0x00]
stp x21, x22, [sp, #0x10]
stp x23, x24, [sp, #0x20]
stp x25, x26, [sp, #0x30]
stp x27, x28, [sp, #0x40]
stp x29, lr, [sp, #0x50]
.cfi_rel_offset lr, 0x58
stp d8, d9, [sp, #0x60]
stp d10, d11, [sp, #0x70]
stp d12, d13, [sp, #0x80]
stp d14, d15, [sp, #0x90]
mov x2, sp
bl unwind_recorder
ldr lr, [sp, #0x58]
.cfi_restore lr
add sp, sp, 0xA0
.cfi_adjust_cfa_offset -0xA0
ret
.cfi_endproc

.global unwind_lander
unwind_lander:
ldp x2, x3, [x0, #0x010]
ldp x4, x5, [x0, #0x020]
ldp x6, x7, [x0, #0x030]
ldp x8, x9, [x0, #0x040]
ldp x10, x11, [x0, #0x050]
ldp x12, x13, [x0, #0x060]
ldp x14, x15, [x0, #0x070]
ldp x16, x17, [x0, #0x080]
ldp x18, x19, [x0, #0x090]
ldp x20, x21, [x0, #0x0A0]
ldp x22, x23, [x0, #0x0B0]
ldp x24, x25, [x0, #0x0C0]
ldp x26, x27, [x0, #0x0D0]
ldp x28, x29, [x0, #0x0E0]
ldp x30, x1, [x0, #0x0F0]
mov sp, x1

ldp d0, d1, [x0, #0x100]
ldp d2, d3, [x0, #0x110]
ldp d4, d5, [x0, #0x120]
ldp d6, d7, [x0, #0x130]
ldp d8, d9, [x0, #0x140]
ldp d10, d11, [x0, #0x150]
ldp d12, d13, [x0, #0x160]
ldp d14, d15, [x0, #0x170]
ldp d16, d17, [x0, #0x180]
ldp d18, d19, [x0, #0x190]
ldp d20, d21, [x0, #0x1A0]
ldp d22, d23, [x0, #0x1B0]
ldp d24, d25, [x0, #0x1C0]
ldp d26, d27, [x0, #0x1D0]
ldp d28, d29, [x0, #0x1E0]
ldp d30, d31, [x0, #0x1F0]

ldp x0, x1, [x0, #0x000]
ret x30 // HYPERSPACE JUMP :D
"#
}

#[repr(C)]
struct LandingRegisters {
r: [u64; 29], // x0-x28
fp: u64, // x29, Frame Pointer
lr: u64, // x30, Link Register
sp: u64, // x31, Stack Pointer
vector_half: [u64; 32], // d0-d31
}

// TODO: Doc hidden
#[repr(C)]
pub struct SavedRegs {
r: [u64; 11], // x19-x29
lr: u64,
vector_half: [u64; 8], // d8-d15
}

// TODO: doc hidden
#[no_mangle]
pub unsafe extern "C" fn unwind_recorder(payload: *mut UnwindPayload, stack: u64, saved_regs: *mut SavedRegs) {
let payload = &mut *payload;
let saved_regs = &*saved_regs;

let mut registers = Registers::default();
for (regnum, v) in saved_regs.r.iter().enumerate() {
registers[DwarfRegisterAArch64::X19 as u8 + regnum as u8] = Some(*v);
}
for (regnum, v) in saved_regs.vector_half.iter().enumerate() {
registers[DwarfRegisterAArch64::V8 as u8 + regnum as u8] = Some(*v);
}
registers[DwarfRegisterAArch64::SP] = Some(stack);
registers[DwarfRegisterAArch64::IP] = Some(saved_regs.lr);

let mut frames = StackFrames {
unwinder: payload.unwinder,
registers,
state: None,
};

(payload.tracer)(&mut frames);
}

pub unsafe fn land(regs: &Registers) {
let mut lr = LandingRegisters {
r: [0; 29],
fp: regs[DwarfRegisterAArch64::X29].unwrap_or(0),
lr: regs[DwarfRegisterAArch64::IP].unwrap_or(0),
sp: regs[DwarfRegisterAArch64::SP].unwrap_or(0),
vector_half: [0; 32]
};
for (i, v) in lr.r.iter_mut().enumerate() {
*v = regs[i as u8].unwrap_or(0);
}

for (i, v) in lr.vector_half.iter_mut().enumerate() {
*v = regs[DwarfRegisterAArch64::V0 as u8 + i as u8].unwrap_or(0);
}
unwind_lander(&lr);
}
14 changes: 14 additions & 0 deletions unwind/src/glue/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#[cfg(target_arch = "x86_64")]
#[path = "x86_64.rs"]
mod imp;

#[cfg(target_arch = "aarch64")]
#[path = "aarch64.rs"]
mod imp;

#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
compiler_error!("Unsupported architecture");

pub use self::imp::{unwind_trampoline, land};
// TODO: doc hidden
pub use self::imp::unwind_recorder;
66 changes: 9 additions & 57 deletions unwind/src/glue.rs → unwind/src/glue/x86_64.rs
Original file line number Diff line number Diff line change
@@ -1,63 +1,14 @@
use super::{UnwindPayload, StackFrames};
use {UnwindPayload, StackFrames};
use registers::{Registers, DwarfRegister};

#[allow(improper_ctypes)] // trampoline just forwards the ptr
extern "C" {
#[cfg(not(feature = "nightly"))]
pub fn unwind_trampoline(payload: *mut UnwindPayload);
#[cfg(not(feature = "nightly"))]
fn unwind_lander(regs: *const LandingRegisters);
}

#[cfg(feature = "nightly")]
#[naked]
pub unsafe extern fn unwind_trampoline(_payload: *mut UnwindPayload) {
asm!("
movq %rsp, %rsi
.cfi_def_cfa rsi, 8
pushq %rbp
.cfi_offset rbp, -16
pushq %rbx
pushq %r12
pushq %r13
pushq %r14
pushq %r15
movq %rsp, %rdx
subq 0x08, %rsp
.cfi_def_cfa rsp, 0x40
call unwind_recorder
addq 0x38, %rsp
.cfi_def_cfa rsp, 8
ret
");
::std::hint::unreachable_unchecked();
}

#[cfg(feature = "nightly")]
#[naked]
unsafe extern fn unwind_lander(_regs: *const LandingRegisters) {
asm!("
movq %rdi, %rsp
popq %rax
popq %rbx
popq %rcx
popq %rdx
popq %rdi
popq %rsi
popq %rbp
popq %r8
popq %r9
popq %r10
popq %r11
popq %r12
popq %r13
popq %r14
popq %r15
movq 0(%rsp), %rsp
ret // HYPERSPACE JUMP :D
");
::std::hint::unreachable_unchecked();
}
global_asm!(include_str!("x86_64_helper.S"));

#[repr(C)]
struct LandingRegisters {
Expand All @@ -82,14 +33,15 @@ struct LandingRegisters {

#[repr(C)]
pub struct SavedRegs {
pub r15: u64,
pub r14: u64,
pub r13: u64,
pub r12: u64,
pub rbx: u64,
pub rbp: u64,
r15: u64,
r14: u64,
r13: u64,
r12: u64,
rbx: u64,
rbp: u64,
}

// TODO: doc hidden
#[no_mangle]
pub unsafe extern "C" fn unwind_recorder(payload: *mut UnwindPayload, stack: u64, saved_regs: *mut SavedRegs) {
let payload = &mut *payload;
Expand Down
41 changes: 41 additions & 0 deletions unwind/src/glue/x86_64_helper.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.global unwind_trampoline
unwind_trampoline:
.cfi_startproc
movq %rsp, %rsi
.cfi_def_cfa rsi, 8
pushq %rbp
.cfi_offset rbp, -16
pushq %rbx
pushq %r12
pushq %r13
pushq %r14
pushq %r15
movq %rsp, %rdx
subq $0x08, %rsp
.cfi_def_cfa rsp, 0x40
call unwind_recorder
addq $0x38, %rsp
.cfi_def_cfa rsp, 8
ret
.cfi_endproc

.global unwind_lander
unwind_lander:
movq %rdi, %rsp
popq %rax
popq %rbx
popq %rcx
popq %rdx
popq %rdi
popq %rsi
popq %rbp
popq %r8
popq %r9
popq %r10
popq %r11
popq %r12
popq %r13
popq %r14
popq %r15
movq 0(%rsp), %rsp
ret /* HYPERSPACE JUMP :D */
6 changes: 3 additions & 3 deletions unwind/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![cfg_attr(feature = "nightly", feature(asm, naked_functions))]
#![cfg_attr(feature = "nightly", feature(global_asm))]

extern crate gimli;
extern crate libc;
Expand Down Expand Up @@ -170,7 +170,7 @@ impl<'a> FallibleIterator for StackFrames<'a> {
newregs[DwarfRegister::IP] = None;
for &(reg, ref rule) in row.registers() {
trace!("rule {} {:?}", reg, rule);
assert!(reg != 7); // stack = cfa
assert!(reg != DwarfRegister::SP as u8); // stack = cfa
newregs[reg] = match *rule {
RegisterRule::Undefined => unreachable!(), // registers[reg],
RegisterRule::SameValue => Some(registers[reg].unwrap()), // not sure why this exists
Expand All @@ -182,7 +182,7 @@ impl<'a> FallibleIterator for StackFrames<'a> {
RegisterRule::Architectural => unreachable!(),
};
}
newregs[7] = Some(cfa);
newregs[DwarfRegister::SP] = Some(cfa);

*registers = newregs;
trace!("registers:{:?}", registers);
Expand Down
30 changes: 20 additions & 10 deletions unwind/src/libunwind_shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,17 +156,27 @@ unsafe fn unwind_tracer(frames: &mut ::StackFrames, exception: *mut _Unwind_Exce
pub unsafe extern "C" fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
trace_argument: *mut c_void)
-> _Unwind_Reason_Code {
let mut reterr = _Unwind_Reason_Code::_URC_END_OF_STACK;
DwarfUnwinder::default().trace(|frames| {
while let Some(frame) = frames.next().unwrap() {
let mut ctx = _Unwind_Context {
lsda: frame.lsda.unwrap_or(0),
ip: frames.registers()[DwarfRegister::IP].unwrap(),
initial_address: frame.initial_address,
registers: frames.registers(),
};

trace(&mut ctx, trace_argument);
loop {
match frames.next() {
Ok(Some(frame)) => {
let mut ctx = _Unwind_Context {
lsda: frame.lsda.unwrap_or(0),
ip: frames.registers()[DwarfRegister::IP].unwrap(),
initial_address: frame.initial_address,
registers: frames.registers(),
};

trace(&mut ctx, trace_argument);
},
Ok(None) => break,
Err(err) => {
reterr = _Unwind_Reason_Code::_URC_FATAL_PHASE1_ERROR;
break;
}
}
}
});
_Unwind_Reason_Code::_URC_END_OF_STACK
reterr
}
Loading