diff --git a/Cargo.toml b/Cargo.toml index 98dd69f..812fddc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,9 @@ gimli = "0.28.1" object = { version = "0.32", optional = true } thiserror = "1.0.30" macho-unwind-info = "0.3.0" +pe-unwind-info = "0.1.0" fallible-iterator = "0.3.0" +arrayvec = "0.7.4" [dev-dependencies] object = "0.32" diff --git a/src/aarch64/mod.rs b/src/aarch64/mod.rs index ec696a0..f4294ca 100644 --- a/src/aarch64/mod.rs +++ b/src/aarch64/mod.rs @@ -3,6 +3,7 @@ mod cache; mod dwarf; mod instruction_analysis; mod macho; +mod pe; mod unwind_rule; mod unwinder; mod unwindregs; @@ -12,6 +13,7 @@ pub use cache::*; pub use dwarf::*; pub use instruction_analysis::*; pub use macho::*; +pub use pe::*; pub use unwind_rule::*; pub use unwinder::*; pub use unwindregs::*; diff --git a/src/aarch64/pe.rs b/src/aarch64/pe.rs new file mode 100644 index 0000000..2c1e0d2 --- /dev/null +++ b/src/aarch64/pe.rs @@ -0,0 +1,18 @@ +use super::arch::ArchAarch64; +use crate::pe::{PeSections, PeUnwinderError, PeUnwinding}; +use crate::unwind_result::UnwindResult; + +impl PeUnwinding for ArchAarch64 { + fn unwind_frame( + _sections: PeSections, + _address: u32, + _regs: &mut Self::UnwindRegs, + _read_stack: &mut F, + ) -> Result, PeUnwinderError> + where + F: FnMut(u64) -> Result, + D: std::ops::Deref, + { + Err(PeUnwinderError::Aarch64Unsupported) + } +} diff --git a/src/error.rs b/src/error.rs index 928a1a6..0bec58e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,6 @@ use crate::dwarf::DwarfUnwinderError; use crate::macho::CompactUnwindInfoUnwinderError; +use crate::pe::PeUnwinderError; /// The error type used in this crate. #[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)] @@ -28,6 +29,9 @@ pub enum UnwinderError { #[error("DWARF unwinding failed: {0}")] Dwarf(#[from] DwarfUnwinderError), + #[error("PE unwinding failed: {0}")] + Pe(#[from] PeUnwinderError), + #[error("__unwind_info referred to DWARF FDE but we do not have __eh_frame data")] NoDwarfData, diff --git a/src/lib.rs b/src/lib.rs index e48dc70..8b6e0ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,6 +120,7 @@ mod dwarf; mod error; mod instruction_analysis; mod macho; +mod pe; mod rule_cache; mod unwind_result; mod unwind_rule; diff --git a/src/pe.rs b/src/pe.rs new file mode 100644 index 0000000..2e2ca0b --- /dev/null +++ b/src/pe.rs @@ -0,0 +1,79 @@ +use crate::{arch::Arch, unwind_result::UnwindResult}; +use std::ops::Range; + +#[derive(thiserror::Error, Clone, Copy, Debug, PartialEq, Eq)] +pub enum PeUnwinderError { + #[error("failed to read unwind info memory at RVA {0:x}")] + MissingUnwindInfoData(u32), + #[error("failed to read instruction memory at RVA {0:x}")] + MissingInstructionData(u32), + #[error("failed to read stack{}", .0.map(|a| format!(" at address {a:x}")).unwrap_or_default())] + MissingStackData(Option), + #[error("failed to parse UnwindInfo")] + UnwindInfoParseError, + #[error("AArch64 is not yet supported")] + Aarch64Unsupported, +} + +/// Data and the related RVA range within the binary. +/// +/// This is only used by PE unwinding. +/// +/// Type arguments: +/// - `D`: The type for unwind section data. This allows carrying owned data on the +/// module, e.g. `Vec`. But it could also be a wrapper around mapped memory from +/// a file or a different process, for example. It just needs to provide a slice of +/// bytes via its `Deref` implementation. +pub struct DataAtRvaRange { + pub data: D, + pub rva_range: Range, +} + +pub struct PeSections<'a, D> { + pub pdata: &'a D, + pub rdata: Option<&'a DataAtRvaRange>, + pub xdata: Option<&'a DataAtRvaRange>, + pub text: Option<&'a DataAtRvaRange>, +} + +impl<'a, D> PeSections<'a, D> +where + D: std::ops::Deref, +{ + pub fn unwind_info_memory_at_rva(&self, rva: u32) -> Result<&'a [u8], PeUnwinderError> { + [&self.rdata, &self.xdata] + .into_iter() + .find_map(|o| o.and_then(|m| memory_at_rva(m, rva))) + .ok_or(PeUnwinderError::MissingUnwindInfoData(rva)) + } + + pub fn text_memory_at_rva(&self, rva: u32) -> Result<&'a [u8], PeUnwinderError> { + self.text + .and_then(|m| memory_at_rva(m, rva)) + .ok_or(PeUnwinderError::MissingInstructionData(rva)) + } +} + +fn memory_at_rva>( + DataAtRvaRange { data, rva_range }: &DataAtRvaRange, + address: u32, +) -> Option<&[u8]> { + if rva_range.contains(&address) { + let offset = address - rva_range.start; + Some(&data[(offset as usize)..]) + } else { + None + } +} + +pub trait PeUnwinding: Arch { + fn unwind_frame( + sections: PeSections, + address: u32, + regs: &mut Self::UnwindRegs, + read_stack: &mut F, + ) -> Result, PeUnwinderError> + where + F: FnMut(u64) -> Result, + D: std::ops::Deref; +} diff --git a/src/unwinder.rs b/src/unwinder.rs index 4197c11..571e1f2 100644 --- a/src/unwinder.rs +++ b/src/unwinder.rs @@ -10,6 +10,7 @@ use crate::instruction_analysis::InstructionAnalysis; use crate::macho::{ CompactUnwindInfoUnwinder, CompactUnwindInfoUnwinding, CuiUnwindResult, TextBytes, }; +use crate::pe::{DataAtRvaRange, PeUnwinding}; use crate::rule_cache::CacheResult; use crate::unwind_result::UnwindResult; use crate::unwind_rule::UnwindRule; @@ -201,7 +202,7 @@ fn next_global_modules_generation() -> u16 { pub struct UnwinderInternal< D: Deref, - A: Arch + DwarfUnwinding + CompactUnwindInfoUnwinding + InstructionAnalysis, + A: Arch + DwarfUnwinding + CompactUnwindInfoUnwinding + PeUnwinding + InstructionAnalysis, P: AllocationPolicy, > { /// sorted by avma_range.start @@ -214,7 +215,7 @@ pub struct UnwinderInternal< impl< D: Deref, - A: Arch + DwarfUnwinding + CompactUnwindInfoUnwinding + InstructionAnalysis, + A: Arch + DwarfUnwinding + CompactUnwindInfoUnwinding + PeUnwinding + InstructionAnalysis, P: AllocationPolicy, > Default for UnwinderInternal { @@ -225,7 +226,7 @@ impl< impl< D: Deref, - A: Arch + DwarfUnwinding + CompactUnwindInfoUnwinding + InstructionAnalysis, + A: Arch + DwarfUnwinding + CompactUnwindInfoUnwinding + PeUnwinding + InstructionAnalysis, P: AllocationPolicy, > UnwinderInternal { @@ -522,6 +523,22 @@ impl< read_stack, )? } + ModuleUnwindDataInternal::PeUnwindInfo { + pdata, + rdata, + xdata, + text, + } => ::unwind_frame( + crate::pe::PeSections { + pdata, + rdata: rdata.as_ref(), + xdata: xdata.as_ref(), + text: text.as_ref(), + }, + rel_lookup_address, + regs, + read_stack, + )?, ModuleUnwindDataInternal::None => return Err(UnwinderError::NoModuleUnwindData), }; Ok(unwind_result) @@ -575,6 +592,13 @@ enum ModuleUnwindDataInternal> { debug_frame: Arc, base_addresses: crate::dwarf::BaseAddresses, }, + /// Used with PE binaries (Windows). + PeUnwindInfo { + pdata: D, + rdata: Option>, + xdata: Option>, + text: Option>, + }, /// No unwind information is used. Unwinding in this module will use a fallback rule /// (usually frame pointer unwinding). None, @@ -611,6 +635,23 @@ impl> ModuleUnwindDataInternal { base_addresses: base_addresses_for_sections(section_info), text_data, } + } else if let Some(pdata) = section_info.section_data(b".pdata") { + let mut range_and_data = |name| { + let rva_range = section_info.section_svma_range(name).and_then(|range| { + Some(Range { + start: (range.start - section_info.base_svma()).try_into().ok()?, + end: (range.end - section_info.base_svma()).try_into().ok()?, + }) + })?; + let data = section_info.section_data(name)?; + Some(DataAtRvaRange { data, rva_range }) + }; + ModuleUnwindDataInternal::PeUnwindInfo { + pdata, + rdata: range_and_data(b".rdata"), + xdata: range_and_data(b".xdata"), + text: range_and_data(b".text"), + } } else if let Some(eh_frame) = section_info .section_data(b".eh_frame") .or_else(|| section_info.section_data(b"__eh_frame")) diff --git a/src/x86_64/mod.rs b/src/x86_64/mod.rs index ec696a0..f4294ca 100644 --- a/src/x86_64/mod.rs +++ b/src/x86_64/mod.rs @@ -3,6 +3,7 @@ mod cache; mod dwarf; mod instruction_analysis; mod macho; +mod pe; mod unwind_rule; mod unwinder; mod unwindregs; @@ -12,6 +13,7 @@ pub use cache::*; pub use dwarf::*; pub use instruction_analysis::*; pub use macho::*; +pub use pe::*; pub use unwind_rule::*; pub use unwinder::*; pub use unwindregs::*; diff --git a/src/x86_64/pe.rs b/src/x86_64/pe.rs new file mode 100644 index 0000000..1abecf5 --- /dev/null +++ b/src/x86_64/pe.rs @@ -0,0 +1,222 @@ +use super::UnwindRuleOffsetSpAndPopRegisters; +use super::{ + arch::ArchX86_64, + unwind_rule::{OffsetOrPop, UnwindRuleX86_64}, + unwindregs::Reg, +}; +use crate::arch::Arch; +use crate::pe::{PeSections, PeUnwinderError, PeUnwinding}; +use crate::unwind_result::UnwindResult; +use std::ops::ControlFlow; + +use pe_unwind_info::x86_64::{ + FunctionEpilogInstruction, FunctionEpilogParser, FunctionTableEntries, Register, UnwindInfo, + UnwindInfoTrailer, UnwindOperation, UnwindState, +}; + +struct State<'a, F> { + regs: &'a mut ::UnwindRegs, + read_stack: &'a mut F, +} + +impl UnwindState for State<'_, F> +where + F: FnMut(u64) -> Result, +{ + fn read_register(&mut self, register: Register) -> u64 { + self.regs.get(convert_pe_register(register)) + } + + fn read_stack(&mut self, addr: u64) -> Option { + (self.read_stack)(addr).ok() + } + + fn write_register(&mut self, register: Register, value: u64) { + self.regs.set(convert_pe_register(register), value) + } + + fn write_xmm_register(&mut self, _register: pe_unwind_info::x86_64::XmmRegister, _value: u128) { + // Ignore + } +} + +fn convert_pe_register(r: Register) -> Reg { + match r { + Register::RAX => Reg::RAX, + Register::RCX => Reg::RCX, + Register::RDX => Reg::RDX, + Register::RBX => Reg::RBX, + Register::RSP => Reg::RSP, + Register::RBP => Reg::RBP, + Register::RSI => Reg::RSI, + Register::RDI => Reg::RDI, + Register::R8 => Reg::R8, + Register::R9 => Reg::R9, + Register::R10 => Reg::R10, + Register::R11 => Reg::R11, + Register::R12 => Reg::R12, + Register::R13 => Reg::R13, + Register::R14 => Reg::R14, + Register::R15 => Reg::R15, + } +} + +impl From<&'_ FunctionEpilogInstruction> for OffsetOrPop { + fn from(value: &'_ FunctionEpilogInstruction) -> Self { + match value { + FunctionEpilogInstruction::AddSP(offset) => { + if let Ok(v) = (offset / 8).try_into() { + OffsetOrPop::OffsetBy8(v) + } else { + OffsetOrPop::None + } + } + FunctionEpilogInstruction::Pop(reg) => OffsetOrPop::Pop(convert_pe_register(*reg)), + _ => OffsetOrPop::None, + } + } +} + +impl From<&'_ UnwindOperation> for OffsetOrPop { + fn from(value: &'_ UnwindOperation) -> Self { + match value { + UnwindOperation::UnStackAlloc(offset) => { + if let Ok(v) = (offset / 8).try_into() { + OffsetOrPop::OffsetBy8(v) + } else { + OffsetOrPop::None + } + } + UnwindOperation::PopNonVolatile(reg) => OffsetOrPop::Pop(convert_pe_register(*reg)), + _ => OffsetOrPop::None, + } + } +} + +impl PeUnwinding for ArchX86_64 { + fn unwind_frame( + sections: PeSections, + address: u32, + regs: &mut Self::UnwindRegs, + read_stack: &mut F, + ) -> Result, PeUnwinderError> + where + F: FnMut(u64) -> Result, + D: std::ops::Deref, + { + let entries = FunctionTableEntries::parse(sections.pdata); + let Some(function) = entries.lookup(address) else { + return Ok(UnwindResult::ExecRule(UnwindRuleX86_64::JustReturn)); + }; + + let read_stack_err = |read_stack: &mut F, addr| { + read_stack(addr).map_err(|()| PeUnwinderError::MissingStackData(Some(addr))) + }; + + let unwind_info_address = function.unwind_info_address.get(); + let unwind_info = + UnwindInfo::parse(sections.unwind_info_memory_at_rva(unwind_info_address)?) + .ok_or(PeUnwinderError::UnwindInfoParseError)?; + + // Check whether the address is in the function epilog. If so, we need to + // simulate the remaining epilog instructions (unwind codes don't account for + // unwinding from the epilog). We only need to check this for the first unwind info (if + // there are chained infos). + let bytes = (function.end_address.get() - address) as usize; + let instruction = §ions.text_memory_at_rva(address)?[..bytes]; + let mut epilog_parser = FunctionEpilogParser::new(); + if let Some(epilog_instructions) = + epilog_parser.is_function_epilog(instruction, unwind_info.frame_register()) + { + // If the epilog is an optional AddSP followed by Pops, we can return a cache + // rule. + if let Some(rule) = + UnwindRuleOffsetSpAndPopRegisters::for_operations(epilog_instructions.iter()) + { + return Ok(UnwindResult::ExecRule(rule)); + } + + for instruction in epilog_instructions.iter() { + match instruction { + FunctionEpilogInstruction::AddSP(offset) => { + let rsp = regs.get(Reg::RSP); + regs.set(Reg::RSP, rsp + *offset as u64); + } + FunctionEpilogInstruction::AddSPFromFP(offset) => { + let fp = unwind_info + .frame_register() + .expect("invalid fp register offset"); + let fp = convert_pe_register(fp); + let fp = regs.get(fp); + regs.set(Reg::RSP, fp + *offset as u64); + } + FunctionEpilogInstruction::Pop(reg) => { + let rsp = regs.get(Reg::RSP); + let val = read_stack_err(read_stack, rsp)?; + regs.set(convert_pe_register(*reg), val); + regs.set(Reg::RSP, rsp + 8); + } + } + } + + let rsp = regs.get(Reg::RSP); + let ra = read_stack_err(read_stack, rsp)?; + regs.set(Reg::RSP, rsp + 8); + + return Ok(UnwindResult::Uncacheable(ra)); + } + + // Get all chained UnwindInfo and resolve errors when collecting. + let chained_info = std::iter::successors(Some(Ok(unwind_info)), |info| { + let Ok(info) = info else { + return None; + }; + if let Some(UnwindInfoTrailer::ChainedUnwindInfo { chained }) = info.trailer() { + let unwind_info_address = chained.unwind_info_address.get(); + Some( + sections + .unwind_info_memory_at_rva(unwind_info_address) + .and_then(|data| { + UnwindInfo::parse(data).ok_or(PeUnwinderError::UnwindInfoParseError) + }), + ) + } else { + None + } + }) + .collect::, _>>()?; + + // Get all operations across chained UnwindInfo. The first should be filtered to only those + // operations which are before the offset in the function. + let offset = address - function.begin_address.get(); + let operations = chained_info.into_iter().enumerate().flat_map(|(i, info)| { + info.unwind_operations() + .skip_while(move |(o, _)| i == 0 && *o as u32 > offset) + .map(|(_, op)| op) + }); + + // We need to collect operations to first check (without losing ownership) whether an + // unwind rule can be returned. + let operations = operations.collect::>(); + if let Some(rule) = UnwindRuleOffsetSpAndPopRegisters::for_operations(operations.iter()) { + return Ok(UnwindResult::ExecRule(rule)); + } + + // Resolve operations to get the return address. + let mut state = State { regs, read_stack }; + for op in operations { + if let ControlFlow::Break(ra) = unwind_info + .resolve_operation(&mut state, &op) + .ok_or(PeUnwinderError::MissingStackData(None))? + { + return Ok(UnwindResult::Uncacheable(ra)); + } + } + + let rsp = regs.get(Reg::RSP); + let ra = read_stack_err(read_stack, rsp)?; + regs.set(Reg::RSP, rsp + 8); + + Ok(UnwindResult::Uncacheable(ra)) + } +} diff --git a/src/x86_64/unwind_rule.rs b/src/x86_64/unwind_rule.rs index 9245f6f..c2b16ec 100644 --- a/src/x86_64/unwind_rule.rs +++ b/src/x86_64/unwind_rule.rs @@ -1,7 +1,8 @@ -use super::unwindregs::UnwindRegsX86_64; +use super::unwindregs::{Reg, UnwindRegsX86_64}; use crate::add_signed::checked_add_signed; use crate::error::Error; use crate::unwind_rule::UnwindRule; +use arrayvec::ArrayVec; /// For all of these: return address is *(new_sp - 8) #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -19,6 +20,120 @@ pub enum UnwindRuleX86_64 { }, /// (sp, bp) = (bp + 16, *bp) UseFramePointer, + /// sp = sp + 8 * (offset + register count) + /// This supports the common case of pushed callee-saved registers followed by a stack + /// allocation. Up to 8 registers can be stored, which covers all callee-saved registers (aside + /// from RSP which is implicit). + /// + /// The registers are stored in a separate compressed ordering to facilitate restoring register + /// values if desired. If not for this we could simply store the total offset. + OffsetSpAndPopRegisters(UnwindRuleOffsetSpAndPopRegisters), +} + +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)] +pub struct UnwindRuleOffsetSpAndPopRegisters { + pub sp_offset_by_8: u16, + registers: u16, +} + +pub enum OffsetOrPop { + None, + OffsetBy8(u16), + Pop(Reg), +} + +impl UnwindRuleOffsetSpAndPopRegisters { + const ENCODE_REGISTERS: [Reg; 8] = [ + Reg::RBX, + Reg::RBP, + Reg::RDI, + Reg::RSI, + Reg::R12, + Reg::R13, + Reg::R14, + Reg::R15, + ]; + + /// Get the rule which represents the given operations, if possible. + pub fn for_operations(iter: I) -> Option + where + I: Iterator, + T: Into, + { + let mut iter = iter.map(Into::into).peekable(); + let mut rule = Self::default(); + if let Some(OffsetOrPop::OffsetBy8(offset)) = iter.peek() { + rule.sp_offset_by_8 = *offset; + iter.next(); + } + + let mut regs = ArrayVec::::new(); + for i in iter { + if let OffsetOrPop::Pop(reg) = i { + // If try_push errors we've exceeded the number of supported registers: there's no + // way to encode these operations as an unwind rule. + regs.try_push(reg).ok()?; + } else { + return None; + } + } + + if regs.is_empty() && rule.sp_offset_by_8 == 0 { + Some(UnwindRuleX86_64::JustReturn) + } else if rule.set_registers(®s) { + Some(UnwindRuleX86_64::OffsetSpAndPopRegisters(rule)) + } else { + None + } + } + + /// Return the initial stack pointer offset, in bytes. + pub fn sp_offset(&self) -> u64 { + self.sp_offset_by_8 as u64 * 8 + } + + /// Return the ordered set of registers to pop. + pub fn registers(&self) -> ArrayVec { + let mut regs: ArrayVec = Self::ENCODE_REGISTERS.into(); + let mut r = self.registers; + let mut n: u16 = 8; + while r != 0 { + let index = r % n; + if index != 0 { + regs[(8 - n as usize)..].swap(index as usize, 0); + } + r /= n; + n -= 1; + } + regs.truncate(8 - n as usize); + regs + } + + /// Set the ordered registers to pop. Returns true if the registers can be stored. + pub fn set_registers(&mut self, registers: &[Reg]) -> bool { + if registers.len() > Self::ENCODE_REGISTERS.len() { + return false; + } + + let mut r: u16 = 0; + let mut reg_order: ArrayVec = Self::ENCODE_REGISTERS.into(); + + let mut n: u16 = 0; + let mut scale: u16 = 1; + for reg in registers { + let Some(index) = reg_order[n as usize..].iter().position(|r| r == reg) else { + return false; + }; + if index as u16 != 0 { + reg_order[n as usize..].swap(index as usize, 0); + } + r += index as u16 * scale; + scale *= 8 - n; + n += 1; + } + self.registers = r; + true + } } impl UnwindRule for UnwindRuleX86_64 { @@ -153,6 +268,18 @@ impl UnwindRule for UnwindRuleX86_64 { (new_sp, new_bp) } + UnwindRuleX86_64::OffsetSpAndPopRegisters(r) => { + let sp = regs.sp(); + let mut sp = sp + .checked_add(r.sp_offset()) + .ok_or(Error::IntegerOverflow)?; + for reg in r.registers() { + let value = read_stack(sp).map_err(|_| Error::CouldNotReadStack(sp))?; + sp = sp.checked_add(8).ok_or(Error::IntegerOverflow)?; + regs.set(reg, value); + } + (sp.checked_add(8).ok_or(Error::IntegerOverflow)?, regs.bp()) + } }; let return_address = read_stack(new_sp - 8).map_err(|_| Error::CouldNotReadStack(new_sp - 8))?; @@ -224,4 +351,17 @@ mod test { let res = UnwindRuleX86_64::UseFramePointer.exec(true, &mut regs, &mut read_stack); assert_eq!(res, Err(Error::IntegerOverflow)); } + + #[test] + fn register_compression() { + use super::Reg::*; + let mut v = UnwindRuleOffsetSpAndPopRegisters::default(); + + let regs = [RSI, R12, R15, R14, RBX]; + + assert!(!v.set_registers(&[RAX])); + assert!(!v.set_registers(&[RSI, RSI])); + assert!(v.set_registers(®s)); + assert_eq!(v.registers().as_slice(), regs); + } } diff --git a/src/x86_64/unwindregs.rs b/src/x86_64/unwindregs.rs index 6f95509..8325605 100644 --- a/src/x86_64/unwindregs.rs +++ b/src/x86_64/unwindregs.rs @@ -5,13 +5,48 @@ use crate::display_utils::HexNum; #[derive(Clone, Copy, PartialEq, Eq)] pub struct UnwindRegsX86_64 { ip: u64, - sp: u64, - bp: u64, + regs: [u64; 16], +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(u8)] +pub enum Reg { + RAX, + RDX, + RCX, + RBX, + RSI, + RDI, + RBP, + RSP, + R8, + R9, + R10, + R11, + R12, + R13, + R14, + R15, } impl UnwindRegsX86_64 { pub fn new(ip: u64, sp: u64, bp: u64) -> Self { - Self { ip, sp, bp } + let mut r = Self { + ip, + regs: Default::default(), + }; + r.set_sp(sp); + r.set_bp(bp); + r + } + + #[inline(always)] + pub fn get(&self, reg: Reg) -> u64 { + self.regs[reg as usize] + } + #[inline(always)] + pub fn set(&mut self, reg: Reg, value: u64) { + self.regs[reg as usize] = value; } #[inline(always)] @@ -25,29 +60,43 @@ impl UnwindRegsX86_64 { #[inline(always)] pub fn sp(&self) -> u64 { - self.sp + self.get(Reg::RSP) } #[inline(always)] pub fn set_sp(&mut self, sp: u64) { - self.sp = sp + self.set(Reg::RSP, sp) } #[inline(always)] pub fn bp(&self) -> u64 { - self.bp + self.get(Reg::RBP) } #[inline(always)] pub fn set_bp(&mut self, bp: u64) { - self.bp = bp + self.set(Reg::RBP, bp) } } impl Debug for UnwindRegsX86_64 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("UnwindRegsX86_64") - .field("ip", &HexNum(self.ip)) - .field("sp", &HexNum(self.sp)) - .field("bp", &HexNum(self.bp)) + .field("ip", &HexNum(self.ip())) + .field("rax", &HexNum(self.get(Reg::RAX))) + .field("rdx", &HexNum(self.get(Reg::RDX))) + .field("rcx", &HexNum(self.get(Reg::RCX))) + .field("rbx", &HexNum(self.get(Reg::RBX))) + .field("rsi", &HexNum(self.get(Reg::RSI))) + .field("rdi", &HexNum(self.get(Reg::RDI))) + .field("rbp", &HexNum(self.get(Reg::RBP))) + .field("rsp", &HexNum(self.get(Reg::RSP))) + .field("r8", &HexNum(self.get(Reg::R8))) + .field("r9", &HexNum(self.get(Reg::R9))) + .field("r10", &HexNum(self.get(Reg::R10))) + .field("r11", &HexNum(self.get(Reg::R11))) + .field("r12", &HexNum(self.get(Reg::R12))) + .field("r13", &HexNum(self.get(Reg::R13))) + .field("r14", &HexNum(self.get(Reg::R14))) + .field("r15", &HexNum(self.get(Reg::R15))) .finish() } }