use gimli::{ AArch64, CfaRule, Encoding, EvaluationStorage, Reader, ReaderOffset, Register, RegisterRule, UnwindContextStorage, UnwindSection, UnwindTableRow, }; use super::{arch::ArchAarch64, unwind_rule::UnwindRuleAarch64, unwindregs::UnwindRegsAarch64}; use crate::unwind_result::UnwindResult; use crate::dwarf::{ eval_cfa_rule, eval_register_rule, ConversionError, DwarfUnwindRegs, DwarfUnwinderError, DwarfUnwinding, }; impl DwarfUnwindRegs for UnwindRegsAarch64 { fn get(&self, register: Register) -> Option { match register { AArch64::SP => Some(self.sp()), AArch64::X29 => Some(self.fp()), AArch64::X30 => Some(self.lr()), _ => None, } } } impl DwarfUnwinding for ArchAarch64 { fn unwind_frame( section: &impl UnwindSection, unwind_info: &UnwindTableRow, encoding: Encoding, regs: &mut Self::UnwindRegs, is_first_frame: bool, read_stack: &mut F, ) -> Result, DwarfUnwinderError> where F: FnMut(u64) -> Result, R: Reader, UCS: UnwindContextStorage, ES: EvaluationStorage, { let cfa_rule = unwind_info.cfa(); let fp_rule = unwind_info.register(AArch64::X29); let lr_rule = unwind_info.register(AArch64::X30); match translate_into_unwind_rule(cfa_rule, &fp_rule, &lr_rule) { Ok(unwind_rule) => return Ok(UnwindResult::ExecRule(unwind_rule)), Err(_err) => { // Could not translate into a cacheable unwind rule. Fall back to the generic path. // eprintln!("Unwind rule translation failed: {:?}", err); } } let cfa = eval_cfa_rule::(section, cfa_rule, encoding, regs) .ok_or(DwarfUnwinderError::CouldNotRecoverCfa)?; let lr = regs.lr(); let fp = regs.fp(); let sp = regs.sp(); let (fp, lr) = if !is_first_frame { if cfa <= sp { return Err(DwarfUnwinderError::StackPointerMovedBackwards); } let fp = eval_register_rule::( section, fp_rule, cfa, encoding, fp, regs, read_stack, ) .ok_or(DwarfUnwinderError::CouldNotRecoverFramePointer)?; let lr = eval_register_rule::( section, lr_rule, cfa, encoding, lr, regs, read_stack, ) .ok_or(DwarfUnwinderError::CouldNotRecoverReturnAddress)?; (fp, lr) } else { // For the first frame, be more lenient when encountering errors. // TODO: Find evidence of what this gives us. I think on macOS the prologue often has Unknown register rules // and we only encounter prologues for the first frame. let fp = eval_register_rule::( section, fp_rule, cfa, encoding, fp, regs, read_stack, ) .unwrap_or(fp); let lr = eval_register_rule::( section, lr_rule, cfa, encoding, lr, regs, read_stack, ) .unwrap_or(lr); (fp, lr) }; regs.set_fp(fp); regs.set_sp(cfa); regs.set_lr(lr); Ok(UnwindResult::Uncacheable(lr)) } fn rule_if_uncovered_by_fde() -> Self::UnwindRule { UnwindRuleAarch64::NoOpIfFirstFrameOtherwiseFp } } fn register_rule_to_cfa_offset( rule: &RegisterRule, ) -> Result, ConversionError> { match *rule { RegisterRule::Undefined | RegisterRule::SameValue => Ok(None), RegisterRule::Offset(offset) => Ok(Some(offset)), _ => Err(ConversionError::RegisterNotStoredRelativeToCfa), } } fn translate_into_unwind_rule( cfa_rule: &CfaRule, fp_rule: &RegisterRule, lr_rule: &RegisterRule, ) -> Result { match cfa_rule { CfaRule::RegisterAndOffset { register, offset } => match *register { AArch64::SP => { let sp_offset_by_16 = u16::try_from(offset / 16).map_err(|_| ConversionError::SpOffsetDoesNotFit)?; let lr_cfa_offset = register_rule_to_cfa_offset(lr_rule)?; let fp_cfa_offset = register_rule_to_cfa_offset(fp_rule)?; match (lr_cfa_offset, fp_cfa_offset) { (None, Some(_)) => Err(ConversionError::RestoringFpButNotLr), (None, None) => { if let RegisterRule::Undefined = lr_rule { // If the return address is undefined, this could have two reasons: // - The column for the return address may have been manually set to "undefined" // using DW_CFA_undefined. This usually means that the function never returns // and can be treated as the root of the stack. // - The column for the return may have been omitted from the DWARF CFI table. // Per spec (at least as of DWARF >= 3), this means that it should be treated // as undefined. But it seems that compilers often do this when they really mean // "same value". // Gimli follows DWARF 3 and does not differentiate between "omitted" and "undefined". Ok( UnwindRuleAarch64::OffsetSpIfFirstFrameOtherwiseStackEndsHere { sp_offset_by_16, }, ) } else { Ok(UnwindRuleAarch64::OffsetSp { sp_offset_by_16 }) } } (Some(lr_cfa_offset), None) => { let lr_storage_offset_from_sp_by_8 = i16::try_from((offset + lr_cfa_offset) / 8) .map_err(|_| ConversionError::LrStorageOffsetDoesNotFit)?; Ok(UnwindRuleAarch64::OffsetSpAndRestoreLr { sp_offset_by_16, lr_storage_offset_from_sp_by_8, }) } (Some(lr_cfa_offset), Some(fp_cfa_offset)) => { let lr_storage_offset_from_sp_by_8 = i16::try_from((offset + lr_cfa_offset) / 8) .map_err(|_| ConversionError::LrStorageOffsetDoesNotFit)?; let fp_storage_offset_from_sp_by_8 = i16::try_from((offset + fp_cfa_offset) / 8) .map_err(|_| ConversionError::FpStorageOffsetDoesNotFit)?; Ok(UnwindRuleAarch64::OffsetSpAndRestoreFpAndLr { sp_offset_by_16, fp_storage_offset_from_sp_by_8, lr_storage_offset_from_sp_by_8, }) } } } AArch64::X29 => { let lr_cfa_offset = register_rule_to_cfa_offset(lr_rule)? .ok_or(ConversionError::FramePointerRuleDoesNotRestoreLr)?; let fp_cfa_offset = register_rule_to_cfa_offset(fp_rule)? .ok_or(ConversionError::FramePointerRuleDoesNotRestoreFp)?; if *offset == 16 && fp_cfa_offset == -16 && lr_cfa_offset == -8 { Ok(UnwindRuleAarch64::UseFramePointer) } else { let sp_offset_from_fp_by_8 = u16::try_from(offset / 8) .map_err(|_| ConversionError::SpOffsetFromFpDoesNotFit)?; let lr_storage_offset_from_fp_by_8 = i16::try_from((offset + lr_cfa_offset) / 8) .map_err(|_| ConversionError::LrStorageOffsetDoesNotFit)?; let fp_storage_offset_from_fp_by_8 = i16::try_from((offset + fp_cfa_offset) / 8) .map_err(|_| ConversionError::FpStorageOffsetDoesNotFit)?; Ok(UnwindRuleAarch64::UseFramepointerWithOffsets { sp_offset_from_fp_by_8, fp_storage_offset_from_fp_by_8, lr_storage_offset_from_fp_by_8, }) } } _ => Err(ConversionError::CfaIsOffsetFromUnknownRegister), }, CfaRule::Expression(_) => Err(ConversionError::CfaIsExpression), } }