diff --git a/unwind/build.rs b/unwind/build.rs index 2ebae21..63bb0f4 100644 --- a/unwind/build.rs +++ b/unwind/build.rs @@ -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"); } diff --git a/unwind/src/find_cfi/baremetal.rs b/unwind/src/find_cfi/baremetal.rs index bf9bbe7..c76b81f 100644 --- a/unwind/src/find_cfi/baremetal.rs +++ b/unwind/src/find_cfi/baremetal.rs @@ -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 { @@ -18,7 +18,7 @@ pub fn find_cfi_sections() -> Vec { 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, diff --git a/unwind/src/glue/aarch64.rs b/unwind/src/glue/aarch64.rs new file mode 100644 index 0000000..21a648d --- /dev/null +++ b/unwind/src/glue/aarch64.rs @@ -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); +} diff --git a/unwind/src/glue/mod.rs b/unwind/src/glue/mod.rs new file mode 100644 index 0000000..a57adc6 --- /dev/null +++ b/unwind/src/glue/mod.rs @@ -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; diff --git a/unwind/src/glue.rs b/unwind/src/glue/x86_64.rs similarity index 68% rename from unwind/src/glue.rs rename to unwind/src/glue/x86_64.rs index 03de7d5..ac441c7 100644 --- a/unwind/src/glue.rs +++ b/unwind/src/glue/x86_64.rs @@ -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 { @@ -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; diff --git a/unwind/src/glue/x86_64_helper.S b/unwind/src/glue/x86_64_helper.S new file mode 100644 index 0000000..982332a --- /dev/null +++ b/unwind/src/glue/x86_64_helper.S @@ -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 */ diff --git a/unwind/src/lib.rs b/unwind/src/lib.rs index 9cf97d0..5b184ca 100644 --- a/unwind/src/lib.rs +++ b/unwind/src/lib.rs @@ -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; @@ -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 @@ -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); diff --git a/unwind/src/libunwind_shim.rs b/unwind/src/libunwind_shim.rs index 58282a8..42d47b6 100644 --- a/unwind/src/libunwind_shim.rs +++ b/unwind/src/libunwind_shim.rs @@ -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 } diff --git a/unwind/src/registers.rs b/unwind/src/registers.rs index f5fdc95..94afb0c 100644 --- a/unwind/src/registers.rs +++ b/unwind/src/registers.rs @@ -1,14 +1,22 @@ use std::fmt::{Debug, Formatter, Result as FmtResult}; use std::ops::{Index, IndexMut}; -#[derive(Default, Clone, PartialEq, Eq)] +#[derive(Clone)] pub struct Registers { - registers: [Option; 17], + registers: [Option; 96], +} + +impl Default for Registers { + fn default() -> Registers { + Registers { + registers: [Default::default(); 96] + } + } } impl Debug for Registers { fn fmt(&self, fmt: &mut Formatter) -> FmtResult { - for reg in &self.registers { + for reg in &self.registers[..] { match *reg { None => write!(fmt, " XXX")?, Some(x) => write!(fmt, " 0x{:x}", x)?, @@ -32,21 +40,35 @@ impl IndexMut for Registers { } } -impl Index for Registers { +impl Index for Registers { + type Output = Option; + + fn index(&self, reg: DwarfRegisterAMD64) -> &Option { + &self[reg as u8] + } +} + +impl IndexMut for Registers { + fn index_mut(&mut self, reg: DwarfRegisterAMD64) -> &mut Option { + &mut self[reg as u8] + } +} + +impl Index for Registers { type Output = Option; - fn index(&self, reg: DwarfRegister) -> &Option { + fn index(&self, reg: DwarfRegisterAArch64) -> &Option { &self[reg as u8] } } -impl IndexMut for Registers { - fn index_mut(&mut self, reg: DwarfRegister) -> &mut Option { +impl IndexMut for Registers { + fn index_mut(&mut self, reg: DwarfRegisterAArch64) -> &mut Option { &mut self[reg as u8] } } -pub enum DwarfRegister { +pub enum DwarfRegisterAMD64 { SP = 7, IP = 16, @@ -66,3 +88,84 @@ pub enum DwarfRegister { R14 = 14, R15 = 15, } + +pub enum DwarfRegisterAArch64 { + X0 = 0, + X1 = 1, + X2 = 2, + X3 = 3, + X4 = 4, + X5 = 5, + X6 = 6, + X7 = 7, + X8 = 8, + X9 = 9, + X10 = 10, + X11 = 11, + X12 = 12, + X13 = 13, + X14 = 14, + X15 = 15, + X16 = 16, + X17 = 17, + X18 = 18, + X19 = 19, + X20 = 20, + X21 = 21, + X22 = 22, + X23 = 23, + X24 = 24, + X25 = 25, + X26 = 26, + X27 = 27, + X28 = 28, + X29 = 29, // Frame Pointer + IP = 30, // Link register, x30, IP is restored in it? + SP = 31, + + + // TODO: ELR_mode + + // Vector regs + V0 = 64, + V1 = 65, + V2 = 66, + V3 = 67, + V4 = 68, + V5 = 69, + V6 = 70, + V7 = 71, + V8 = 72, + V9 = 73, + V10 = 74, + V11 = 75, + V12 = 76, + V13 = 77, + V14 = 78, + V15 = 79, + V16 = 80, + V17 = 81, + V18 = 82, + V19 = 83, + V20 = 84, + V21 = 85, + V22 = 86, + V23 = 87, + V24 = 88, + V25 = 89, + V26 = 90, + V27 = 91, + V28 = 92, + V29 = 93, + V30 = 94, + V31 = 95, +} + +#[cfg(target_arch = "x86_64")] +pub use self::DwarfRegisterAMD64 as DwarfRegister; + +#[cfg(target_arch = "aarch64")] +pub use self::DwarfRegisterAArch64 as DwarfRegister; + +#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] +compiler_error!("Unsupported architecture"); diff --git a/unwind/src/unwind_helper.c b/unwind/src/unwind_helper.c deleted file mode 100644 index 25c1ba4..0000000 --- a/unwind/src/unwind_helper.c +++ /dev/null @@ -1,46 +0,0 @@ - -asm ( - ".global unwind_trampoline \n" - "unwind_trampoline: \n" - ".cfi_startproc \n" - "movq %rsp, %rsi \n" - ".cfi_def_cfa rsi, 8 \n" - "pushq %rbp \n" - ".cfi_offset rbp, -16 \n" - "pushq %rbx \n" - "pushq %r12 \n" - "pushq %r13 \n" - "pushq %r14 \n" - "pushq %r15 \n" - "movq %rsp, %rdx \n" - "subq $0x08, %rsp \n" - ".cfi_def_cfa rsp, 0x40 \n" - "call unwind_recorder \n" - "addq $0x38, %rsp \n" - ".cfi_def_cfa rsp, 8 \n" - "ret \n" - ".cfi_endproc" - ); - -asm ( - ".global unwind_lander \n" - "unwind_lander: \n" - "movq %rdi, %rsp \n" - "popq %rax \n" - "popq %rbx \n" - "popq %rcx \n" - "popq %rdx \n" - "popq %rdi \n" - "popq %rsi \n" - "popq %rbp \n" - "popq %r8 \n" - "popq %r9 \n" - "popq %r10 \n" - "popq %r11 \n" - "popq %r12 \n" - "popq %r13 \n" - "popq %r14 \n" - "popq %r15 \n" - "movq 0(%rsp), %rsp \n" - "ret \n" // HYPERSPACE JUMP :D - );