use alloc::vec::Vec; use indexmap::IndexSet; use std::ops::{Deref, DerefMut}; use crate::common::{DebugFrameOffset, EhFrameOffset, Encoding, Format, Register, SectionId}; use crate::constants; use crate::write::{Address, BaseId, Error, Expression, Result, Section, Writer}; define_section!( DebugFrame, DebugFrameOffset, "A writable `.debug_frame` section." ); define_section!(EhFrame, EhFrameOffset, "A writable `.eh_frame` section."); define_id!(CieId, "An identifier for a CIE in a `FrameTable`."); /// A table of frame description entries. #[derive(Debug, Default)] pub struct FrameTable { /// Base id for CIEs. base_id: BaseId, /// The common information entries. cies: IndexSet, /// The frame description entries. fdes: Vec<(CieId, FrameDescriptionEntry)>, } impl FrameTable { /// Add a CIE and return its id. /// /// If the CIE already exists, then return the id of the existing CIE. pub fn add_cie(&mut self, cie: CommonInformationEntry) -> CieId { let (index, _) = self.cies.insert_full(cie); CieId::new(self.base_id, index) } /// The number of CIEs. pub fn cie_count(&self) -> usize { self.cies.len() } /// Add a FDE. /// /// Does not check for duplicates. /// /// # Panics /// /// Panics if the CIE id is invalid. pub fn add_fde(&mut self, cie: CieId, fde: FrameDescriptionEntry) { debug_assert_eq!(self.base_id, cie.base_id); self.fdes.push((cie, fde)); } /// The number of FDEs. pub fn fde_count(&self) -> usize { self.fdes.len() } /// Write the frame table entries to the given `.debug_frame` section. pub fn write_debug_frame(&self, w: &mut DebugFrame) -> Result<()> { self.write(&mut w.0, false) } /// Write the frame table entries to the given `.eh_frame` section. pub fn write_eh_frame(&self, w: &mut EhFrame) -> Result<()> { self.write(&mut w.0, true) } fn write(&self, w: &mut W, eh_frame: bool) -> Result<()> { let mut cie_offsets = vec![None; self.cies.len()]; for (cie_id, fde) in &self.fdes { let cie_index = cie_id.index; let cie = self.cies.get_index(cie_index).unwrap(); let cie_offset = match cie_offsets[cie_index] { Some(offset) => offset, None => { // Only write CIEs as they are referenced. let offset = cie.write(w, eh_frame)?; cie_offsets[cie_index] = Some(offset); offset } }; fde.write(w, eh_frame, cie_offset, cie)?; } // TODO: write length 0 terminator for eh_frame? Ok(()) } } /// A common information entry. This contains information that is shared between FDEs. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CommonInformationEntry { encoding: Encoding, /// A constant that is factored out of code offsets. /// /// This should be set to the minimum instruction length. /// Writing a code offset that is not a multiple of this factor will generate an error. code_alignment_factor: u8, /// A constant that is factored out of data offsets. /// /// This should be set to the minimum data alignment for the frame. /// Writing a data offset that is not a multiple of this factor will generate an error. data_alignment_factor: i8, /// The return address register. This might not correspond to an actual machine register. return_address_register: Register, /// The address of the personality function and its encoding. pub personality: Option<(constants::DwEhPe, Address)>, /// The encoding to use for the LSDA address in FDEs. /// /// If set then all FDEs which use this CIE must have a LSDA address. pub lsda_encoding: Option, /// The encoding to use for addresses in FDEs. pub fde_address_encoding: constants::DwEhPe, /// True for signal trampolines. pub signal_trampoline: bool, /// The initial instructions upon entry to this function. instructions: Vec, } impl CommonInformationEntry { /// Create a new common information entry. /// /// The encoding version must be a CFI version, not a DWARF version. pub fn new( encoding: Encoding, code_alignment_factor: u8, data_alignment_factor: i8, return_address_register: Register, ) -> Self { CommonInformationEntry { encoding, code_alignment_factor, data_alignment_factor, return_address_register, personality: None, lsda_encoding: None, fde_address_encoding: constants::DW_EH_PE_absptr, signal_trampoline: false, instructions: Vec::new(), } } /// Add an initial instruction. pub fn add_instruction(&mut self, instruction: CallFrameInstruction) { self.instructions.push(instruction); } fn has_augmentation(&self) -> bool { self.personality.is_some() || self.lsda_encoding.is_some() || self.signal_trampoline || self.fde_address_encoding != constants::DW_EH_PE_absptr } /// Returns the section offset of the CIE. fn write(&self, w: &mut W, eh_frame: bool) -> Result { let encoding = self.encoding; let offset = w.len(); let length_offset = w.write_initial_length(encoding.format)?; let length_base = w.len(); if eh_frame { w.write_u32(0)?; } else { match encoding.format { Format::Dwarf32 => w.write_u32(0xffff_ffff)?, Format::Dwarf64 => w.write_u64(0xffff_ffff_ffff_ffff)?, } } if eh_frame { if encoding.version != 1 { return Err(Error::UnsupportedVersion(encoding.version)); }; } else { match encoding.version { 1 | 3 | 4 => {} _ => return Err(Error::UnsupportedVersion(encoding.version)), }; } w.write_u8(encoding.version as u8)?; let augmentation = self.has_augmentation(); if augmentation { w.write_u8(b'z')?; if self.lsda_encoding.is_some() { w.write_u8(b'L')?; } if self.personality.is_some() { w.write_u8(b'P')?; } if self.fde_address_encoding != constants::DW_EH_PE_absptr { w.write_u8(b'R')?; } if self.signal_trampoline { w.write_u8(b'S')?; } } w.write_u8(0)?; if encoding.version >= 4 { w.write_u8(encoding.address_size)?; // TODO: segment_selector_size w.write_u8(0)?; } w.write_uleb128(self.code_alignment_factor.into())?; w.write_sleb128(self.data_alignment_factor.into())?; if !eh_frame && encoding.version == 1 { let register = self.return_address_register.0 as u8; if u16::from(register) != self.return_address_register.0 { return Err(Error::ValueTooLarge); } w.write_u8(register)?; } else { w.write_uleb128(self.return_address_register.0.into())?; } if augmentation { let augmentation_length_offset = w.len(); w.write_u8(0)?; let augmentation_length_base = w.len(); if let Some(eh_pe) = self.lsda_encoding { w.write_u8(eh_pe.0)?; } if let Some((eh_pe, address)) = self.personality { w.write_u8(eh_pe.0)?; w.write_eh_pointer(address, eh_pe, encoding.address_size)?; } if self.fde_address_encoding != constants::DW_EH_PE_absptr { w.write_u8(self.fde_address_encoding.0)?; } let augmentation_length = (w.len() - augmentation_length_base) as u64; debug_assert!(augmentation_length < 0x80); w.write_udata_at(augmentation_length_offset, augmentation_length, 1)?; } for instruction in &self.instructions { instruction.write(w, encoding, self)?; } write_nop( w, encoding.format.word_size() as usize + w.len() - length_base, encoding.address_size, )?; let length = (w.len() - length_base) as u64; w.write_initial_length_at(length_offset, length, encoding.format)?; Ok(offset) } } /// A frame description entry. There should be one FDE per function. #[derive(Debug, Clone, PartialEq, Eq)] pub struct FrameDescriptionEntry { /// The initial address of the function. address: Address, /// The length in bytes of the function. length: u32, /// The address of the LSDA. pub lsda: Option
, /// The instructions for this function, ordered by offset. instructions: Vec<(u32, CallFrameInstruction)>, } impl FrameDescriptionEntry { /// Create a new frame description entry for a function. pub fn new(address: Address, length: u32) -> Self { FrameDescriptionEntry { address, length, lsda: None, instructions: Vec::new(), } } /// Add an instruction. /// /// Instructions must be added in increasing order of offset, or writing will fail. pub fn add_instruction(&mut self, offset: u32, instruction: CallFrameInstruction) { debug_assert!(self.instructions.last().map(|x| x.0).unwrap_or(0) <= offset); self.instructions.push((offset, instruction)); } fn write( &self, w: &mut W, eh_frame: bool, cie_offset: usize, cie: &CommonInformationEntry, ) -> Result<()> { let encoding = cie.encoding; let length_offset = w.write_initial_length(encoding.format)?; let length_base = w.len(); if eh_frame { // .eh_frame uses a relative offset which doesn't need relocation. w.write_udata((w.len() - cie_offset) as u64, 4)?; } else { w.write_offset( cie_offset, SectionId::DebugFrame, encoding.format.word_size(), )?; } if cie.fde_address_encoding != constants::DW_EH_PE_absptr { w.write_eh_pointer( self.address, cie.fde_address_encoding, encoding.address_size, )?; w.write_eh_pointer_data( self.length.into(), cie.fde_address_encoding.format(), encoding.address_size, )?; } else { w.write_address(self.address, encoding.address_size)?; w.write_udata(self.length.into(), encoding.address_size)?; } if cie.has_augmentation() { let mut augmentation_length = 0u64; if self.lsda.is_some() { augmentation_length += u64::from(encoding.address_size); } w.write_uleb128(augmentation_length)?; debug_assert_eq!(self.lsda.is_some(), cie.lsda_encoding.is_some()); if let (Some(lsda), Some(lsda_encoding)) = (self.lsda, cie.lsda_encoding) { w.write_eh_pointer(lsda, lsda_encoding, encoding.address_size)?; } } let mut prev_offset = 0; for (offset, instruction) in &self.instructions { write_advance_loc(w, cie.code_alignment_factor, prev_offset, *offset)?; prev_offset = *offset; instruction.write(w, encoding, cie)?; } write_nop( w, encoding.format.word_size() as usize + w.len() - length_base, encoding.address_size, )?; let length = (w.len() - length_base) as u64; w.write_initial_length_at(length_offset, length, encoding.format)?; Ok(()) } } /// An instruction in a frame description entry. /// /// This may be a CFA definition, a register rule, or some other directive. #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum CallFrameInstruction { /// Define the CFA rule to use the provided register and offset. Cfa(Register, i32), /// Update the CFA rule to use the provided register. The offset is unchanged. CfaRegister(Register), /// Update the CFA rule to use the provided offset. The register is unchanged. CfaOffset(i32), /// Define the CFA rule to use the provided expression. CfaExpression(Expression), /// Restore the initial rule for the register. Restore(Register), /// The previous value of the register is not recoverable. Undefined(Register), /// The register has not been modified. SameValue(Register), /// The previous value of the register is saved at address CFA + offset. Offset(Register, i32), /// The previous value of the register is CFA + offset. ValOffset(Register, i32), /// The previous value of the register is stored in another register. Register(Register, Register), /// The previous value of the register is saved at address given by the expression. Expression(Register, Expression), /// The previous value of the register is given by the expression. ValExpression(Register, Expression), /// Push all register rules onto a stack. RememberState, /// Pop all register rules off the stack. RestoreState, /// The size of the arguments that have been pushed onto the stack. ArgsSize(u32), /// AAarch64 extension: negate the `RA_SIGN_STATE` pseudo-register. NegateRaState, } impl CallFrameInstruction { fn write( &self, w: &mut W, encoding: Encoding, cie: &CommonInformationEntry, ) -> Result<()> { match *self { CallFrameInstruction::Cfa(register, offset) => { if offset < 0 { let offset = factored_data_offset(offset, cie.data_alignment_factor)?; w.write_u8(constants::DW_CFA_def_cfa_sf.0)?; w.write_uleb128(register.0.into())?; w.write_sleb128(offset.into())?; } else { // Unfactored offset. w.write_u8(constants::DW_CFA_def_cfa.0)?; w.write_uleb128(register.0.into())?; w.write_uleb128(offset as u64)?; } } CallFrameInstruction::CfaRegister(register) => { w.write_u8(constants::DW_CFA_def_cfa_register.0)?; w.write_uleb128(register.0.into())?; } CallFrameInstruction::CfaOffset(offset) => { if offset < 0 { let offset = factored_data_offset(offset, cie.data_alignment_factor)?; w.write_u8(constants::DW_CFA_def_cfa_offset_sf.0)?; w.write_sleb128(offset.into())?; } else { // Unfactored offset. w.write_u8(constants::DW_CFA_def_cfa_offset.0)?; w.write_uleb128(offset as u64)?; } } CallFrameInstruction::CfaExpression(ref expression) => { w.write_u8(constants::DW_CFA_def_cfa_expression.0)?; w.write_uleb128(expression.size(encoding, None) as u64)?; expression.write(w, None, encoding, None)?; } CallFrameInstruction::Restore(register) => { if register.0 < 0x40 { w.write_u8(constants::DW_CFA_restore.0 | register.0 as u8)?; } else { w.write_u8(constants::DW_CFA_restore_extended.0)?; w.write_uleb128(register.0.into())?; } } CallFrameInstruction::Undefined(register) => { w.write_u8(constants::DW_CFA_undefined.0)?; w.write_uleb128(register.0.into())?; } CallFrameInstruction::SameValue(register) => { w.write_u8(constants::DW_CFA_same_value.0)?; w.write_uleb128(register.0.into())?; } CallFrameInstruction::Offset(register, offset) => { let offset = factored_data_offset(offset, cie.data_alignment_factor)?; if offset < 0 { w.write_u8(constants::DW_CFA_offset_extended_sf.0)?; w.write_uleb128(register.0.into())?; w.write_sleb128(offset.into())?; } else if register.0 < 0x40 { w.write_u8(constants::DW_CFA_offset.0 | register.0 as u8)?; w.write_uleb128(offset as u64)?; } else { w.write_u8(constants::DW_CFA_offset_extended.0)?; w.write_uleb128(register.0.into())?; w.write_uleb128(offset as u64)?; } } CallFrameInstruction::ValOffset(register, offset) => { let offset = factored_data_offset(offset, cie.data_alignment_factor)?; if offset < 0 { w.write_u8(constants::DW_CFA_val_offset_sf.0)?; w.write_uleb128(register.0.into())?; w.write_sleb128(offset.into())?; } else { w.write_u8(constants::DW_CFA_val_offset.0)?; w.write_uleb128(register.0.into())?; w.write_uleb128(offset as u64)?; } } CallFrameInstruction::Register(register1, register2) => { w.write_u8(constants::DW_CFA_register.0)?; w.write_uleb128(register1.0.into())?; w.write_uleb128(register2.0.into())?; } CallFrameInstruction::Expression(register, ref expression) => { w.write_u8(constants::DW_CFA_expression.0)?; w.write_uleb128(register.0.into())?; w.write_uleb128(expression.size(encoding, None) as u64)?; expression.write(w, None, encoding, None)?; } CallFrameInstruction::ValExpression(register, ref expression) => { w.write_u8(constants::DW_CFA_val_expression.0)?; w.write_uleb128(register.0.into())?; w.write_uleb128(expression.size(encoding, None) as u64)?; expression.write(w, None, encoding, None)?; } CallFrameInstruction::RememberState => { w.write_u8(constants::DW_CFA_remember_state.0)?; } CallFrameInstruction::RestoreState => { w.write_u8(constants::DW_CFA_restore_state.0)?; } CallFrameInstruction::ArgsSize(size) => { w.write_u8(constants::DW_CFA_GNU_args_size.0)?; w.write_uleb128(size.into())?; } CallFrameInstruction::NegateRaState => { w.write_u8(constants::DW_CFA_AARCH64_negate_ra_state.0)?; } } Ok(()) } } fn write_advance_loc( w: &mut W, code_alignment_factor: u8, prev_offset: u32, offset: u32, ) -> Result<()> { if offset == prev_offset { return Ok(()); } let delta = factored_code_delta(prev_offset, offset, code_alignment_factor)?; if delta < 0x40 { w.write_u8(constants::DW_CFA_advance_loc.0 | delta as u8)?; } else if delta < 0x100 { w.write_u8(constants::DW_CFA_advance_loc1.0)?; w.write_u8(delta as u8)?; } else if delta < 0x10000 { w.write_u8(constants::DW_CFA_advance_loc2.0)?; w.write_u16(delta as u16)?; } else { w.write_u8(constants::DW_CFA_advance_loc4.0)?; w.write_u32(delta)?; } Ok(()) } fn write_nop(w: &mut W, len: usize, align: u8) -> Result<()> { debug_assert_eq!(align & (align - 1), 0); let tail_len = (!len + 1) & (align as usize - 1); for _ in 0..tail_len { w.write_u8(constants::DW_CFA_nop.0)?; } Ok(()) } fn factored_code_delta(prev_offset: u32, offset: u32, factor: u8) -> Result { if offset < prev_offset { return Err(Error::InvalidFrameCodeOffset(offset)); } let delta = offset - prev_offset; let factor = u32::from(factor); let factored_delta = delta / factor; if delta != factored_delta * factor { return Err(Error::InvalidFrameCodeOffset(offset)); } Ok(factored_delta) } fn factored_data_offset(offset: i32, factor: i8) -> Result { let factor = i32::from(factor); let factored_offset = offset / factor; if offset != factored_offset * factor { return Err(Error::InvalidFrameDataOffset(offset)); } Ok(factored_offset) } #[cfg(feature = "read")] pub(crate) mod convert { use super::*; use crate::read::{self, Reader}; use crate::write::{ConvertError, ConvertResult}; use std::collections::{hash_map, HashMap}; impl FrameTable { /// Create a frame table by reading the data in the given section. /// /// `convert_address` is a function to convert read addresses into the `Address` /// type. For non-relocatable addresses, this function may simply return /// `Address::Constant(address)`. For relocatable addresses, it is the caller's /// responsibility to determine the symbol and addend corresponding to the address /// and return `Address::Symbol { symbol, addend }`. pub fn from( frame: &Section, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult where R: Reader, Section: read::UnwindSection, Section::Offset: read::UnwindOffset, { let bases = read::BaseAddresses::default().set_eh_frame(0); let mut frame_table = FrameTable::default(); let mut cie_ids = HashMap::new(); let mut entries = frame.entries(&bases); while let Some(entry) = entries.next()? { let partial = match entry { read::CieOrFde::Cie(_) => continue, read::CieOrFde::Fde(partial) => partial, }; // TODO: is it worth caching the parsed CIEs? It would be better if FDEs only // stored a reference. let from_fde = partial.parse(Section::cie_from_offset)?; let from_cie = from_fde.cie(); let cie_id = match cie_ids.entry(from_cie.offset()) { hash_map::Entry::Occupied(o) => *o.get(), hash_map::Entry::Vacant(e) => { let cie = CommonInformationEntry::from(from_cie, frame, &bases, convert_address)?; let cie_id = frame_table.add_cie(cie); e.insert(cie_id); cie_id } }; let fde = FrameDescriptionEntry::from(&from_fde, frame, &bases, convert_address)?; frame_table.add_fde(cie_id, fde); } Ok(frame_table) } } impl CommonInformationEntry { fn from( from_cie: &read::CommonInformationEntry, frame: &Section, bases: &read::BaseAddresses, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult where R: Reader, Section: read::UnwindSection, Section::Offset: read::UnwindOffset, { let mut cie = CommonInformationEntry::new( from_cie.encoding(), from_cie.code_alignment_factor() as u8, from_cie.data_alignment_factor() as i8, from_cie.return_address_register(), ); cie.personality = match from_cie.personality_with_encoding() { // We treat these the same because the encoding already determines // whether it is indirect. Some((eh_pe, read::Pointer::Direct(p))) | Some((eh_pe, read::Pointer::Indirect(p))) => { let address = convert_address(p).ok_or(ConvertError::InvalidAddress)?; Some((eh_pe, address)) } _ => None, }; cie.lsda_encoding = from_cie.lsda_encoding(); cie.fde_address_encoding = from_cie .fde_address_encoding() .unwrap_or(constants::DW_EH_PE_absptr); cie.signal_trampoline = from_cie.is_signal_trampoline(); let mut offset = 0; let mut from_instructions = from_cie.instructions(frame, bases); while let Some(from_instruction) = from_instructions.next()? { if let Some(instruction) = CallFrameInstruction::from( from_instruction, from_cie, convert_address, &mut offset, )? { cie.instructions.push(instruction); } } Ok(cie) } } impl FrameDescriptionEntry { fn from( from_fde: &read::FrameDescriptionEntry, frame: &Section, bases: &read::BaseAddresses, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult where R: Reader, Section: read::UnwindSection, Section::Offset: read::UnwindOffset, { let address = convert_address(from_fde.initial_address()).ok_or(ConvertError::InvalidAddress)?; let length = from_fde.len() as u32; let mut fde = FrameDescriptionEntry::new(address, length); match from_fde.lsda() { // We treat these the same because the encoding already determines // whether it is indirect. Some(read::Pointer::Direct(p)) | Some(read::Pointer::Indirect(p)) => { let address = convert_address(p).ok_or(ConvertError::InvalidAddress)?; fde.lsda = Some(address); } None => {} } let from_cie = from_fde.cie(); let mut offset = 0; let mut from_instructions = from_fde.instructions(frame, bases); while let Some(from_instruction) = from_instructions.next()? { if let Some(instruction) = CallFrameInstruction::from( from_instruction, from_cie, convert_address, &mut offset, )? { fde.instructions.push((offset, instruction)); } } Ok(fde) } } impl CallFrameInstruction { fn from>( from_instruction: read::CallFrameInstruction, from_cie: &read::CommonInformationEntry, convert_address: &dyn Fn(u64) -> Option
, offset: &mut u32, ) -> ConvertResult> { let convert_expression = |x| Expression::from(x, from_cie.encoding(), None, None, None, convert_address); // TODO: validate integer type conversions Ok(Some(match from_instruction { read::CallFrameInstruction::SetLoc { .. } => { return Err(ConvertError::UnsupportedCfiInstruction); } read::CallFrameInstruction::AdvanceLoc { delta } => { *offset += delta * from_cie.code_alignment_factor() as u32; return Ok(None); } read::CallFrameInstruction::DefCfa { register, offset } => { CallFrameInstruction::Cfa(register, offset as i32) } read::CallFrameInstruction::DefCfaSf { register, factored_offset, } => { let offset = factored_offset * from_cie.data_alignment_factor(); CallFrameInstruction::Cfa(register, offset as i32) } read::CallFrameInstruction::DefCfaRegister { register } => { CallFrameInstruction::CfaRegister(register) } read::CallFrameInstruction::DefCfaOffset { offset } => { CallFrameInstruction::CfaOffset(offset as i32) } read::CallFrameInstruction::DefCfaOffsetSf { factored_offset } => { let offset = factored_offset * from_cie.data_alignment_factor(); CallFrameInstruction::CfaOffset(offset as i32) } read::CallFrameInstruction::DefCfaExpression { expression } => { CallFrameInstruction::CfaExpression(convert_expression(expression)?) } read::CallFrameInstruction::Undefined { register } => { CallFrameInstruction::Undefined(register) } read::CallFrameInstruction::SameValue { register } => { CallFrameInstruction::SameValue(register) } read::CallFrameInstruction::Offset { register, factored_offset, } => { let offset = factored_offset as i64 * from_cie.data_alignment_factor(); CallFrameInstruction::Offset(register, offset as i32) } read::CallFrameInstruction::OffsetExtendedSf { register, factored_offset, } => { let offset = factored_offset * from_cie.data_alignment_factor(); CallFrameInstruction::Offset(register, offset as i32) } read::CallFrameInstruction::ValOffset { register, factored_offset, } => { let offset = factored_offset as i64 * from_cie.data_alignment_factor(); CallFrameInstruction::ValOffset(register, offset as i32) } read::CallFrameInstruction::ValOffsetSf { register, factored_offset, } => { let offset = factored_offset * from_cie.data_alignment_factor(); CallFrameInstruction::ValOffset(register, offset as i32) } read::CallFrameInstruction::Register { dest_register, src_register, } => CallFrameInstruction::Register(dest_register, src_register), read::CallFrameInstruction::Expression { register, expression, } => CallFrameInstruction::Expression(register, convert_expression(expression)?), read::CallFrameInstruction::ValExpression { register, expression, } => CallFrameInstruction::ValExpression(register, convert_expression(expression)?), read::CallFrameInstruction::Restore { register } => { CallFrameInstruction::Restore(register) } read::CallFrameInstruction::RememberState => CallFrameInstruction::RememberState, read::CallFrameInstruction::RestoreState => CallFrameInstruction::RestoreState, read::CallFrameInstruction::ArgsSize { size } => { CallFrameInstruction::ArgsSize(size as u32) } read::CallFrameInstruction::NegateRaState => CallFrameInstruction::NegateRaState, read::CallFrameInstruction::Nop => return Ok(None), })) } } } #[cfg(test)] #[cfg(feature = "read")] mod tests { use super::*; use crate::arch::X86_64; use crate::read; use crate::write::EndianVec; use crate::{LittleEndian, Vendor}; #[test] fn test_frame_table() { for &version in &[1, 3, 4] { for &address_size in &[4, 8] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let mut frames = FrameTable::default(); let cie1 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA); let cie1_id = frames.add_cie(cie1.clone()); assert_eq!(cie1_id, frames.add_cie(cie1.clone())); let mut cie2 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA); cie2.lsda_encoding = Some(constants::DW_EH_PE_absptr); cie2.personality = Some((constants::DW_EH_PE_absptr, Address::Constant(0x1234))); cie2.signal_trampoline = true; let cie2_id = frames.add_cie(cie2.clone()); assert_ne!(cie1_id, cie2_id); assert_eq!(cie2_id, frames.add_cie(cie2.clone())); let fde1 = FrameDescriptionEntry::new(Address::Constant(0x1000), 0x10); frames.add_fde(cie1_id, fde1.clone()); let fde2 = FrameDescriptionEntry::new(Address::Constant(0x2000), 0x20); frames.add_fde(cie1_id, fde2.clone()); let mut fde3 = FrameDescriptionEntry::new(Address::Constant(0x3000), 0x30); fde3.lsda = Some(Address::Constant(0x3300)); frames.add_fde(cie2_id, fde3.clone()); let mut fde4 = FrameDescriptionEntry::new(Address::Constant(0x4000), 0x40); fde4.lsda = Some(Address::Constant(0x4400)); frames.add_fde(cie2_id, fde4.clone()); let mut cie3 = CommonInformationEntry::new(encoding, 1, 8, X86_64::RA); cie3.fde_address_encoding = constants::DW_EH_PE_pcrel; cie3.lsda_encoding = Some(constants::DW_EH_PE_pcrel); cie3.personality = Some((constants::DW_EH_PE_pcrel, Address::Constant(0x1235))); cie3.signal_trampoline = true; let cie3_id = frames.add_cie(cie3.clone()); assert_ne!(cie2_id, cie3_id); assert_eq!(cie3_id, frames.add_cie(cie3.clone())); let mut fde5 = FrameDescriptionEntry::new(Address::Constant(0x5000), 0x50); fde5.lsda = Some(Address::Constant(0x5500)); frames.add_fde(cie3_id, fde5.clone()); // Test writing `.debug_frame`. let mut debug_frame = DebugFrame::from(EndianVec::new(LittleEndian)); frames.write_debug_frame(&mut debug_frame).unwrap(); let mut read_debug_frame = read::DebugFrame::new(debug_frame.slice(), LittleEndian); read_debug_frame.set_address_size(address_size); let convert_frames = FrameTable::from(&read_debug_frame, &|address| { Some(Address::Constant(address)) }) .unwrap(); assert_eq!(frames.cies, convert_frames.cies); assert_eq!(frames.fdes.len(), convert_frames.fdes.len()); for (a, b) in frames.fdes.iter().zip(convert_frames.fdes.iter()) { assert_eq!(a.1, b.1); } if version == 1 { // Test writing `.eh_frame`. let mut eh_frame = EhFrame::from(EndianVec::new(LittleEndian)); frames.write_eh_frame(&mut eh_frame).unwrap(); let mut read_eh_frame = read::EhFrame::new(eh_frame.slice(), LittleEndian); read_eh_frame.set_address_size(address_size); let convert_frames = FrameTable::from(&read_eh_frame, &|address| { Some(Address::Constant(address)) }) .unwrap(); assert_eq!(frames.cies, convert_frames.cies); assert_eq!(frames.fdes.len(), convert_frames.fdes.len()); for (a, b) in frames.fdes.iter().zip(convert_frames.fdes.iter()) { assert_eq!(a.1, b.1); } } } } } } #[test] fn test_frame_instruction() { let mut expression = Expression::new(); expression.op_constu(0); let cie_instructions = [ CallFrameInstruction::Cfa(X86_64::RSP, 8), CallFrameInstruction::Offset(X86_64::RA, -8), ]; let fde_instructions = [ (0, CallFrameInstruction::Cfa(X86_64::RSP, 0)), (0, CallFrameInstruction::Cfa(X86_64::RSP, -8)), (2, CallFrameInstruction::CfaRegister(X86_64::RBP)), (4, CallFrameInstruction::CfaOffset(8)), (4, CallFrameInstruction::CfaOffset(0)), (4, CallFrameInstruction::CfaOffset(-8)), (6, CallFrameInstruction::CfaExpression(expression.clone())), (8, CallFrameInstruction::Restore(Register(1))), (8, CallFrameInstruction::Restore(Register(101))), (10, CallFrameInstruction::Undefined(Register(2))), (12, CallFrameInstruction::SameValue(Register(3))), (14, CallFrameInstruction::Offset(Register(4), 16)), (14, CallFrameInstruction::Offset(Register(104), 16)), (16, CallFrameInstruction::ValOffset(Register(5), -24)), (16, CallFrameInstruction::ValOffset(Register(5), 24)), (18, CallFrameInstruction::Register(Register(6), Register(7))), ( 20, CallFrameInstruction::Expression(Register(8), expression.clone()), ), ( 22, CallFrameInstruction::ValExpression(Register(9), expression.clone()), ), (24 + 0x80, CallFrameInstruction::RememberState), (26 + 0x280, CallFrameInstruction::RestoreState), (28 + 0x20280, CallFrameInstruction::ArgsSize(23)), ]; let fde_instructions_aarch64 = [(0, CallFrameInstruction::NegateRaState)]; for &version in &[1, 3, 4] { for &address_size in &[4, 8] { for &vendor in &[Vendor::Default, Vendor::AArch64] { for &format in &[Format::Dwarf32, Format::Dwarf64] { let encoding = Encoding { format, version, address_size, }; let mut frames = FrameTable::default(); let mut cie = CommonInformationEntry::new(encoding, 2, 8, X86_64::RA); for i in &cie_instructions { cie.add_instruction(i.clone()); } let cie_id = frames.add_cie(cie); let mut fde = FrameDescriptionEntry::new(Address::Constant(0x1000), 0x10); for (o, i) in &fde_instructions { fde.add_instruction(*o, i.clone()); } frames.add_fde(cie_id, fde); if vendor == Vendor::AArch64 { let mut fde = FrameDescriptionEntry::new(Address::Constant(0x2000), 0x10); for (o, i) in &fde_instructions_aarch64 { fde.add_instruction(*o, i.clone()); } frames.add_fde(cie_id, fde); } let mut debug_frame = DebugFrame::from(EndianVec::new(LittleEndian)); frames.write_debug_frame(&mut debug_frame).unwrap(); let mut read_debug_frame = read::DebugFrame::new(debug_frame.slice(), LittleEndian); read_debug_frame.set_address_size(address_size); read_debug_frame.set_vendor(vendor); let frames = FrameTable::from(&read_debug_frame, &|address| { Some(Address::Constant(address)) }) .unwrap(); assert_eq!( &frames.cies.get_index(0).unwrap().instructions, &cie_instructions ); assert_eq!(&frames.fdes[0].1.instructions, &fde_instructions); if vendor == Vendor::AArch64 { assert_eq!(&frames.fdes[1].1.instructions, &fde_instructions_aarch64); } } } } } } }