From d8361e853d073507d94639a3a7d4fda093a02201 Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Thu, 14 Mar 2024 16:57:10 +1000 Subject: [PATCH] read/cfi: replace Expression with UnwindExpression containing offset/length (#703) This avoids the need to store `Reader` in the `UnwindContext`, which simplifies lifetime management for reuse of the context. --- src/read/cfi.rs | 380 ++++++++++++++++++++++++++++------------------- src/write/cfi.rs | 24 ++- 2 files changed, 247 insertions(+), 157 deletions(-) diff --git a/src/read/cfi.rs b/src/read/cfi.rs index 37728ac3..d764219c 100644 --- a/src/read/cfi.rs +++ b/src/read/cfi.rs @@ -459,14 +459,14 @@ impl<'a, R: Reader + 'a> EhHdrTable<'a, R> { /// /// You must provide a function to get the associated CIE. See /// `PartialFrameDescriptionEntry::parse` for more information. - pub fn unwind_info_for_address<'ctx, F, A: UnwindContextStorage>( + pub fn unwind_info_for_address<'ctx, F, A: UnwindContextStorage>( &self, frame: &EhFrame, bases: &BaseAddresses, - ctx: &'ctx mut UnwindContext, + ctx: &'ctx mut UnwindContext, address: u64, get_cie: F, - ) -> Result<&'ctx UnwindTableRow> + ) -> Result<&'ctx UnwindTableRow> where F: FnMut( &EhFrame, @@ -778,13 +778,13 @@ pub trait UnwindSection: Clone + Debug + _UnwindSectionPrivate { /// # } /// ``` #[inline] - fn unwind_info_for_address<'ctx, F, A: UnwindContextStorage>( + fn unwind_info_for_address<'ctx, F, A: UnwindContextStorage>( &self, bases: &BaseAddresses, - ctx: &'ctx mut UnwindContext, + ctx: &'ctx mut UnwindContext, address: u64, get_cie: F, - ) -> Result<&'ctx UnwindTableRow> + ) -> Result<&'ctx UnwindTableRow> where F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result>, { @@ -1734,11 +1734,11 @@ impl FrameDescriptionEntry { /// Return the table of unwind information for this FDE. #[inline] - pub fn rows<'a, 'ctx, Section: UnwindSection, A: UnwindContextStorage>( + pub fn rows<'a, 'ctx, Section: UnwindSection, A: UnwindContextStorage>( &self, section: &'a Section, bases: &'a BaseAddresses, - ctx: &'ctx mut UnwindContext, + ctx: &'ctx mut UnwindContext, ) -> Result> { UnwindTable::new(section, bases, ctx, self) } @@ -1749,13 +1749,17 @@ impl FrameDescriptionEntry { /// context in the form `Ok((unwind_info, context))`. If not found, /// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned. If parsing or /// CFI evaluation fails, the error is returned. - pub fn unwind_info_for_address<'ctx, Section: UnwindSection, A: UnwindContextStorage>( + pub fn unwind_info_for_address< + 'ctx, + Section: UnwindSection, + A: UnwindContextStorage, + >( &self, section: &Section, bases: &BaseAddresses, - ctx: &'ctx mut UnwindContext, + ctx: &'ctx mut UnwindContext, address: u64, - ) -> Result<&'ctx UnwindTableRow> { + ) -> Result<&'ctx UnwindTableRow> { let mut table = self.rows(section, bases, ctx)?; while let Some(row) = table.next_row()? { if row.contains(address) { @@ -1894,9 +1898,9 @@ You may want to supply your own storage type for one of the following reasons: /// # /// struct StoreOnStack; /// -/// impl UnwindContextStorage for StoreOnStack { -/// type Rules = [(Register, RegisterRule); 192]; -/// type Stack = [UnwindTableRow; 4]; +/// impl UnwindContextStorage for StoreOnStack { +/// type Rules = [(Register, RegisterRule); 192]; +/// type Stack = [UnwindTableRow; 4]; /// } /// /// let mut ctx = UnwindContext::<_, StoreOnStack>::new_in(); @@ -1911,14 +1915,14 @@ You may want to supply your own storage type for one of the following reasons: /// # unreachable!() /// # } /// ``` -pub trait UnwindContextStorage: Sized { +pub trait UnwindContextStorage: Sized { /// The storage used for register rules in a unwind table row. /// /// Note that this is nested within the stack. - type Rules: ArrayLike)>; + type Rules: ArrayLike)>; /// The storage used for unwind table row stack. - type Stack: ArrayLike>; + type Stack: ArrayLike>; } #[cfg(feature = "read")] @@ -1927,9 +1931,9 @@ const MAX_RULES: usize = 192; const MAX_UNWIND_STACK_DEPTH: usize = 4; #[cfg(feature = "read")] -impl UnwindContextStorage for StoreOnHeap { - type Rules = [(Register, RegisterRule); MAX_RULES]; - type Stack = Box<[UnwindTableRow; MAX_UNWIND_STACK_DEPTH]>; +impl UnwindContextStorage for StoreOnHeap { + type Rules = [(Register, RegisterRule); MAX_RULES]; + type Stack = Box<[UnwindTableRow; MAX_UNWIND_STACK_DEPTH]>; } /// Common context needed when evaluating the call frame unwinding information. @@ -1966,7 +1970,7 @@ impl UnwindContextStorage for StoreOnHeap { /// # } /// ``` #[derive(Clone, PartialEq, Eq)] -pub struct UnwindContext = StoreOnHeap> { +pub struct UnwindContext = StoreOnHeap> { // Stack of rows. The last row is the row currently being built by the // program. There is always at least one row. The vast majority of CFI // programs will only ever have one row on the stack. @@ -1980,12 +1984,12 @@ pub struct UnwindContext = StoreOnHeap> { // `DW_CFA_restore`. Otherwise, when we are currently evaluating a CIE's // initial instructions, `is_initialized` will be `false` and initial rules // cannot be read. - initial_rule: Option<(Register, RegisterRule)>, + initial_rule: Option<(Register, RegisterRule)>, is_initialized: bool, } -impl> Debug for UnwindContext { +impl> Debug for UnwindContext { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("UnwindContext") .field("stack", &self.stack) @@ -1995,14 +1999,14 @@ impl> Debug for UnwindContext { } } -impl> Default for UnwindContext { +impl> Default for UnwindContext { fn default() -> Self { Self::new_in() } } #[cfg(feature = "read")] -impl UnwindContext { +impl UnwindContext { /// Construct a new call frame unwinding context. pub fn new() -> Self { Self::new_in() @@ -2013,7 +2017,7 @@ impl UnwindContext { /// /// These methods are guaranteed not to allocate, acquire locks, or perform any /// other signal-unsafe operations, if an non-allocating storage is used. -impl> UnwindContext { +impl> UnwindContext { /// Construct a new call frame unwinding context. pub fn new_in() -> Self { let mut ctx = UnwindContext { @@ -2026,12 +2030,16 @@ impl> UnwindContext { } /// Run the CIE's initial instructions and initialize this `UnwindContext`. - fn initialize>( + fn initialize( &mut self, section: &Section, bases: &BaseAddresses, cie: &CommonInformationEntry, - ) -> Result<()> { + ) -> Result<()> + where + R: Reader, + Section: UnwindSection, + { // Always reset because previous initialization failure may leave dirty state. self.reset(); @@ -2050,11 +2058,11 @@ impl> UnwindContext { self.is_initialized = false; } - fn row(&self) -> &UnwindTableRow { + fn row(&self) -> &UnwindTableRow { self.stack.last().unwrap() } - fn row_mut(&mut self) -> &mut UnwindTableRow { + fn row_mut(&mut self) -> &mut UnwindTableRow { self.stack.last_mut().unwrap() } @@ -2086,14 +2094,14 @@ impl> UnwindContext { row.start_address = start_address; } - fn set_register_rule(&mut self, register: Register, rule: RegisterRule) -> Result<()> { + fn set_register_rule(&mut self, register: Register, rule: RegisterRule) -> Result<()> { let row = self.row_mut(); row.registers.set(register, rule) } /// Returns `None` if we have not completed evaluation of a CIE's initial /// instructions. - fn get_initial_rule(&self, register: Register) -> Option> { + fn get_initial_rule(&self, register: Register) -> Option> { if !self.is_initialized { return None; } @@ -2104,11 +2112,11 @@ impl> UnwindContext { }) } - fn set_cfa(&mut self, cfa: CfaRule) { + fn set_cfa(&mut self, cfa: CfaRule) { self.row_mut().cfa = cfa; } - fn cfa_mut(&mut self) -> &mut CfaRule { + fn cfa_mut(&mut self) -> &mut CfaRule { &mut self.row_mut().cfa } @@ -2188,7 +2196,7 @@ impl> UnwindContext { /// > recording just the differences starting at the beginning address of each /// > subroutine in the program. #[derive(Debug)] -pub struct UnwindTable<'a, 'ctx, R: Reader, A: UnwindContextStorage = StoreOnHeap> { +pub struct UnwindTable<'a, 'ctx, R: Reader, A: UnwindContextStorage = StoreOnHeap> { code_alignment_factor: Wrapping, data_alignment_factor: Wrapping, next_start_address: u64, @@ -2196,20 +2204,20 @@ pub struct UnwindTable<'a, 'ctx, R: Reader, A: UnwindContextStorage = StoreOn returned_last_row: bool, current_row_valid: bool, instructions: CallFrameInstructionIter<'a, R>, - ctx: &'ctx mut UnwindContext, + ctx: &'ctx mut UnwindContext, } /// # Signal Safe Methods /// /// These methods are guaranteed not to allocate, acquire locks, or perform any /// other signal-unsafe operations. -impl<'a, 'ctx, R: Reader, A: UnwindContextStorage> UnwindTable<'a, 'ctx, R, A> { +impl<'a, 'ctx, R: Reader, A: UnwindContextStorage> UnwindTable<'a, 'ctx, R, A> { /// Construct a new `UnwindTable` for the given /// `FrameDescriptionEntry`'s CFI unwinding program. pub fn new>( section: &'a Section, bases: &'a BaseAddresses, - ctx: &'ctx mut UnwindContext, + ctx: &'ctx mut UnwindContext, fde: &FrameDescriptionEntry, ) -> Result { ctx.initialize(section, bases, fde.cie())?; @@ -2219,7 +2227,7 @@ impl<'a, 'ctx, R: Reader, A: UnwindContextStorage> UnwindTable<'a, 'ctx, R, A fn new_for_fde>( section: &'a Section, bases: &'a BaseAddresses, - ctx: &'ctx mut UnwindContext, + ctx: &'ctx mut UnwindContext, fde: &FrameDescriptionEntry, ) -> Self { assert!(ctx.stack.len() >= 1); @@ -2238,7 +2246,7 @@ impl<'a, 'ctx, R: Reader, A: UnwindContextStorage> UnwindTable<'a, 'ctx, R, A fn new_for_cie>( section: &'a Section, bases: &'a BaseAddresses, - ctx: &'ctx mut UnwindContext, + ctx: &'ctx mut UnwindContext, cie: &CommonInformationEntry, ) -> Self { assert!(ctx.stack.len() >= 1); @@ -2259,7 +2267,7 @@ impl<'a, 'ctx, R: Reader, A: UnwindContextStorage> UnwindTable<'a, 'ctx, R, A /// /// Unfortunately, this cannot be used with `FallibleIterator` because of /// the restricted lifetime of the yielded item. - pub fn next_row(&mut self) -> Result>> { + pub fn next_row(&mut self) -> Result>> { assert!(self.ctx.stack.len() >= 1); self.ctx.set_start_address(self.next_start_address); self.current_row_valid = false; @@ -2292,7 +2300,7 @@ impl<'a, 'ctx, R: Reader, A: UnwindContextStorage> UnwindTable<'a, 'ctx, R, A } /// Returns the current row with the lifetime of the context. - pub fn into_current_row(self) -> Option<&'ctx UnwindTableRow> { + pub fn into_current_row(self) -> Option<&'ctx UnwindTableRow> { if self.current_row_valid { Some(self.ctx.row()) } else { @@ -2302,7 +2310,7 @@ impl<'a, 'ctx, R: Reader, A: UnwindContextStorage> UnwindTable<'a, 'ctx, R, A /// Evaluate one call frame instruction. Return `Ok(true)` if the row is /// complete, `Ok(false)` otherwise. - fn evaluate(&mut self, instruction: CallFrameInstruction) -> Result { + fn evaluate(&mut self, instruction: CallFrameInstruction) -> Result { use crate::CallFrameInstruction::*; match instruction { @@ -2511,11 +2519,11 @@ impl<'a, 'ctx, R: Reader, A: UnwindContextStorage> UnwindTable<'a, 'ctx, R, A // - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-aarch64/dwarf-config.h#L32 // - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-arm/dwarf-config.h#L31 // - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-mips/dwarf-config.h#L31 -struct RegisterRuleMap = StoreOnHeap> { +struct RegisterRuleMap = StoreOnHeap> { rules: ArrayVec, } -impl> Debug for RegisterRuleMap { +impl> Debug for RegisterRuleMap { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("RegisterRuleMap") .field("rules", &self.rules) @@ -2523,7 +2531,7 @@ impl> Debug for RegisterRuleMap { } } -impl> Clone for RegisterRuleMap { +impl> Clone for RegisterRuleMap { fn clone(&self) -> Self { Self { rules: self.rules.clone(), @@ -2531,7 +2539,7 @@ impl> Clone for RegisterRuleMap { } } -impl> Default for RegisterRuleMap { +impl> Default for RegisterRuleMap { fn default() -> Self { RegisterRuleMap { rules: Default::default(), @@ -2543,12 +2551,12 @@ impl> Default for RegisterRuleMap { /// /// These methods are guaranteed not to allocate, acquire locks, or perform any /// other signal-unsafe operations. -impl> RegisterRuleMap { +impl> RegisterRuleMap { fn is_default(&self) -> bool { self.rules.is_empty() } - fn get(&self, register: Register) -> RegisterRule { + fn get(&self, register: Register) -> RegisterRule { self.rules .iter() .find(|rule| rule.0 == register) @@ -2559,7 +2567,7 @@ impl> RegisterRuleMap { .unwrap_or(RegisterRule::Undefined) } - fn set(&mut self, register: Register, rule: RegisterRule) -> Result<()> { + fn set(&mut self, register: Register, rule: RegisterRule) -> Result<()> { if !rule.is_defined() { let idx = self .rules @@ -2586,7 +2594,7 @@ impl> RegisterRuleMap { .map_err(|_| Error::TooManyRegisterRules) } - fn iter(&self) -> RegisterRuleIter { + fn iter(&self) -> RegisterRuleIter { RegisterRuleIter(self.rules.iter()) } } @@ -2594,7 +2602,7 @@ impl> RegisterRuleMap { impl<'a, R, S: UnwindContextStorage> FromIterator<&'a (Register, RegisterRule)> for RegisterRuleMap where - R: 'a + Reader, + R: 'a + ReaderOffset, { fn from_iter(iter: T) -> Self where @@ -2612,9 +2620,9 @@ where } } -impl> PartialEq for RegisterRuleMap +impl> PartialEq for RegisterRuleMap where - R: Reader + PartialEq, + T: ReaderOffset + PartialEq, { fn eq(&self, rhs: &Self) -> bool { for &(reg, ref rule) in &*self.rules { @@ -2635,16 +2643,16 @@ where } } -impl> Eq for RegisterRuleMap where R: Reader + Eq {} +impl> Eq for RegisterRuleMap where T: ReaderOffset + Eq {} /// An unordered iterator for register rules. #[derive(Debug, Clone)] -pub struct RegisterRuleIter<'iter, R>(::core::slice::Iter<'iter, (Register, RegisterRule)>) +pub struct RegisterRuleIter<'iter, T>(::core::slice::Iter<'iter, (Register, RegisterRule)>) where - R: Reader; + T: ReaderOffset; -impl<'iter, R: Reader> Iterator for RegisterRuleIter<'iter, R> { - type Item = &'iter (Register, RegisterRule); +impl<'iter, T: ReaderOffset> Iterator for RegisterRuleIter<'iter, T> { + type Item = &'iter (Register, RegisterRule); fn next(&mut self) -> Option { self.0.next() @@ -2654,15 +2662,15 @@ impl<'iter, R: Reader> Iterator for RegisterRuleIter<'iter, R> { /// A row in the virtual unwind table that describes how to find the values of /// the registers in the *previous* frame for a range of PC addresses. #[derive(PartialEq, Eq)] -pub struct UnwindTableRow = StoreOnHeap> { +pub struct UnwindTableRow = StoreOnHeap> { start_address: u64, end_address: u64, saved_args_size: u64, - cfa: CfaRule, - registers: RegisterRuleMap, + cfa: CfaRule, + registers: RegisterRuleMap, } -impl> Debug for UnwindTableRow { +impl> Debug for UnwindTableRow { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("UnwindTableRow") .field("start_address", &self.start_address) @@ -2674,7 +2682,7 @@ impl> Debug for UnwindTableRow { } } -impl> Clone for UnwindTableRow { +impl> Clone for UnwindTableRow { fn clone(&self) -> Self { Self { start_address: self.start_address, @@ -2686,7 +2694,7 @@ impl> Clone for UnwindTableRow { } } -impl> Default for UnwindTableRow { +impl> Default for UnwindTableRow { fn default() -> Self { UnwindTableRow { start_address: 0, @@ -2698,7 +2706,7 @@ impl> Default for UnwindTableRow { } } -impl> UnwindTableRow { +impl> UnwindTableRow { fn is_default(&self) -> bool { self.start_address == 0 && self.end_address == 0 @@ -2737,7 +2745,7 @@ impl> UnwindTableRow { } /// Get the canonical frame address (CFA) recovery rule for this row. - pub fn cfa(&self) -> &CfaRule { + pub fn cfa(&self) -> &CfaRule { &self.cfa } @@ -2785,7 +2793,7 @@ impl> UnwindTableRow { /// > Vector Mask Registers 0–7 118-125 %k0–%k7 /// > Reserved 126-129 /// > - pub fn register(&self, register: Register) -> RegisterRule { + pub fn register(&self, register: Register) -> RegisterRule { self.registers.get(register) } @@ -2797,21 +2805,21 @@ impl> UnwindTableRow { /// /// ``` /// # use gimli::{EndianSlice, LittleEndian, UnwindTableRow}; - /// # fn foo<'input>(unwind_table_row: UnwindTableRow>) { + /// # fn foo<'input>(unwind_table_row: UnwindTableRow) { /// for &(register, ref rule) in unwind_table_row.registers() { /// // ... /// # drop(register); drop(rule); /// } /// # } /// ``` - pub fn registers(&self) -> RegisterRuleIter { + pub fn registers(&self) -> RegisterRuleIter { self.registers.iter() } } /// The canonical frame address (CFA) recovery rules. #[derive(Clone, Debug, PartialEq, Eq)] -pub enum CfaRule { +pub enum CfaRule { /// The CFA is given offset from the given register's value. RegisterAndOffset { /// The register containing the base value. @@ -2821,10 +2829,10 @@ pub enum CfaRule { }, /// The CFA is obtained by evaluating this `Reader` as a DWARF expression /// program. - Expression(Expression), + Expression(UnwindExpression), } -impl Default for CfaRule { +impl Default for CfaRule { fn default() -> Self { CfaRule::RegisterAndOffset { register: Register(0), @@ -2833,7 +2841,7 @@ impl Default for CfaRule { } } -impl CfaRule { +impl CfaRule { fn is_default(&self) -> bool { match *self { CfaRule::RegisterAndOffset { register, offset } => { @@ -2852,7 +2860,7 @@ impl CfaRule { /// previous frame." #[derive(Clone, Debug, PartialEq, Eq)] #[non_exhaustive] -pub enum RegisterRule { +pub enum RegisterRule { /// > A register that has this rule has no recoverable value in the previous /// > frame. (By convention, it is not preserved by a callee.) Undefined, @@ -2876,11 +2884,11 @@ pub enum RegisterRule { /// "The previous value of this register is located at the address produced /// by executing the DWARF expression." - Expression(Expression), + Expression(UnwindExpression), /// "The previous value of this register is the value produced by executing /// the DWARF expression." - ValExpression(Expression), + ValExpression(UnwindExpression), /// "The rule is defined externally to this specification by the augmenter." Architectural, @@ -2889,7 +2897,7 @@ pub enum RegisterRule { Constant(u64), } -impl RegisterRule { +impl RegisterRule { fn is_defined(&self) -> bool { !matches!(*self, RegisterRule::Undefined) } @@ -2898,7 +2906,7 @@ impl RegisterRule { /// A parsed call frame instruction. #[derive(Clone, Debug, PartialEq, Eq)] #[non_exhaustive] -pub enum CallFrameInstruction { +pub enum CallFrameInstruction { // 6.4.2.1 Row Creation Methods /// > 1. DW_CFA_set_loc /// > @@ -3002,8 +3010,8 @@ pub enum CallFrameInstruction { /// > expression. The required action is to establish that expression as the /// > means by which the current CFA is computed. DefCfaExpression { - /// The DWARF expression. - expression: Expression, + /// The location of the DWARF expression. + expression: UnwindExpression, }, // 6.4.2.3 Register Rule Instructions @@ -3113,8 +3121,8 @@ pub enum CallFrameInstruction { Expression { /// The target register's number. register: Register, - /// The DWARF expression. - expression: Expression, + /// The location of the DWARF expression. + expression: UnwindExpression, }, /// > 10. DW_CFA_val_expression @@ -3130,8 +3138,8 @@ pub enum CallFrameInstruction { ValExpression { /// The target register's number. register: Register, - /// The DWARF expression. - expression: Expression, + /// The location of the DWARF expression. + expression: UnwindExpression, }, /// The `Restore` instruction represents both `DW_CFA_restore` and @@ -3197,13 +3205,13 @@ pub enum CallFrameInstruction { const CFI_INSTRUCTION_HIGH_BITS_MASK: u8 = 0b1100_0000; const CFI_INSTRUCTION_LOW_BITS_MASK: u8 = !CFI_INSTRUCTION_HIGH_BITS_MASK; -impl CallFrameInstruction { - fn parse( +impl CallFrameInstruction { + fn parse>( input: &mut R, address_encoding: Option, parameters: &PointerEncodingParameters, vendor: Vendor, - ) -> Result> { + ) -> Result> { let instruction = input.read_u8()?; let high_bits = instruction & CFI_INSTRUCTION_HIGH_BITS_MASK; @@ -3316,20 +3324,22 @@ impl CallFrameInstruction { } constants::DW_CFA_def_cfa_expression => { - let len = input.read_uleb128().and_then(R::Offset::from_u64)?; - let expression = input.split(len)?; + let length = input.read_uleb128().and_then(R::Offset::from_u64)?; + let offset = input.offset_from(parameters.section); + input.skip(length)?; Ok(CallFrameInstruction::DefCfaExpression { - expression: Expression(expression), + expression: UnwindExpression { offset, length }, }) } constants::DW_CFA_expression => { let register = input.read_uleb128().and_then(Register::from_u64)?; - let len = input.read_uleb128().and_then(R::Offset::from_u64)?; - let expression = input.split(len)?; + let length = input.read_uleb128().and_then(R::Offset::from_u64)?; + let offset = input.offset_from(parameters.section); + input.skip(length)?; Ok(CallFrameInstruction::Expression { register, - expression: Expression(expression), + expression: UnwindExpression { offset, length }, }) } @@ -3378,11 +3388,12 @@ impl CallFrameInstruction { constants::DW_CFA_val_expression => { let register = input.read_uleb128().and_then(Register::from_u64)?; - let len = input.read_uleb128().and_then(R::Offset::from_u64)?; - let expression = input.split(len)?; + let length = input.read_uleb128().and_then(R::Offset::from_u64)?; + let offset = input.offset_from(parameters.section); + input.skip(length)?; Ok(CallFrameInstruction::ValExpression { register, - expression: Expression(expression), + expression: UnwindExpression { offset, length }, }) } @@ -3414,7 +3425,7 @@ pub struct CallFrameInstructionIter<'a, R: Reader> { impl<'a, R: Reader> CallFrameInstructionIter<'a, R> { /// Parse the next call frame instruction. - pub fn next(&mut self) -> Result>> { + pub fn next(&mut self) -> Result>> { if self.input.is_empty() { return Ok(None); } @@ -3436,7 +3447,7 @@ impl<'a, R: Reader> CallFrameInstructionIter<'a, R> { #[cfg(feature = "fallible-iterator")] impl<'a, R: Reader> fallible_iterator::FallibleIterator for CallFrameInstructionIter<'a, R> { - type Item = CallFrameInstruction; + type Item = CallFrameInstruction; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { @@ -3444,6 +3455,34 @@ impl<'a, R: Reader> fallible_iterator::FallibleIterator for CallFrameInstruction } } +/// The location of a DWARF expression within an unwind section. +/// +/// This is stored as an offset and length within the section instead of as a +/// `Reader` to avoid lifetime issues when reusing [`UnwindContext`]. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct UnwindExpression { + /// The offset of the expression within the section. + pub offset: T, + /// The length of the expression. + pub length: T, +} + +impl UnwindExpression { + /// Get the expression from the section. + /// + /// The offset and length were previously validated when the + /// `UnwindExpression` was created, so this should not fail. + pub fn get, S: UnwindSection>( + &self, + section: &S, + ) -> Result> { + let input = &mut section.section().clone(); + input.skip(self.offset)?; + let data = input.split(self.length)?; + Ok(Expression(data)) + } +} + /// Parse a `DW_EH_PE_*` pointer encoding. #[doc(hidden)] #[inline] @@ -3597,7 +3636,7 @@ mod tests { use crate::constants; use crate::endianity::{BigEndian, Endianity, LittleEndian, NativeEndian}; use crate::read::{ - EndianSlice, Error, Expression, Pointer, ReaderOffsetId, Result, Section as ReadSection, + EndianSlice, Error, Pointer, ReaderOffsetId, Result, Section as ReadSection, }; use crate::test_util::GimliSectionMethods; use alloc::boxed::Box; @@ -4537,12 +4576,13 @@ mod tests { fn parse_cfi_instruction( input: &mut R, address_size: u8, - ) -> Result> { + ) -> Result> { + let section = input.clone(); let parameters = &PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size, - section: &R::default(), + section: §ion, }; CallFrameInstruction::parse(input, None, parameters, Vendor::Default) } @@ -4936,13 +4976,17 @@ mod tests { .append_bytes(&expected_rest); length.set_const((&end - &start) as u64); + let expected_expression = UnwindExpression { + offset: (&start - §ion.start()) as usize, + length: (&end - &start) as usize, + }; let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); assert_eq!( parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::DefCfaExpression { - expression: Expression(EndianSlice::new(&expected_expr, LittleEndian)), + expression: expected_expression, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); @@ -4968,6 +5012,10 @@ mod tests { .append_bytes(&expected_rest); length.set_const((&end - &start) as u64); + let expected_expression = UnwindExpression { + offset: (&start - §ion.start()) as usize, + length: (&end - &start) as usize, + }; let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); @@ -4975,7 +5023,7 @@ mod tests { parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::Expression { register: Register(expected_reg), - expression: Expression(EndianSlice::new(&expected_expr, LittleEndian)), + expression: expected_expression, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); @@ -5108,6 +5156,10 @@ mod tests { .append_bytes(&expected_rest); length.set_const((&end - &start) as u64); + let expected_expression = UnwindExpression { + offset: (&start - §ion.start()) as usize, + length: (&end - &start) as usize, + }; let contents = section.get_contents().unwrap(); let input = &mut EndianSlice::new(&contents, LittleEndian); @@ -5115,7 +5167,7 @@ mod tests { parse_cfi_instruction(input, 8), Ok(CallFrameInstruction::ValExpression { register: Register(expected_reg), - expression: Expression(EndianSlice::new(&expected_expr, LittleEndian)), + expression: expected_expression, }) ); assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); @@ -5178,13 +5230,17 @@ mod tests { .D8(expected_delta); length.set_const((&end - &start) as u64); + let expected_expression = UnwindExpression { + offset: (&start - §ion.start()) as usize, + length: (&end - &start) as usize, + }; let contents = section.get_contents().unwrap(); let input = EndianSlice::new(&contents, BigEndian); let parameters = PointerEncodingParameters { bases: &SectionBaseAddresses::default(), func_base: None, address_size: 8, - section: &EndianSlice::default(), + section: &input, }; let mut iter = CallFrameInstructionIter { input, @@ -5197,7 +5253,7 @@ mod tests { iter.next(), Ok(Some(CallFrameInstruction::ValExpression { register: Register(expected_reg), - expression: Expression(EndianSlice::new(&expected_expr, BigEndian)), + expression: expected_expression, })) ); @@ -5239,18 +5295,13 @@ mod tests { } fn assert_eval<'a, I>( - mut initial_ctx: UnwindContext>, - expected_ctx: UnwindContext>, + mut initial_ctx: UnwindContext, + expected_ctx: UnwindContext, cie: CommonInformationEntry>, fde: Option>>, instructions: I, ) where - I: AsRef< - [( - Result, - CallFrameInstruction>, - )], - >, + I: AsRef<[(Result, CallFrameInstruction)]>, { { let section = &DebugFrame::from(EndianSlice::default()); @@ -5392,10 +5443,10 @@ mod tests { fn test_eval_def_cfa_register_invalid_context() { let cie = make_test_cie(); let mut ctx = UnwindContext::new(); - ctx.set_cfa(CfaRule::Expression(Expression(EndianSlice::new( - &[], - LittleEndian, - )))); + ctx.set_cfa(CfaRule::Expression(UnwindExpression { + offset: 0, + length: 0, + })); let expected = ctx.clone(); let instructions = [( Err(Error::CfiInstructionInInvalidContext), @@ -5427,10 +5478,10 @@ mod tests { fn test_eval_def_cfa_offset_invalid_context() { let cie = make_test_cie(); let mut ctx = UnwindContext::new(); - ctx.set_cfa(CfaRule::Expression(Expression(EndianSlice::new( - &[], - LittleEndian, - )))); + ctx.set_cfa(CfaRule::Expression(UnwindExpression { + offset: 10, + length: 11, + })); let expected = ctx.clone(); let instructions = [( Err(Error::CfiInstructionInInvalidContext), @@ -5441,19 +5492,17 @@ mod tests { #[test] fn test_eval_def_cfa_expression() { - let expr = [1, 2, 3, 4]; + let expr = UnwindExpression { + offset: 10, + length: 11, + }; let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); - expected.set_cfa(CfaRule::Expression(Expression(EndianSlice::new( - &expr, - LittleEndian, - )))); + expected.set_cfa(CfaRule::Expression(expr)); let instructions = [( Ok(false), - CallFrameInstruction::DefCfaExpression { - expression: Expression(EndianSlice::new(&expr, LittleEndian)), - }, + CallFrameInstruction::DefCfaExpression { expression: expr }, )]; assert_eval(ctx, expected, cie, None, instructions); } @@ -5578,21 +5627,21 @@ mod tests { #[test] fn test_eval_expression() { - let expr = [1, 2, 3, 4]; + let expr = UnwindExpression { + offset: 10, + length: 11, + }; let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected - .set_register_rule( - Register(9), - RegisterRule::Expression(Expression(EndianSlice::new(&expr, LittleEndian))), - ) + .set_register_rule(Register(9), RegisterRule::Expression(expr)) .unwrap(); let instructions = [( Ok(false), CallFrameInstruction::Expression { register: Register(9), - expression: Expression(EndianSlice::new(&expr, LittleEndian)), + expression: expr, }, )]; assert_eval(ctx, expected, cie, None, instructions); @@ -5600,21 +5649,21 @@ mod tests { #[test] fn test_eval_val_expression() { - let expr = [1, 2, 3, 4]; + let expr = UnwindExpression { + offset: 10, + length: 11, + }; let cie = make_test_cie(); let ctx = UnwindContext::new(); let mut expected = ctx.clone(); expected - .set_register_rule( - Register(9), - RegisterRule::ValExpression(Expression(EndianSlice::new(&expr, LittleEndian))), - ) + .set_register_rule(Register(9), RegisterRule::ValExpression(expr)) .unwrap(); let instructions = [( Ok(false), CallFrameInstruction::ValExpression { register: Register(9), - expression: Expression(EndianSlice::new(&expr, LittleEndian)), + expression: expr, }, )]; assert_eval(ctx, expected, cie, None, instructions); @@ -7212,13 +7261,13 @@ mod tests { #[test] fn register_rule_map_eq() { // Different order, but still equal. - let map1: RegisterRuleMap> = [ + let map1: RegisterRuleMap = [ (Register(0), RegisterRule::SameValue), (Register(3), RegisterRule::Offset(1)), ] .iter() .collect(); - let map2: RegisterRuleMap> = [ + let map2: RegisterRuleMap = [ (Register(3), RegisterRule::Offset(1)), (Register(0), RegisterRule::SameValue), ] @@ -7228,13 +7277,13 @@ mod tests { assert_eq!(map2, map1); // Not equal. - let map3: RegisterRuleMap> = [ + let map3: RegisterRuleMap = [ (Register(0), RegisterRule::SameValue), (Register(2), RegisterRule::Offset(1)), ] .iter() .collect(); - let map4: RegisterRuleMap> = [ + let map4: RegisterRuleMap = [ (Register(3), RegisterRule::Offset(1)), (Register(0), RegisterRule::SameValue), ] @@ -7244,17 +7293,17 @@ mod tests { assert!(map4 != map3); // One has undefined explicitly set, other implicitly has undefined. - let mut map5 = RegisterRuleMap::>::default(); + let mut map5 = RegisterRuleMap::::default(); map5.set(Register(0), RegisterRule::SameValue).unwrap(); map5.set(Register(0), RegisterRule::Undefined).unwrap(); - let map6 = RegisterRuleMap::>::default(); + let map6 = RegisterRuleMap::::default(); assert_eq!(map5, map6); assert_eq!(map6, map5); } #[test] fn iter_register_rules() { - let row = UnwindTableRow::> { + let row = UnwindTableRow:: { registers: [ (Register(0), RegisterRule::SameValue), (Register(1), RegisterRule::Offset(1)), @@ -7299,7 +7348,7 @@ mod tests { #[cfg(target_pointer_width = "64")] fn size_of_unwind_ctx() { use core::mem; - let size = mem::size_of::>>(); + let size = mem::size_of::>(); let max_size = 30968; if size > max_size { assert_eq!(size, max_size); @@ -7310,7 +7359,7 @@ mod tests { #[cfg(target_pointer_width = "64")] fn size_of_register_rule_map() { use core::mem; - let size = mem::size_of::>>(); + let size = mem::size_of::>(); let max_size = 6152; if size > max_size { assert_eq!(size, max_size); @@ -7852,4 +7901,31 @@ mod tests { ); assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); } + + #[test] + fn test_unwind_context_reuse() { + fn unwind_one(ctx: &mut UnwindContext, data: &[u8]) { + let debug_frame = DebugFrame::new(data, NativeEndian); + let bases = Default::default(); + let result = debug_frame.unwind_info_for_address( + &bases, + ctx, + 0xbadb_ad99, + DebugFrame::cie_from_offset, + ); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::NoUnwindInfoForAddress); + } + + // Use the same context for two different data lifetimes. + let mut ctx: UnwindContext = UnwindContext::new(); + { + let data1 = vec![]; + unwind_one(&mut ctx, &data1); + } + { + let data2 = vec![]; + unwind_one(&mut ctx, &data2); + } + } } diff --git a/src/write/cfi.rs b/src/write/cfi.rs index 5e108f15..2ac62313 100644 --- a/src/write/cfi.rs +++ b/src/write/cfi.rs @@ -690,6 +690,7 @@ pub(crate) mod convert { if let Some(instruction) = CallFrameInstruction::from( from_instruction, from_cie, + frame, convert_address, &mut offset, )? { @@ -734,6 +735,7 @@ pub(crate) mod convert { if let Some(instruction) = CallFrameInstruction::from( from_instruction, from_cie, + frame, convert_address, &mut offset, )? { @@ -746,12 +748,17 @@ pub(crate) mod convert { } impl CallFrameInstruction { - fn from>( - from_instruction: read::CallFrameInstruction, + fn from( + from_instruction: read::CallFrameInstruction, from_cie: &read::CommonInformationEntry, + frame: &Section, convert_address: &dyn Fn(u64) -> Option
, offset: &mut u32, - ) -> ConvertResult> { + ) -> ConvertResult> + where + R: Reader, + Section: read::UnwindSection, + { let convert_expression = |x| Expression::from(x, from_cie.encoding(), None, None, None, convert_address); // TODO: validate integer type conversions @@ -785,6 +792,7 @@ pub(crate) mod convert { CallFrameInstruction::CfaOffset(offset as i32) } read::CallFrameInstruction::DefCfaExpression { expression } => { + let expression = expression.get(frame)?; CallFrameInstruction::CfaExpression(convert_expression(expression)?) } read::CallFrameInstruction::Undefined { register } => { @@ -828,11 +836,17 @@ pub(crate) mod convert { read::CallFrameInstruction::Expression { register, expression, - } => CallFrameInstruction::Expression(register, convert_expression(expression)?), + } => { + let expression = expression.get(frame)?; + CallFrameInstruction::Expression(register, convert_expression(expression)?) + } read::CallFrameInstruction::ValExpression { register, expression, - } => CallFrameInstruction::ValExpression(register, convert_expression(expression)?), + } => { + let expression = expression.get(frame)?; + CallFrameInstruction::ValExpression(register, convert_expression(expression)?) + } read::CallFrameInstruction::Restore { register } => { CallFrameInstruction::Restore(register) }