| 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<CommonInformationEntry>, |
| /// 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<W: Writer>(&self, w: &mut DebugFrame<W>) -> Result<()> { |
| self.write(&mut w.0, false) |
| } |
| |
| /// Write the frame table entries to the given `.eh_frame` section. |
| pub fn write_eh_frame<W: Writer>(&self, w: &mut EhFrame<W>) -> Result<()> { |
| self.write(&mut w.0, true) |
| } |
| |
| fn write<W: Writer>(&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<constants::DwEhPe>, |
| |
| /// 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<CallFrameInstruction>, |
| } |
| |
| 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<W: Writer>(&self, w: &mut W, eh_frame: bool) -> Result<usize> { |
| 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, constants::DW_EH_PE_absptr, 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<Address>, |
| |
| /// 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<W: Writer>( |
| &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)] |
| 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), |
| } |
| |
| impl CallFrameInstruction { |
| fn write<W: Writer>( |
| &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())?; |
| } |
| } |
| Ok(()) |
| } |
| } |
| |
| fn write_advance_loc<W: Writer>( |
| 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: Writer>(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<u32> { |
| 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<i32> { |
| 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<R, Section>( |
| frame: &Section, |
| convert_address: &dyn Fn(u64) -> Option<Address>, |
| ) -> ConvertResult<FrameTable> |
| where |
| R: Reader<Offset = usize>, |
| Section: read::UnwindSection<R>, |
| Section::Offset: read::UnwindOffset<usize>, |
| { |
| 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<R, Section>( |
| from_cie: &read::CommonInformationEntry<R>, |
| frame: &Section, |
| bases: &read::BaseAddresses, |
| convert_address: &dyn Fn(u64) -> Option<Address>, |
| ) -> ConvertResult<CommonInformationEntry> |
| where |
| R: Reader<Offset = usize>, |
| Section: read::UnwindSection<R>, |
| Section::Offset: read::UnwindOffset<usize>, |
| { |
| 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<R, Section>( |
| from_fde: &read::FrameDescriptionEntry<R>, |
| frame: &Section, |
| bases: &read::BaseAddresses, |
| convert_address: &dyn Fn(u64) -> Option<Address>, |
| ) -> ConvertResult<FrameDescriptionEntry> |
| where |
| R: Reader<Offset = usize>, |
| Section: read::UnwindSection<R>, |
| Section::Offset: read::UnwindOffset<usize>, |
| { |
| 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<R: Reader<Offset = usize>>( |
| from_instruction: read::CallFrameInstruction<R>, |
| from_cie: &read::CommonInformationEntry<R>, |
| convert_address: &dyn Fn(u64) -> Option<Address>, |
| offset: &mut u32, |
| ) -> ConvertResult<Option<CallFrameInstruction>> { |
| 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::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; |
| |
| #[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()); |
| |
| // 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)), |
| ]; |
| |
| 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 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); |
| |
| 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 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); |
| } |
| } |
| } |
| } |
| } |