diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /vendor/gimli/src/write | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gimli/src/write')
-rw-r--r-- | vendor/gimli/src/write/abbrev.rs | 188 | ||||
-rw-r--r-- | vendor/gimli/src/write/cfi.rs | 1012 | ||||
-rw-r--r-- | vendor/gimli/src/write/dwarf.rs | 138 | ||||
-rw-r--r-- | vendor/gimli/src/write/endian_vec.rs | 117 | ||||
-rw-r--r-- | vendor/gimli/src/write/line.rs | 1960 | ||||
-rw-r--r-- | vendor/gimli/src/write/loc.rs | 549 | ||||
-rw-r--r-- | vendor/gimli/src/write/mod.rs | 425 | ||||
-rw-r--r-- | vendor/gimli/src/write/op.rs | 1617 | ||||
-rw-r--r-- | vendor/gimli/src/write/range.rs | 415 | ||||
-rw-r--r-- | vendor/gimli/src/write/section.rs | 172 | ||||
-rw-r--r-- | vendor/gimli/src/write/str.rs | 172 | ||||
-rw-r--r-- | vendor/gimli/src/write/unit.rs | 3157 | ||||
-rw-r--r-- | vendor/gimli/src/write/writer.rs | 497 |
13 files changed, 10419 insertions, 0 deletions
diff --git a/vendor/gimli/src/write/abbrev.rs b/vendor/gimli/src/write/abbrev.rs new file mode 100644 index 000000000..7cdfa969c --- /dev/null +++ b/vendor/gimli/src/write/abbrev.rs @@ -0,0 +1,188 @@ +use alloc::vec::Vec; +use indexmap::IndexSet; +use std::ops::{Deref, DerefMut}; + +use crate::common::{DebugAbbrevOffset, SectionId}; +use crate::constants; +use crate::write::{Result, Section, Writer}; + +/// A table of abbreviations that will be stored in a `.debug_abbrev` section. +// Requirements: +// - values are `Abbreviation` +// - insertion returns an abbreviation code for use in writing a DIE +// - inserting a duplicate returns the code of the existing value +#[derive(Debug, Default)] +pub(crate) struct AbbreviationTable { + abbrevs: IndexSet<Abbreviation>, +} + +impl AbbreviationTable { + /// Add an abbreviation to the table and return its code. + pub fn add(&mut self, abbrev: Abbreviation) -> u64 { + let (code, _) = self.abbrevs.insert_full(abbrev); + // Code must be non-zero + (code + 1) as u64 + } + + /// Write the abbreviation table to the `.debug_abbrev` section. + pub fn write<W: Writer>(&self, w: &mut DebugAbbrev<W>) -> Result<()> { + for (code, abbrev) in self.abbrevs.iter().enumerate() { + w.write_uleb128((code + 1) as u64)?; + abbrev.write(w)?; + } + // Null abbreviation code + w.write_u8(0) + } +} + +/// An abbreviation describes the shape of a `DebuggingInformationEntry`'s type: +/// its tag type, whether it has children, and its set of attributes. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub(crate) struct Abbreviation { + tag: constants::DwTag, + has_children: bool, + attributes: Vec<AttributeSpecification>, +} + +impl Abbreviation { + /// Construct a new `Abbreviation`. + #[inline] + pub fn new( + tag: constants::DwTag, + has_children: bool, + attributes: Vec<AttributeSpecification>, + ) -> Abbreviation { + Abbreviation { + tag, + has_children, + attributes, + } + } + + /// Write the abbreviation to the `.debug_abbrev` section. + pub fn write<W: Writer>(&self, w: &mut DebugAbbrev<W>) -> Result<()> { + w.write_uleb128(self.tag.0.into())?; + w.write_u8(if self.has_children { + constants::DW_CHILDREN_yes.0 + } else { + constants::DW_CHILDREN_no.0 + })?; + for attr in &self.attributes { + attr.write(w)?; + } + // Null name and form + w.write_u8(0)?; + w.write_u8(0) + } +} + +/// The description of an attribute in an abbreviated type. +// TODO: support implicit const +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) struct AttributeSpecification { + name: constants::DwAt, + form: constants::DwForm, +} + +impl AttributeSpecification { + /// Construct a new `AttributeSpecification`. + #[inline] + pub fn new(name: constants::DwAt, form: constants::DwForm) -> AttributeSpecification { + AttributeSpecification { name, form } + } + + /// Write the attribute specification to the `.debug_abbrev` section. + #[inline] + pub fn write<W: Writer>(&self, w: &mut DebugAbbrev<W>) -> Result<()> { + w.write_uleb128(self.name.0.into())?; + w.write_uleb128(self.form.0.into()) + } +} + +define_section!( + DebugAbbrev, + DebugAbbrevOffset, + "A writable `.debug_abbrev` section." +); + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::constants; + use crate::read; + use crate::write::EndianVec; + use crate::LittleEndian; + + #[test] + fn test_abbreviation_table() { + let mut abbrevs = AbbreviationTable::default(); + let abbrev1 = Abbreviation::new( + constants::DW_TAG_subprogram, + false, + vec![AttributeSpecification::new( + constants::DW_AT_name, + constants::DW_FORM_string, + )], + ); + let abbrev2 = Abbreviation::new( + constants::DW_TAG_compile_unit, + true, + vec![ + AttributeSpecification::new(constants::DW_AT_producer, constants::DW_FORM_strp), + AttributeSpecification::new(constants::DW_AT_language, constants::DW_FORM_data2), + ], + ); + let code1 = abbrevs.add(abbrev1.clone()); + assert_eq!(code1, 1); + let code2 = abbrevs.add(abbrev2.clone()); + assert_eq!(code2, 2); + assert_eq!(abbrevs.add(abbrev1.clone()), code1); + assert_eq!(abbrevs.add(abbrev2.clone()), code2); + + let mut debug_abbrev = DebugAbbrev::from(EndianVec::new(LittleEndian)); + let debug_abbrev_offset = debug_abbrev.offset(); + assert_eq!(debug_abbrev_offset, DebugAbbrevOffset(0)); + abbrevs.write(&mut debug_abbrev).unwrap(); + assert_eq!(debug_abbrev.offset(), DebugAbbrevOffset(17)); + + let read_debug_abbrev = read::DebugAbbrev::new(debug_abbrev.slice(), LittleEndian); + let read_abbrevs = read_debug_abbrev + .abbreviations(debug_abbrev_offset) + .unwrap(); + + let read_abbrev1 = read_abbrevs.get(code1).unwrap(); + assert_eq!(abbrev1.tag, read_abbrev1.tag()); + assert_eq!(abbrev1.has_children, read_abbrev1.has_children()); + assert_eq!(abbrev1.attributes.len(), read_abbrev1.attributes().len()); + assert_eq!( + abbrev1.attributes[0].name, + read_abbrev1.attributes()[0].name() + ); + assert_eq!( + abbrev1.attributes[0].form, + read_abbrev1.attributes()[0].form() + ); + + let read_abbrev2 = read_abbrevs.get(code2).unwrap(); + assert_eq!(abbrev2.tag, read_abbrev2.tag()); + assert_eq!(abbrev2.has_children, read_abbrev2.has_children()); + assert_eq!(abbrev2.attributes.len(), read_abbrev2.attributes().len()); + assert_eq!( + abbrev2.attributes[0].name, + read_abbrev2.attributes()[0].name() + ); + assert_eq!( + abbrev2.attributes[0].form, + read_abbrev2.attributes()[0].form() + ); + assert_eq!( + abbrev2.attributes[1].name, + read_abbrev2.attributes()[1].name() + ); + assert_eq!( + abbrev2.attributes[1].form, + read_abbrev2.attributes()[1].form() + ); + } +} diff --git a/vendor/gimli/src/write/cfi.rs b/vendor/gimli/src/write/cfi.rs new file mode 100644 index 000000000..c58eb1b1d --- /dev/null +++ b/vendor/gimli/src/write/cfi.rs @@ -0,0 +1,1012 @@ +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); + } + } + } + } +} diff --git a/vendor/gimli/src/write/dwarf.rs b/vendor/gimli/src/write/dwarf.rs new file mode 100644 index 000000000..ea507126a --- /dev/null +++ b/vendor/gimli/src/write/dwarf.rs @@ -0,0 +1,138 @@ +use alloc::vec::Vec; + +use crate::common::Encoding; +use crate::write::{ + AbbreviationTable, LineProgram, LineStringTable, Result, Sections, StringTable, Unit, + UnitTable, Writer, +}; + +/// Writable DWARF information for more than one unit. +#[derive(Debug, Default)] +pub struct Dwarf { + /// A table of units. These are primarily stored in the `.debug_info` section, + /// but they also contain information that is stored in other sections. + pub units: UnitTable, + + /// Extra line number programs that are not associated with a unit. + /// + /// These should only be used when generating DWARF5 line-only debug + /// information. + pub line_programs: Vec<LineProgram>, + + /// A table of strings that will be stored in the `.debug_line_str` section. + pub line_strings: LineStringTable, + + /// A table of strings that will be stored in the `.debug_str` section. + pub strings: StringTable, +} + +impl Dwarf { + /// Create a new `Dwarf` instance. + #[inline] + pub fn new() -> Self { + Self::default() + } + + /// Write the DWARF information to the given sections. + pub fn write<W: Writer>(&mut self, sections: &mut Sections<W>) -> Result<()> { + let line_strings = self.line_strings.write(&mut sections.debug_line_str)?; + let strings = self.strings.write(&mut sections.debug_str)?; + self.units.write(sections, &line_strings, &strings)?; + for line_program in &self.line_programs { + line_program.write( + &mut sections.debug_line, + line_program.encoding(), + &line_strings, + &strings, + )?; + } + Ok(()) + } +} + +/// Writable DWARF information for a single unit. +#[derive(Debug)] +pub struct DwarfUnit { + /// A unit. This is primarily stored in the `.debug_info` section, + /// but also contains information that is stored in other sections. + pub unit: Unit, + + /// A table of strings that will be stored in the `.debug_line_str` section. + pub line_strings: LineStringTable, + + /// A table of strings that will be stored in the `.debug_str` section. + pub strings: StringTable, +} + +impl DwarfUnit { + /// Create a new `DwarfUnit`. + /// + /// Note: you should set `self.unit.line_program` after creation. + /// This cannot be done earlier because it may need to reference + /// `self.line_strings`. + pub fn new(encoding: Encoding) -> Self { + let unit = Unit::new(encoding, LineProgram::none()); + DwarfUnit { + unit, + line_strings: LineStringTable::default(), + strings: StringTable::default(), + } + } + + /// Write the DWARf information to the given sections. + pub fn write<W: Writer>(&mut self, sections: &mut Sections<W>) -> Result<()> { + let line_strings = self.line_strings.write(&mut sections.debug_line_str)?; + let strings = self.strings.write(&mut sections.debug_str)?; + + let abbrev_offset = sections.debug_abbrev.offset(); + let mut abbrevs = AbbreviationTable::default(); + + self.unit.write( + sections, + abbrev_offset, + &mut abbrevs, + &line_strings, + &strings, + )?; + // None should exist because we didn't give out any UnitId. + assert!(sections.debug_info_refs.is_empty()); + assert!(sections.debug_loc_refs.is_empty()); + assert!(sections.debug_loclists_refs.is_empty()); + + abbrevs.write(&mut sections.debug_abbrev)?; + Ok(()) + } +} + +#[cfg(feature = "read")] +pub(crate) mod convert { + use super::*; + use crate::read::{self, Reader}; + use crate::write::{Address, ConvertResult}; + + impl Dwarf { + /// Create a `write::Dwarf` by converting a `read::Dwarf`. + /// + /// `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: Reader<Offset = usize>>( + dwarf: &read::Dwarf<R>, + convert_address: &dyn Fn(u64) -> Option<Address>, + ) -> ConvertResult<Dwarf> { + let mut line_strings = LineStringTable::default(); + let mut strings = StringTable::default(); + let units = UnitTable::from(dwarf, &mut line_strings, &mut strings, convert_address)?; + // TODO: convert the line programs that were not referenced by a unit. + let line_programs = Vec::new(); + Ok(Dwarf { + units, + line_programs, + line_strings, + strings, + }) + } + } +} diff --git a/vendor/gimli/src/write/endian_vec.rs b/vendor/gimli/src/write/endian_vec.rs new file mode 100644 index 000000000..7b040606a --- /dev/null +++ b/vendor/gimli/src/write/endian_vec.rs @@ -0,0 +1,117 @@ +use alloc::vec::Vec; +use std::mem; + +use crate::endianity::Endianity; +use crate::write::{Error, Result, Writer}; + +/// A `Vec<u8>` with endianity metadata. +/// +/// This implements the `Writer` trait, which is used for all writing of DWARF sections. +#[derive(Debug, Clone)] +pub struct EndianVec<Endian> +where + Endian: Endianity, +{ + vec: Vec<u8>, + endian: Endian, +} + +impl<Endian> EndianVec<Endian> +where + Endian: Endianity, +{ + /// Construct an empty `EndianVec` with the given endianity. + pub fn new(endian: Endian) -> EndianVec<Endian> { + EndianVec { + vec: Vec::new(), + endian, + } + } + + /// Return a reference to the raw slice. + pub fn slice(&self) -> &[u8] { + &self.vec + } + + /// Convert into a `Vec<u8>`. + pub fn into_vec(self) -> Vec<u8> { + self.vec + } + + /// Take any written data out of the `EndianVec`, leaving an empty `Vec` in its place. + pub fn take(&mut self) -> Vec<u8> { + let mut vec = Vec::new(); + mem::swap(&mut self.vec, &mut vec); + vec + } +} + +impl<Endian> Writer for EndianVec<Endian> +where + Endian: Endianity, +{ + type Endian = Endian; + + #[inline] + fn endian(&self) -> Self::Endian { + self.endian + } + + #[inline] + fn len(&self) -> usize { + self.vec.len() + } + + fn write(&mut self, bytes: &[u8]) -> Result<()> { + self.vec.extend(bytes); + Ok(()) + } + + fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> { + if offset > self.vec.len() { + return Err(Error::OffsetOutOfBounds); + } + let to = &mut self.vec[offset..]; + if bytes.len() > to.len() { + return Err(Error::LengthOutOfBounds); + } + let to = &mut to[..bytes.len()]; + to.copy_from_slice(bytes); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::LittleEndian; + + #[test] + fn test_endian_vec() { + let mut w = EndianVec::new(LittleEndian); + assert_eq!(w.endian(), LittleEndian); + assert_eq!(w.len(), 0); + + w.write(&[1, 2]).unwrap(); + assert_eq!(w.slice(), &[1, 2]); + assert_eq!(w.len(), 2); + + w.write(&[3, 4, 5]).unwrap(); + assert_eq!(w.slice(), &[1, 2, 3, 4, 5]); + assert_eq!(w.len(), 5); + + w.write_at(0, &[6, 7]).unwrap(); + assert_eq!(w.slice(), &[6, 7, 3, 4, 5]); + assert_eq!(w.len(), 5); + + w.write_at(3, &[8, 9]).unwrap(); + assert_eq!(w.slice(), &[6, 7, 3, 8, 9]); + assert_eq!(w.len(), 5); + + assert_eq!(w.write_at(4, &[6, 7]), Err(Error::LengthOutOfBounds)); + assert_eq!(w.write_at(5, &[6, 7]), Err(Error::LengthOutOfBounds)); + assert_eq!(w.write_at(6, &[6, 7]), Err(Error::OffsetOutOfBounds)); + + assert_eq!(w.into_vec(), vec![6, 7, 3, 8, 9]); + } +} diff --git a/vendor/gimli/src/write/line.rs b/vendor/gimli/src/write/line.rs new file mode 100644 index 000000000..310170d9a --- /dev/null +++ b/vendor/gimli/src/write/line.rs @@ -0,0 +1,1960 @@ +use alloc::vec::Vec; +use indexmap::{IndexMap, IndexSet}; +use std::ops::{Deref, DerefMut}; + +use crate::common::{DebugLineOffset, Encoding, Format, LineEncoding, SectionId}; +use crate::constants; +use crate::leb128; +use crate::write::{ + Address, DebugLineStrOffsets, DebugStrOffsets, Error, LineStringId, LineStringTable, Result, + Section, StringId, Writer, +}; + +/// The number assigned to the first special opcode. +// +// We output all instructions for all DWARF versions, since readers +// should be able to ignore instructions they don't support. +const OPCODE_BASE: u8 = 13; + +/// A line number program. +#[derive(Debug, Clone)] +pub struct LineProgram { + /// True if this line program was created with `LineProgram::none()`. + none: bool, + encoding: Encoding, + line_encoding: LineEncoding, + + /// A list of source directory path names. + /// + /// If a path is relative, then the directory is located relative to the working + /// directory of the compilation unit. + /// + /// The first entry is for the working directory of the compilation unit. + directories: IndexSet<LineString>, + + /// A list of source file entries. + /// + /// Each entry has a path name and a directory. + /// + /// If a path is a relative, then the file is located relative to the + /// directory. Otherwise the directory is meaningless. + /// + /// Does not include comp_file, even for version >= 5. + files: IndexMap<(LineString, DirectoryId), FileInfo>, + + /// The primary source file of the compilation unit. + /// This is required for version >= 5, but we never reference it elsewhere + /// because DWARF defines DW_AT_decl_file=0 to mean not specified. + comp_file: (LineString, FileInfo), + + /// True if the file entries may have valid timestamps. + /// + /// Entries may still have a timestamp of 0 even if this is set. + /// For version <= 4, this is ignored. + /// For version 5, this controls whether to emit `DW_LNCT_timestamp`. + pub file_has_timestamp: bool, + + /// True if the file entries may have valid sizes. + /// + /// Entries may still have a size of 0 even if this is set. + /// For version <= 4, this is ignored. + /// For version 5, this controls whether to emit `DW_LNCT_size`. + pub file_has_size: bool, + + /// True if the file entries have valid MD5 checksums. + /// + /// For version <= 4, this is ignored. + /// For version 5, this controls whether to emit `DW_LNCT_MD5`. + pub file_has_md5: bool, + + prev_row: LineRow, + row: LineRow, + // TODO: this probably should be either rows or sequences instead + instructions: Vec<LineInstruction>, + in_sequence: bool, +} + +impl LineProgram { + /// Create a new `LineProgram`. + /// + /// `comp_dir` defines the working directory of the compilation unit, + /// and must be the same as the `DW_AT_comp_dir` attribute + /// of the compilation unit DIE. + /// + /// `comp_file` and `comp_file_info` define the primary source file + /// of the compilation unit and must be the same as the `DW_AT_name` + /// attribute of the compilation unit DIE. + /// + /// # Panics + /// + /// Panics if `line_encoding.line_base` > 0. + /// + /// Panics if `line_encoding.line_base` + `line_encoding.line_range` <= 0. + /// + /// Panics if `comp_dir` is empty or contains a null byte. + /// + /// Panics if `comp_file` is empty or contains a null byte. + #[allow(clippy::too_many_arguments)] + #[allow(clippy::new_ret_no_self)] + pub fn new( + encoding: Encoding, + line_encoding: LineEncoding, + comp_dir: LineString, + comp_file: LineString, + comp_file_info: Option<FileInfo>, + ) -> LineProgram { + // We require a special opcode for a line advance of 0. + // See the debug_asserts in generate_row(). + assert!(line_encoding.line_base <= 0); + assert!(line_encoding.line_base + line_encoding.line_range as i8 > 0); + let mut program = LineProgram { + none: false, + encoding, + line_encoding, + directories: IndexSet::new(), + files: IndexMap::new(), + comp_file: (comp_file, comp_file_info.unwrap_or_default()), + prev_row: LineRow::initial_state(line_encoding), + row: LineRow::initial_state(line_encoding), + instructions: Vec::new(), + in_sequence: false, + file_has_timestamp: false, + file_has_size: false, + file_has_md5: false, + }; + // For all DWARF versions, directory index 0 is comp_dir. + // For version <= 4, the entry is implicit. We still add + // it here so that we use it, but we don't emit it. + program.add_directory(comp_dir); + program + } + + /// Create a new `LineProgram` with no fields set. + /// + /// This can be used when the `LineProgram` will not be used. + /// + /// You should not attempt to add files or line instructions to + /// this line program, or write it to the `.debug_line` section. + pub fn none() -> Self { + let line_encoding = LineEncoding::default(); + LineProgram { + none: true, + encoding: Encoding { + format: Format::Dwarf32, + version: 2, + address_size: 0, + }, + line_encoding, + directories: IndexSet::new(), + files: IndexMap::new(), + comp_file: (LineString::String(Vec::new()), FileInfo::default()), + prev_row: LineRow::initial_state(line_encoding), + row: LineRow::initial_state(line_encoding), + instructions: Vec::new(), + in_sequence: false, + file_has_timestamp: false, + file_has_size: false, + file_has_md5: false, + } + } + + /// Return true if this line program was created with `LineProgram::none()`. + #[inline] + pub fn is_none(&self) -> bool { + self.none + } + + /// Return the encoding parameters for this line program. + #[inline] + pub fn encoding(&self) -> Encoding { + self.encoding + } + + /// Return the DWARF version for this line program. + #[inline] + pub fn version(&self) -> u16 { + self.encoding.version + } + + /// Return the address size in bytes for this line program. + #[inline] + pub fn address_size(&self) -> u8 { + self.encoding.address_size + } + + /// Return the DWARF format for this line program. + #[inline] + pub fn format(&self) -> Format { + self.encoding.format + } + + /// Return the id for the working directory of the compilation unit. + #[inline] + pub fn default_directory(&self) -> DirectoryId { + DirectoryId(0) + } + + /// Add a directory entry and return its id. + /// + /// If the directory already exists, then return the id of the existing entry. + /// + /// If the path is relative, then the directory is located relative to the working + /// directory of the compilation unit. + /// + /// # Panics + /// + /// Panics if `directory` is empty or contains a null byte. + pub fn add_directory(&mut self, directory: LineString) -> DirectoryId { + if let LineString::String(ref val) = directory { + // For DWARF version <= 4, directories must not be empty. + // The first directory isn't emitted so skip the check for it. + if self.encoding.version <= 4 && !self.directories.is_empty() { + assert!(!val.is_empty()); + } + assert!(!val.contains(&0)); + } + let (index, _) = self.directories.insert_full(directory); + DirectoryId(index) + } + + /// Get a reference to a directory entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get_directory(&self, id: DirectoryId) -> &LineString { + self.directories.get_index(id.0).unwrap() + } + + /// Add a file entry and return its id. + /// + /// If the file already exists, then return the id of the existing entry. + /// + /// If the file path is relative, then the file is located relative + /// to the directory. Otherwise the directory is meaningless, but it + /// is still used as a key for file entries. + /// + /// If `info` is `None`, then new entries are assigned + /// default information, and existing entries are unmodified. + /// + /// If `info` is not `None`, then it is always assigned to the + /// entry, even if the entry already exists. + /// + /// # Panics + /// + /// Panics if 'file' is empty or contains a null byte. + pub fn add_file( + &mut self, + file: LineString, + directory: DirectoryId, + info: Option<FileInfo>, + ) -> FileId { + if let LineString::String(ref val) = file { + assert!(!val.is_empty()); + assert!(!val.contains(&0)); + } + + let key = (file, directory); + let index = if let Some(info) = info { + let (index, _) = self.files.insert_full(key, info); + index + } else { + let entry = self.files.entry(key); + let index = entry.index(); + entry.or_insert(FileInfo::default()); + index + }; + FileId::new(index) + } + + /// Get a reference to a file entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get_file(&self, id: FileId) -> (&LineString, DirectoryId) { + match id.index() { + None => (&self.comp_file.0, DirectoryId(0)), + Some(index) => self + .files + .get_index(index) + .map(|entry| (&(entry.0).0, (entry.0).1)) + .unwrap(), + } + } + + /// Get a reference to the info for a file entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get_file_info(&self, id: FileId) -> &FileInfo { + match id.index() { + None => &self.comp_file.1, + Some(index) => self.files.get_index(index).map(|entry| entry.1).unwrap(), + } + } + + /// Get a mutable reference to the info for a file entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get_file_info_mut(&mut self, id: FileId) -> &mut FileInfo { + match id.index() { + None => &mut self.comp_file.1, + Some(index) => self + .files + .get_index_mut(index) + .map(|entry| entry.1) + .unwrap(), + } + } + + /// Begin a new sequence and set its base address. + /// + /// # Panics + /// + /// Panics if a sequence has already begun. + pub fn begin_sequence(&mut self, address: Option<Address>) { + assert!(!self.in_sequence); + self.in_sequence = true; + if let Some(address) = address { + self.instructions.push(LineInstruction::SetAddress(address)); + } + } + + /// End the sequence, and reset the row to its default values. + /// + /// Only the `address_offset` and op_index` fields of the current row are used. + /// + /// # Panics + /// + /// Panics if a sequence has not begun. + pub fn end_sequence(&mut self, address_offset: u64) { + assert!(self.in_sequence); + self.in_sequence = false; + self.row.address_offset = address_offset; + let op_advance = self.op_advance(); + if op_advance != 0 { + self.instructions + .push(LineInstruction::AdvancePc(op_advance)); + } + self.instructions.push(LineInstruction::EndSequence); + self.prev_row = LineRow::initial_state(self.line_encoding); + self.row = LineRow::initial_state(self.line_encoding); + } + + /// Return true if a sequence has begun. + #[inline] + pub fn in_sequence(&self) -> bool { + self.in_sequence + } + + /// Returns a reference to the data for the current row. + #[inline] + pub fn row(&mut self) -> &mut LineRow { + &mut self.row + } + + /// Generates the line number information instructions for the current row. + /// + /// After the instructions are generated, it sets `discriminator` to 0, and sets + /// `basic_block`, `prologue_end`, and `epilogue_begin` to false. + /// + /// # Panics + /// + /// Panics if a sequence has not begun. + /// Panics if the address_offset decreases. + pub fn generate_row(&mut self) { + assert!(self.in_sequence); + + // Output fields that are reset on every row. + if self.row.discriminator != 0 { + self.instructions + .push(LineInstruction::SetDiscriminator(self.row.discriminator)); + self.row.discriminator = 0; + } + if self.row.basic_block { + self.instructions.push(LineInstruction::SetBasicBlock); + self.row.basic_block = false; + } + if self.row.prologue_end { + self.instructions.push(LineInstruction::SetPrologueEnd); + self.row.prologue_end = false; + } + if self.row.epilogue_begin { + self.instructions.push(LineInstruction::SetEpilogueBegin); + self.row.epilogue_begin = false; + } + + // Output fields that are not reset on every row. + if self.row.is_statement != self.prev_row.is_statement { + self.instructions.push(LineInstruction::NegateStatement); + } + if self.row.file != self.prev_row.file { + self.instructions + .push(LineInstruction::SetFile(self.row.file)); + } + if self.row.column != self.prev_row.column { + self.instructions + .push(LineInstruction::SetColumn(self.row.column)); + } + if self.row.isa != self.prev_row.isa { + self.instructions + .push(LineInstruction::SetIsa(self.row.isa)); + } + + // Advance the line, address, and operation index. + let line_base = i64::from(self.line_encoding.line_base) as u64; + let line_range = u64::from(self.line_encoding.line_range); + let line_advance = self.row.line as i64 - self.prev_row.line as i64; + let op_advance = self.op_advance(); + + // Default to special advances of 0. + let special_base = u64::from(OPCODE_BASE); + // TODO: handle lack of special opcodes for 0 line advance + debug_assert!(self.line_encoding.line_base <= 0); + debug_assert!(self.line_encoding.line_base + self.line_encoding.line_range as i8 >= 0); + let special_default = special_base.wrapping_sub(line_base); + let mut special = special_default; + let mut use_special = false; + + if line_advance != 0 { + let special_line = (line_advance as u64).wrapping_sub(line_base); + if special_line < line_range { + special = special_base + special_line; + use_special = true; + } else { + self.instructions + .push(LineInstruction::AdvanceLine(line_advance)); + } + } + + if op_advance != 0 { + // Using ConstAddPc can save a byte. + let (special_op_advance, const_add_pc) = if special + op_advance * line_range <= 255 { + (op_advance, false) + } else { + let op_range = (255 - special_base) / line_range; + (op_advance - op_range, true) + }; + + let special_op = special_op_advance * line_range; + if special + special_op <= 255 { + special += special_op; + use_special = true; + if const_add_pc { + self.instructions.push(LineInstruction::ConstAddPc); + } + } else { + self.instructions + .push(LineInstruction::AdvancePc(op_advance)); + } + } + + if use_special && special != special_default { + debug_assert!(special >= special_base); + debug_assert!(special <= 255); + self.instructions + .push(LineInstruction::Special(special as u8)); + } else { + self.instructions.push(LineInstruction::Copy); + } + + self.prev_row = self.row; + } + + fn op_advance(&self) -> u64 { + debug_assert!(self.row.address_offset >= self.prev_row.address_offset); + let mut address_advance = self.row.address_offset - self.prev_row.address_offset; + if self.line_encoding.minimum_instruction_length != 1 { + debug_assert_eq!( + self.row.address_offset % u64::from(self.line_encoding.minimum_instruction_length), + 0 + ); + address_advance /= u64::from(self.line_encoding.minimum_instruction_length); + } + address_advance * u64::from(self.line_encoding.maximum_operations_per_instruction) + + self.row.op_index + - self.prev_row.op_index + } + + /// Returns true if the line number program has no instructions. + /// + /// Does not check the file or directory entries. + #[inline] + pub fn is_empty(&self) -> bool { + self.instructions.is_empty() + } + + /// Write the line number program to the given section. + /// + /// # Panics + /// + /// Panics if `self.is_none()`. + pub fn write<W: Writer>( + &self, + w: &mut DebugLine<W>, + encoding: Encoding, + debug_line_str_offsets: &DebugLineStrOffsets, + debug_str_offsets: &DebugStrOffsets, + ) -> Result<DebugLineOffset> { + assert!(!self.is_none()); + + if encoding.version < self.version() + || encoding.format != self.format() + || encoding.address_size != self.address_size() + { + return Err(Error::IncompatibleLineProgramEncoding); + } + + let offset = w.offset(); + + let length_offset = w.write_initial_length(self.format())?; + let length_base = w.len(); + + if self.version() < 2 || self.version() > 5 { + return Err(Error::UnsupportedVersion(self.version())); + } + w.write_u16(self.version())?; + + if self.version() >= 5 { + w.write_u8(self.address_size())?; + // Segment selector size. + w.write_u8(0)?; + } + + let header_length_offset = w.len(); + w.write_udata(0, self.format().word_size())?; + let header_length_base = w.len(); + + w.write_u8(self.line_encoding.minimum_instruction_length)?; + if self.version() >= 4 { + w.write_u8(self.line_encoding.maximum_operations_per_instruction)?; + } else if self.line_encoding.maximum_operations_per_instruction != 1 { + return Err(Error::NeedVersion(4)); + }; + w.write_u8(if self.line_encoding.default_is_stmt { + 1 + } else { + 0 + })?; + w.write_u8(self.line_encoding.line_base as u8)?; + w.write_u8(self.line_encoding.line_range)?; + w.write_u8(OPCODE_BASE)?; + w.write(&[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1])?; + + if self.version() <= 4 { + // The first directory is stored as DW_AT_comp_dir. + for dir in self.directories.iter().skip(1) { + dir.write( + w, + constants::DW_FORM_string, + self.encoding, + debug_line_str_offsets, + debug_str_offsets, + )?; + } + w.write_u8(0)?; + + for ((file, dir), info) in self.files.iter() { + file.write( + w, + constants::DW_FORM_string, + self.encoding, + debug_line_str_offsets, + debug_str_offsets, + )?; + w.write_uleb128(dir.0 as u64)?; + w.write_uleb128(info.timestamp)?; + w.write_uleb128(info.size)?; + } + w.write_u8(0)?; + } else { + // Directory entry formats (only ever 1). + w.write_u8(1)?; + w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?; + let dir_form = self.directories.get_index(0).unwrap().form(); + w.write_uleb128(dir_form.0.into())?; + + // Directory entries. + w.write_uleb128(self.directories.len() as u64)?; + for dir in self.directories.iter() { + dir.write( + w, + dir_form, + self.encoding, + debug_line_str_offsets, + debug_str_offsets, + )?; + } + + // File name entry formats. + let count = 2 + + if self.file_has_timestamp { 1 } else { 0 } + + if self.file_has_size { 1 } else { 0 } + + if self.file_has_md5 { 1 } else { 0 }; + w.write_u8(count)?; + w.write_uleb128(u64::from(constants::DW_LNCT_path.0))?; + let file_form = self.comp_file.0.form(); + w.write_uleb128(file_form.0.into())?; + w.write_uleb128(u64::from(constants::DW_LNCT_directory_index.0))?; + w.write_uleb128(constants::DW_FORM_udata.0.into())?; + if self.file_has_timestamp { + w.write_uleb128(u64::from(constants::DW_LNCT_timestamp.0))?; + w.write_uleb128(constants::DW_FORM_udata.0.into())?; + } + if self.file_has_size { + w.write_uleb128(u64::from(constants::DW_LNCT_size.0))?; + w.write_uleb128(constants::DW_FORM_udata.0.into())?; + } + if self.file_has_md5 { + w.write_uleb128(u64::from(constants::DW_LNCT_MD5.0))?; + w.write_uleb128(constants::DW_FORM_data16.0.into())?; + } + + // File name entries. + w.write_uleb128(self.files.len() as u64 + 1)?; + let mut write_file = |file: &LineString, dir: DirectoryId, info: &FileInfo| { + file.write( + w, + file_form, + self.encoding, + debug_line_str_offsets, + debug_str_offsets, + )?; + w.write_uleb128(dir.0 as u64)?; + if self.file_has_timestamp { + w.write_uleb128(info.timestamp)?; + } + if self.file_has_size { + w.write_uleb128(info.size)?; + } + if self.file_has_md5 { + w.write(&info.md5)?; + } + Ok(()) + }; + write_file(&self.comp_file.0, DirectoryId(0), &self.comp_file.1)?; + for ((file, dir), info) in self.files.iter() { + write_file(file, *dir, info)?; + } + } + + let header_length = (w.len() - header_length_base) as u64; + w.write_udata_at( + header_length_offset, + header_length, + self.format().word_size(), + )?; + + for instruction in &self.instructions { + instruction.write(w, self.address_size())?; + } + + let length = (w.len() - length_base) as u64; + w.write_initial_length_at(length_offset, length, self.format())?; + + Ok(offset) + } +} + +/// A row in the line number table that corresponds to a machine instruction. +#[derive(Debug, Clone, Copy)] +pub struct LineRow { + /// The offset of the instruction from the start address of the sequence. + pub address_offset: u64, + /// The index of an operation within a VLIW instruction. + /// + /// The index of the first operation is 0. + /// Set to 0 for non-VLIW instructions. + pub op_index: u64, + + /// The source file corresponding to the instruction. + pub file: FileId, + /// The line number within the source file. + /// + /// Lines are numbered beginning at 1. Set to 0 if there is no source line. + pub line: u64, + /// The column number within the source line. + /// + /// Columns are numbered beginning at 1. Set to 0 for the "left edge" of the line. + pub column: u64, + /// An additional discriminator used to distinguish between source locations. + /// This value is assigned arbitrarily by the DWARF producer. + pub discriminator: u64, + + /// Set to true if the instruction is a recommended breakpoint for a statement. + pub is_statement: bool, + /// Set to true if the instruction is the beginning of a basic block. + pub basic_block: bool, + /// Set to true if the instruction is a recommended breakpoint at the entry of a + /// function. + pub prologue_end: bool, + /// Set to true if the instruction is a recommended breakpoint prior to the exit of + /// a function. + pub epilogue_begin: bool, + + /// The instruction set architecture of the instruction. + /// + /// Set to 0 for the default ISA. Other values are defined by the architecture ABI. + pub isa: u64, +} + +impl LineRow { + /// Return the initial state as specified in the DWARF standard. + fn initial_state(line_encoding: LineEncoding) -> Self { + LineRow { + address_offset: 0, + op_index: 0, + + file: FileId::initial_state(), + line: 1, + column: 0, + discriminator: 0, + + is_statement: line_encoding.default_is_stmt, + basic_block: false, + prologue_end: false, + epilogue_begin: false, + + isa: 0, + } + } +} + +/// An instruction in a line number program. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum LineInstruction { + // Special opcodes + Special(u8), + + // Standard opcodes + Copy, + AdvancePc(u64), + AdvanceLine(i64), + SetFile(FileId), + SetColumn(u64), + NegateStatement, + SetBasicBlock, + ConstAddPc, + // DW_LNS_fixed_advance_pc is not supported. + SetPrologueEnd, + SetEpilogueBegin, + SetIsa(u64), + + // Extended opcodes + EndSequence, + // TODO: this doubles the size of this enum. + SetAddress(Address), + // DW_LNE_define_file is not supported. + SetDiscriminator(u64), +} + +impl LineInstruction { + /// Write the line number instruction to the given section. + fn write<W: Writer>(self, w: &mut DebugLine<W>, address_size: u8) -> Result<()> { + use self::LineInstruction::*; + match self { + Special(val) => w.write_u8(val)?, + Copy => w.write_u8(constants::DW_LNS_copy.0)?, + AdvancePc(val) => { + w.write_u8(constants::DW_LNS_advance_pc.0)?; + w.write_uleb128(val)?; + } + AdvanceLine(val) => { + w.write_u8(constants::DW_LNS_advance_line.0)?; + w.write_sleb128(val)?; + } + SetFile(val) => { + w.write_u8(constants::DW_LNS_set_file.0)?; + w.write_uleb128(val.raw())?; + } + SetColumn(val) => { + w.write_u8(constants::DW_LNS_set_column.0)?; + w.write_uleb128(val)?; + } + NegateStatement => w.write_u8(constants::DW_LNS_negate_stmt.0)?, + SetBasicBlock => w.write_u8(constants::DW_LNS_set_basic_block.0)?, + ConstAddPc => w.write_u8(constants::DW_LNS_const_add_pc.0)?, + SetPrologueEnd => w.write_u8(constants::DW_LNS_set_prologue_end.0)?, + SetEpilogueBegin => w.write_u8(constants::DW_LNS_set_epilogue_begin.0)?, + SetIsa(val) => { + w.write_u8(constants::DW_LNS_set_isa.0)?; + w.write_uleb128(val)?; + } + EndSequence => { + w.write_u8(0)?; + w.write_uleb128(1)?; + w.write_u8(constants::DW_LNE_end_sequence.0)?; + } + SetAddress(address) => { + w.write_u8(0)?; + w.write_uleb128(1 + u64::from(address_size))?; + w.write_u8(constants::DW_LNE_set_address.0)?; + w.write_address(address, address_size)?; + } + SetDiscriminator(val) => { + let mut bytes = [0u8; 10]; + // bytes is long enough so this will never fail. + let len = leb128::write::unsigned(&mut { &mut bytes[..] }, val).unwrap(); + w.write_u8(0)?; + w.write_uleb128(1 + len as u64)?; + w.write_u8(constants::DW_LNE_set_discriminator.0)?; + w.write(&bytes[..len])?; + } + } + Ok(()) + } +} + +/// A string value for use in defining paths in line number programs. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum LineString { + /// A slice of bytes representing a string. Must not include null bytes. + /// Not guaranteed to be UTF-8 or anything like that. + String(Vec<u8>), + + /// A reference to a string in the `.debug_str` section. + StringRef(StringId), + + /// A reference to a string in the `.debug_line_str` section. + LineStringRef(LineStringId), +} + +impl LineString { + /// Create a `LineString` using the normal form for the given encoding. + pub fn new<T>(val: T, encoding: Encoding, line_strings: &mut LineStringTable) -> Self + where + T: Into<Vec<u8>>, + { + let val = val.into(); + if encoding.version <= 4 { + LineString::String(val) + } else { + LineString::LineStringRef(line_strings.add(val)) + } + } + + fn form(&self) -> constants::DwForm { + match *self { + LineString::String(..) => constants::DW_FORM_string, + LineString::StringRef(..) => constants::DW_FORM_strp, + LineString::LineStringRef(..) => constants::DW_FORM_line_strp, + } + } + + fn write<W: Writer>( + &self, + w: &mut DebugLine<W>, + form: constants::DwForm, + encoding: Encoding, + debug_line_str_offsets: &DebugLineStrOffsets, + debug_str_offsets: &DebugStrOffsets, + ) -> Result<()> { + if form != self.form() { + return Err(Error::LineStringFormMismatch); + } + + match *self { + LineString::String(ref val) => { + if encoding.version <= 4 { + debug_assert!(!val.is_empty()); + } + w.write(val)?; + w.write_u8(0)?; + } + LineString::StringRef(val) => { + if encoding.version < 5 { + return Err(Error::NeedVersion(5)); + } + w.write_offset( + debug_str_offsets.get(val).0, + SectionId::DebugStr, + encoding.format.word_size(), + )?; + } + LineString::LineStringRef(val) => { + if encoding.version < 5 { + return Err(Error::NeedVersion(5)); + } + w.write_offset( + debug_line_str_offsets.get(val).0, + SectionId::DebugLineStr, + encoding.format.word_size(), + )?; + } + } + Ok(()) + } +} + +/// An identifier for a directory in a `LineProgram`. +/// +/// Defaults to the working directory of the compilation unit. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct DirectoryId(usize); + +// Force FileId access via the methods. +mod id { + /// An identifier for a file in a `LineProgram`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct FileId(usize); + + impl FileId { + /// Create a FileId given an index into `LineProgram::files`. + pub(crate) fn new(index: usize) -> Self { + FileId(index + 1) + } + + /// The index of the file in `LineProgram::files`. + pub(super) fn index(self) -> Option<usize> { + if self.0 == 0 { + None + } else { + Some(self.0 - 1) + } + } + + /// The initial state of the file register. + pub(super) fn initial_state() -> Self { + FileId(1) + } + + /// The raw value used when writing. + pub(crate) fn raw(self) -> u64 { + self.0 as u64 + } + + /// The id for file index 0 in DWARF version 5. + /// Only used when converting. + // Used for tests only. + #[allow(unused)] + pub(super) fn zero() -> Self { + FileId(0) + } + } +} +pub use self::id::*; + +/// Extra information for file in a `LineProgram`. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub struct FileInfo { + /// The implementation defined timestamp of the last modification of the file, + /// or 0 if not available. + pub timestamp: u64, + + /// The size of the file in bytes, or 0 if not available. + pub size: u64, + + /// A 16-byte MD5 digest of the file contents. + /// + /// Only used if version >= 5 and `LineProgram::file_has_md5` is `true`. + pub md5: [u8; 16], +} + +define_section!( + DebugLine, + DebugLineOffset, + "A writable `.debug_line` section." +); + +#[cfg(feature = "read")] +mod convert { + use super::*; + use crate::read::{self, Reader}; + use crate::write::{self, ConvertError, ConvertResult}; + + impl LineProgram { + /// Create a line number program by reading the data from the given program. + /// + /// Return the program and a mapping from file index to `FileId`. + pub fn from<R: Reader<Offset = usize>>( + mut from_program: read::IncompleteLineProgram<R>, + dwarf: &read::Dwarf<R>, + line_strings: &mut write::LineStringTable, + strings: &mut write::StringTable, + convert_address: &dyn Fn(u64) -> Option<Address>, + ) -> ConvertResult<(LineProgram, Vec<FileId>)> { + // Create mappings in case the source has duplicate files or directories. + let mut dirs = Vec::new(); + let mut files = Vec::new(); + + let mut program = { + let from_header = from_program.header(); + let encoding = from_header.encoding(); + + let comp_dir = match from_header.directory(0) { + Some(comp_dir) => LineString::from(comp_dir, dwarf, line_strings, strings)?, + None => LineString::new(&[][..], encoding, line_strings), + }; + + let (comp_name, comp_file_info) = match from_header.file(0) { + Some(comp_file) => { + if comp_file.directory_index() != 0 { + return Err(ConvertError::InvalidDirectoryIndex); + } + ( + LineString::from(comp_file.path_name(), dwarf, line_strings, strings)?, + Some(FileInfo { + timestamp: comp_file.timestamp(), + size: comp_file.size(), + md5: *comp_file.md5(), + }), + ) + } + None => (LineString::new(&[][..], encoding, line_strings), None), + }; + + if from_header.line_base() > 0 { + return Err(ConvertError::InvalidLineBase); + } + let mut program = LineProgram::new( + encoding, + from_header.line_encoding(), + comp_dir, + comp_name, + comp_file_info, + ); + + let file_skip; + if from_header.version() <= 4 { + // The first directory is implicit. + dirs.push(DirectoryId(0)); + // A file index of 0 is invalid for version <= 4, but putting + // something there makes the indexing easier. + file_skip = 0; + files.push(FileId::zero()); + } else { + // We don't add the first file to `files`, but still allow + // it to be referenced from converted instructions. + file_skip = 1; + files.push(FileId::zero()); + } + + for from_dir in from_header.include_directories() { + let from_dir = + LineString::from(from_dir.clone(), dwarf, line_strings, strings)?; + dirs.push(program.add_directory(from_dir)); + } + + program.file_has_timestamp = from_header.file_has_timestamp(); + program.file_has_size = from_header.file_has_size(); + program.file_has_md5 = from_header.file_has_md5(); + for from_file in from_header.file_names().iter().skip(file_skip) { + let from_name = + LineString::from(from_file.path_name(), dwarf, line_strings, strings)?; + let from_dir = from_file.directory_index(); + if from_dir >= dirs.len() as u64 { + return Err(ConvertError::InvalidDirectoryIndex); + } + let from_dir = dirs[from_dir as usize]; + let from_info = Some(FileInfo { + timestamp: from_file.timestamp(), + size: from_file.size(), + md5: *from_file.md5(), + }); + files.push(program.add_file(from_name, from_dir, from_info)); + } + + program + }; + + // We can't use the `from_program.rows()` because that wouldn't let + // us preserve address relocations. + let mut from_row = read::LineRow::new(from_program.header()); + let mut instructions = from_program.header().instructions(); + let mut address = None; + while let Some(instruction) = instructions.next_instruction(from_program.header())? { + match instruction { + read::LineInstruction::SetAddress(val) => { + if program.in_sequence() { + return Err(ConvertError::UnsupportedLineInstruction); + } + match convert_address(val) { + Some(val) => address = Some(val), + None => return Err(ConvertError::InvalidAddress), + } + from_row.execute(read::LineInstruction::SetAddress(0), &mut from_program); + } + read::LineInstruction::DefineFile(_) => { + return Err(ConvertError::UnsupportedLineInstruction); + } + _ => { + if from_row.execute(instruction, &mut from_program) { + if !program.in_sequence() { + program.begin_sequence(address); + address = None; + } + if from_row.end_sequence() { + program.end_sequence(from_row.address()); + } else { + program.row().address_offset = from_row.address(); + program.row().op_index = from_row.op_index(); + program.row().file = { + let file = from_row.file_index(); + if file >= files.len() as u64 { + return Err(ConvertError::InvalidFileIndex); + } + if file == 0 && program.version() <= 4 { + return Err(ConvertError::InvalidFileIndex); + } + files[file as usize] + }; + program.row().line = match from_row.line() { + Some(line) => line.get(), + None => 0, + }; + program.row().column = match from_row.column() { + read::ColumnType::LeftEdge => 0, + read::ColumnType::Column(val) => val.get(), + }; + program.row().discriminator = from_row.discriminator(); + program.row().is_statement = from_row.is_stmt(); + program.row().basic_block = from_row.basic_block(); + program.row().prologue_end = from_row.prologue_end(); + program.row().epilogue_begin = from_row.epilogue_begin(); + program.row().isa = from_row.isa(); + program.generate_row(); + } + from_row.reset(from_program.header()); + } + } + }; + } + Ok((program, files)) + } + } + + impl LineString { + fn from<R: Reader<Offset = usize>>( + from_attr: read::AttributeValue<R>, + dwarf: &read::Dwarf<R>, + line_strings: &mut write::LineStringTable, + strings: &mut write::StringTable, + ) -> ConvertResult<LineString> { + Ok(match from_attr { + read::AttributeValue::String(r) => LineString::String(r.to_slice()?.to_vec()), + read::AttributeValue::DebugStrRef(offset) => { + let r = dwarf.debug_str.get_str(offset)?; + let id = strings.add(r.to_slice()?); + LineString::StringRef(id) + } + read::AttributeValue::DebugLineStrRef(offset) => { + let r = dwarf.debug_line_str.get_str(offset)?; + let id = line_strings.add(r.to_slice()?); + LineString::LineStringRef(id) + } + _ => return Err(ConvertError::UnsupportedLineStringForm), + }) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::read; + use crate::write::{DebugLineStr, DebugStr, EndianVec, StringTable}; + use crate::LittleEndian; + + #[test] + fn test_line_program_table() { + let dir1 = LineString::String(b"dir1".to_vec()); + let file1 = LineString::String(b"file1".to_vec()); + let dir2 = LineString::String(b"dir2".to_vec()); + let file2 = LineString::String(b"file2".to_vec()); + + let mut programs = Vec::new(); + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let mut program = LineProgram::new( + encoding, + LineEncoding::default(), + dir1.clone(), + file1.clone(), + None, + ); + + { + assert_eq!(&dir1, program.get_directory(program.default_directory())); + program.file_has_timestamp = true; + program.file_has_size = true; + if encoding.version >= 5 { + program.file_has_md5 = true; + } + + let dir_id = program.add_directory(dir2.clone()); + assert_eq!(&dir2, program.get_directory(dir_id)); + assert_eq!(dir_id, program.add_directory(dir2.clone())); + + let file_info = FileInfo { + timestamp: 1, + size: 2, + md5: if encoding.version >= 5 { + [3; 16] + } else { + [0; 16] + }, + }; + let file_id = program.add_file(file2.clone(), dir_id, Some(file_info)); + assert_eq!((&file2, dir_id), program.get_file(file_id)); + assert_eq!(file_info, *program.get_file_info(file_id)); + + program.get_file_info_mut(file_id).size = 3; + assert_ne!(file_info, *program.get_file_info(file_id)); + assert_eq!(file_id, program.add_file(file2.clone(), dir_id, None)); + assert_ne!(file_info, *program.get_file_info(file_id)); + assert_eq!( + file_id, + program.add_file(file2.clone(), dir_id, Some(file_info)) + ); + assert_eq!(file_info, *program.get_file_info(file_id)); + + programs.push((program, file_id, encoding)); + } + } + } + } + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let mut debug_line_offsets = Vec::new(); + for (program, _, encoding) in &programs { + debug_line_offsets.push( + program + .write( + &mut debug_line, + *encoding, + &debug_line_str_offsets, + &debug_str_offsets, + ) + .unwrap(), + ); + } + + let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); + + let convert_address = &|address| Some(Address::Constant(address)); + for ((program, file_id, encoding), offset) in programs.iter().zip(debug_line_offsets.iter()) + { + let read_program = read_debug_line + .program( + *offset, + encoding.address_size, + Some(read::EndianSlice::new(b"dir1", LittleEndian)), + Some(read::EndianSlice::new(b"file1", LittleEndian)), + ) + .unwrap(); + + let dwarf = read::Dwarf::default(); + let mut convert_line_strings = LineStringTable::default(); + let mut convert_strings = StringTable::default(); + let (convert_program, convert_files) = LineProgram::from( + read_program, + &dwarf, + &mut convert_line_strings, + &mut convert_strings, + convert_address, + ) + .unwrap(); + assert_eq!(convert_program.version(), program.version()); + assert_eq!(convert_program.address_size(), program.address_size()); + assert_eq!(convert_program.format(), program.format()); + + let convert_file_id = convert_files[file_id.raw() as usize]; + let (file, dir) = program.get_file(*file_id); + let (convert_file, convert_dir) = convert_program.get_file(convert_file_id); + assert_eq!(file, convert_file); + assert_eq!( + program.get_directory(dir), + convert_program.get_directory(convert_dir) + ); + assert_eq!( + program.get_file_info(*file_id), + convert_program.get_file_info(convert_file_id) + ); + } + } + + #[test] + fn test_line_row() { + let dir1 = &b"dir1"[..]; + let file1 = &b"file1"[..]; + let file2 = &b"file2"[..]; + let convert_address = &|address| Some(Address::Constant(address)); + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let line_base = -5; + let line_range = 14; + let neg_line_base = (-line_base) as u8; + let mut program = LineProgram::new( + encoding, + LineEncoding { + line_base, + line_range, + ..Default::default() + }, + LineString::String(dir1.to_vec()), + LineString::String(file1.to_vec()), + None, + ); + let dir_id = program.default_directory(); + program.add_file(LineString::String(file1.to_vec()), dir_id, None); + let file_id = + program.add_file(LineString::String(file2.to_vec()), dir_id, None); + + // Test sequences. + { + let mut program = program.clone(); + let address = Address::Constant(0x12); + program.begin_sequence(Some(address)); + assert_eq!( + program.instructions, + vec![LineInstruction::SetAddress(address)] + ); + } + + { + let mut program = program.clone(); + program.begin_sequence(None); + assert_eq!(program.instructions, Vec::new()); + } + + { + let mut program = program.clone(); + program.begin_sequence(None); + program.end_sequence(0x1234); + assert_eq!( + program.instructions, + vec![ + LineInstruction::AdvancePc(0x1234), + LineInstruction::EndSequence + ] + ); + } + + // Create a base program. + program.begin_sequence(None); + program.row.line = 0x1000; + program.generate_row(); + let base_row = program.row; + let base_instructions = program.instructions.clone(); + + // Create test cases. + let mut tests = Vec::new(); + + let row = base_row; + tests.push((row, vec![LineInstruction::Copy])); + + let mut row = base_row; + row.line -= u64::from(neg_line_base); + tests.push((row, vec![LineInstruction::Special(OPCODE_BASE)])); + + let mut row = base_row; + row.line += u64::from(line_range) - 1; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::Special(OPCODE_BASE + line_range - 1)], + )); + + let mut row = base_row; + row.line += u64::from(line_range); + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![ + LineInstruction::AdvanceLine(i64::from(line_range - neg_line_base)), + LineInstruction::Copy, + ], + )); + + let mut row = base_row; + row.address_offset = 1; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::Special(OPCODE_BASE + line_range)], + )); + + let op_range = (255 - OPCODE_BASE) / line_range; + let mut row = base_row; + row.address_offset = u64::from(op_range); + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::Special( + OPCODE_BASE + op_range * line_range, + )], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range); + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range); + row.line -= u64::from(neg_line_base); + tests.push((row, vec![LineInstruction::Special(255)])); + + let mut row = base_row; + row.address_offset = u64::from(op_range); + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::ConstAddPc, LineInstruction::Copy], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range); + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![ + LineInstruction::ConstAddPc, + LineInstruction::Special(OPCODE_BASE + 6), + ], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range) * 2; + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range); + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![LineInstruction::ConstAddPc, LineInstruction::Special(255)], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range) * 2; + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 1; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![ + LineInstruction::AdvancePc(row.address_offset), + LineInstruction::Copy, + ], + )); + + let mut row = base_row; + row.address_offset = u64::from(op_range) * 2; + row.line += u64::from(255 - OPCODE_BASE - op_range * line_range) + 2; + row.line -= u64::from(neg_line_base); + tests.push(( + row, + vec![ + LineInstruction::AdvancePc(row.address_offset), + LineInstruction::Special(OPCODE_BASE + 6), + ], + )); + + let mut row = base_row; + row.address_offset = 0x1234; + tests.push(( + row, + vec![LineInstruction::AdvancePc(0x1234), LineInstruction::Copy], + )); + + let mut row = base_row; + row.line += 0x1234; + tests.push(( + row, + vec![LineInstruction::AdvanceLine(0x1234), LineInstruction::Copy], + )); + + let mut row = base_row; + row.file = file_id; + tests.push(( + row, + vec![LineInstruction::SetFile(file_id), LineInstruction::Copy], + )); + + let mut row = base_row; + row.column = 0x1234; + tests.push(( + row, + vec![LineInstruction::SetColumn(0x1234), LineInstruction::Copy], + )); + + let mut row = base_row; + row.discriminator = 0x1234; + tests.push(( + row, + vec![ + LineInstruction::SetDiscriminator(0x1234), + LineInstruction::Copy, + ], + )); + + let mut row = base_row; + row.is_statement = !row.is_statement; + tests.push(( + row, + vec![LineInstruction::NegateStatement, LineInstruction::Copy], + )); + + let mut row = base_row; + row.basic_block = true; + tests.push(( + row, + vec![LineInstruction::SetBasicBlock, LineInstruction::Copy], + )); + + let mut row = base_row; + row.prologue_end = true; + tests.push(( + row, + vec![LineInstruction::SetPrologueEnd, LineInstruction::Copy], + )); + + let mut row = base_row; + row.epilogue_begin = true; + tests.push(( + row, + vec![LineInstruction::SetEpilogueBegin, LineInstruction::Copy], + )); + + let mut row = base_row; + row.isa = 0x1234; + tests.push(( + row, + vec![LineInstruction::SetIsa(0x1234), LineInstruction::Copy], + )); + + for test in tests { + // Test generate_row(). + let mut program = program.clone(); + program.row = test.0; + program.generate_row(); + assert_eq!( + &program.instructions[base_instructions.len()..], + &test.1[..] + ); + + // Test LineProgram::from(). + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let debug_line_offset = program + .write( + &mut debug_line, + encoding, + &debug_line_str_offsets, + &debug_str_offsets, + ) + .unwrap(); + + let read_debug_line = + read::DebugLine::new(debug_line.slice(), LittleEndian); + let read_program = read_debug_line + .program( + debug_line_offset, + address_size, + Some(read::EndianSlice::new(dir1, LittleEndian)), + Some(read::EndianSlice::new(file1, LittleEndian)), + ) + .unwrap(); + + let dwarf = read::Dwarf::default(); + let mut convert_line_strings = LineStringTable::default(); + let mut convert_strings = StringTable::default(); + let (convert_program, _convert_files) = LineProgram::from( + read_program, + &dwarf, + &mut convert_line_strings, + &mut convert_strings, + convert_address, + ) + .unwrap(); + assert_eq!( + &convert_program.instructions[base_instructions.len()..], + &test.1[..] + ); + } + } + } + } + } + + #[test] + fn test_line_instruction() { + let dir1 = &b"dir1"[..]; + let file1 = &b"file1"[..]; + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let mut program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(dir1.to_vec()), + LineString::String(file1.to_vec()), + None, + ); + let dir_id = program.default_directory(); + let file_id = + program.add_file(LineString::String(file1.to_vec()), dir_id, None); + + for &(ref inst, ref expect_inst) in &[ + ( + LineInstruction::Special(OPCODE_BASE), + read::LineInstruction::Special(OPCODE_BASE), + ), + ( + LineInstruction::Special(255), + read::LineInstruction::Special(255), + ), + (LineInstruction::Copy, read::LineInstruction::Copy), + ( + LineInstruction::AdvancePc(0x12), + read::LineInstruction::AdvancePc(0x12), + ), + ( + LineInstruction::AdvanceLine(0x12), + read::LineInstruction::AdvanceLine(0x12), + ), + ( + LineInstruction::SetFile(file_id), + read::LineInstruction::SetFile(file_id.raw()), + ), + ( + LineInstruction::SetColumn(0x12), + read::LineInstruction::SetColumn(0x12), + ), + ( + LineInstruction::NegateStatement, + read::LineInstruction::NegateStatement, + ), + ( + LineInstruction::SetBasicBlock, + read::LineInstruction::SetBasicBlock, + ), + ( + LineInstruction::ConstAddPc, + read::LineInstruction::ConstAddPc, + ), + ( + LineInstruction::SetPrologueEnd, + read::LineInstruction::SetPrologueEnd, + ), + ( + LineInstruction::SetEpilogueBegin, + read::LineInstruction::SetEpilogueBegin, + ), + ( + LineInstruction::SetIsa(0x12), + read::LineInstruction::SetIsa(0x12), + ), + ( + LineInstruction::EndSequence, + read::LineInstruction::EndSequence, + ), + ( + LineInstruction::SetAddress(Address::Constant(0x12)), + read::LineInstruction::SetAddress(0x12), + ), + ( + LineInstruction::SetDiscriminator(0x12), + read::LineInstruction::SetDiscriminator(0x12), + ), + ][..] + { + let mut program = program.clone(); + program.instructions.push(*inst); + + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let debug_line_offset = program + .write( + &mut debug_line, + encoding, + &debug_line_str_offsets, + &debug_str_offsets, + ) + .unwrap(); + + let read_debug_line = + read::DebugLine::new(debug_line.slice(), LittleEndian); + let read_program = read_debug_line + .program( + debug_line_offset, + address_size, + Some(read::EndianSlice::new(dir1, LittleEndian)), + Some(read::EndianSlice::new(file1, LittleEndian)), + ) + .unwrap(); + let read_header = read_program.header(); + let mut read_insts = read_header.instructions(); + assert_eq!( + *expect_inst, + read_insts.next_instruction(read_header).unwrap().unwrap() + ); + assert_eq!(None, read_insts.next_instruction(read_header).unwrap()); + } + } + } + } + } + + // Test that the address/line advance is correct. We don't test for optimality. + #[test] + #[allow(clippy::useless_vec)] + fn test_advance() { + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 8, + }; + + let dir1 = &b"dir1"[..]; + let file1 = &b"file1"[..]; + + let addresses = 0..50; + let lines = -10..25i64; + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + + for minimum_instruction_length in vec![1, 4] { + for maximum_operations_per_instruction in vec![1, 3] { + for line_base in vec![-5, 0] { + for line_range in vec![10, 20] { + let line_encoding = LineEncoding { + minimum_instruction_length, + maximum_operations_per_instruction, + line_base, + line_range, + default_is_stmt: true, + }; + let mut program = LineProgram::new( + encoding, + line_encoding, + LineString::String(dir1.to_vec()), + LineString::String(file1.to_vec()), + None, + ); + for address_advance in addresses.clone() { + program.begin_sequence(Some(Address::Constant(0x1000))); + program.row().line = 0x10000; + program.generate_row(); + for line_advance in lines.clone() { + { + let row = program.row(); + row.address_offset += + address_advance * u64::from(minimum_instruction_length); + row.line = row.line.wrapping_add(line_advance as u64); + } + program.generate_row(); + } + let address_offset = program.row().address_offset + + u64::from(minimum_instruction_length); + program.end_sequence(address_offset); + } + + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let debug_line_offset = program + .write( + &mut debug_line, + encoding, + &debug_line_str_offsets, + &debug_str_offsets, + ) + .unwrap(); + + let read_debug_line = + read::DebugLine::new(debug_line.slice(), LittleEndian); + let read_program = read_debug_line + .program( + debug_line_offset, + 8, + Some(read::EndianSlice::new(dir1, LittleEndian)), + Some(read::EndianSlice::new(file1, LittleEndian)), + ) + .unwrap(); + + let mut rows = read_program.rows(); + for address_advance in addresses.clone() { + let mut address; + let mut line; + { + let row = rows.next_row().unwrap().unwrap().1; + address = row.address(); + line = row.line().unwrap().get(); + } + assert_eq!(address, 0x1000); + assert_eq!(line, 0x10000); + for line_advance in lines.clone() { + let row = rows.next_row().unwrap().unwrap().1; + assert_eq!( + row.address() - address, + address_advance * u64::from(minimum_instruction_length) + ); + assert_eq!( + (row.line().unwrap().get() as i64) - (line as i64), + line_advance + ); + address = row.address(); + line = row.line().unwrap().get(); + } + let row = rows.next_row().unwrap().unwrap().1; + assert!(row.end_sequence()); + } + } + } + } + } + } + + #[test] + fn test_line_string() { + let version = 5; + + let file = b"file1"; + + let mut strings = StringTable::default(); + let string_id = strings.add("file2"); + let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian)); + let debug_str_offsets = strings.write(&mut debug_str).unwrap(); + + let mut line_strings = LineStringTable::default(); + let line_string_id = line_strings.add("file3"); + let mut debug_line_str = DebugLineStr::from(EndianVec::new(LittleEndian)); + let debug_line_str_offsets = line_strings.write(&mut debug_line_str).unwrap(); + + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + for (file, expect_file) in vec![ + ( + LineString::String(file.to_vec()), + read::AttributeValue::String(read::EndianSlice::new(file, LittleEndian)), + ), + ( + LineString::StringRef(string_id), + read::AttributeValue::DebugStrRef(debug_str_offsets.get(string_id)), + ), + ( + LineString::LineStringRef(line_string_id), + read::AttributeValue::DebugLineStrRef( + debug_line_str_offsets.get(line_string_id), + ), + ), + ] { + let program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(b"dir".to_vec()), + file, + None, + ); + + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let debug_line_offset = program + .write( + &mut debug_line, + encoding, + &debug_line_str_offsets, + &debug_str_offsets, + ) + .unwrap(); + + let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); + let read_program = read_debug_line + .program(debug_line_offset, address_size, None, None) + .unwrap(); + let read_header = read_program.header(); + assert_eq!(read_header.file(0).unwrap().path_name(), expect_file); + } + } + } + } + + #[test] + fn test_missing_comp_dir() { + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + let program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(Vec::new()), + LineString::String(Vec::new()), + None, + ); + + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let debug_line_offset = program + .write( + &mut debug_line, + encoding, + &debug_line_str_offsets, + &debug_str_offsets, + ) + .unwrap(); + + let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); + let read_program = read_debug_line + .program( + debug_line_offset, + address_size, + // Testing missing comp_dir/comp_name. + None, + None, + ) + .unwrap(); + + let dwarf = read::Dwarf::default(); + let mut convert_line_strings = LineStringTable::default(); + let mut convert_strings = StringTable::default(); + let convert_address = &|address| Some(Address::Constant(address)); + LineProgram::from( + read_program, + &dwarf, + &mut convert_line_strings, + &mut convert_strings, + convert_address, + ) + .unwrap(); + } + } + } + } +} diff --git a/vendor/gimli/src/write/loc.rs b/vendor/gimli/src/write/loc.rs new file mode 100644 index 000000000..ea0ecb1cf --- /dev/null +++ b/vendor/gimli/src/write/loc.rs @@ -0,0 +1,549 @@ +use alloc::vec::Vec; +use indexmap::IndexSet; +use std::ops::{Deref, DerefMut}; + +use crate::common::{Encoding, LocationListsOffset, SectionId}; +use crate::write::{ + Address, BaseId, DebugInfoReference, Error, Expression, Result, Section, Sections, UnitOffsets, + Writer, +}; + +define_section!( + DebugLoc, + LocationListsOffset, + "A writable `.debug_loc` section." +); +define_section!( + DebugLocLists, + LocationListsOffset, + "A writable `.debug_loclists` section." +); + +define_offsets!( + LocationListOffsets: LocationListId => LocationListsOffset, + "The section offsets of a series of location lists within the `.debug_loc` or `.debug_loclists` sections." +); + +define_id!( + LocationListId, + "An identifier for a location list in a `LocationListTable`." +); + +/// A table of location lists that will be stored in a `.debug_loc` or `.debug_loclists` section. +#[derive(Debug, Default)] +pub struct LocationListTable { + base_id: BaseId, + locations: IndexSet<LocationList>, +} + +impl LocationListTable { + /// Add a location list to the table. + pub fn add(&mut self, loc_list: LocationList) -> LocationListId { + let (index, _) = self.locations.insert_full(loc_list); + LocationListId::new(self.base_id, index) + } + + /// Write the location list table to the appropriate section for the given DWARF version. + pub(crate) fn write<W: Writer>( + &self, + sections: &mut Sections<W>, + encoding: Encoding, + unit_offsets: Option<&UnitOffsets>, + ) -> Result<LocationListOffsets> { + if self.locations.is_empty() { + return Ok(LocationListOffsets::none()); + } + + match encoding.version { + 2..=4 => self.write_loc( + &mut sections.debug_loc, + &mut sections.debug_loc_refs, + encoding, + unit_offsets, + ), + 5 => self.write_loclists( + &mut sections.debug_loclists, + &mut sections.debug_loclists_refs, + encoding, + unit_offsets, + ), + _ => Err(Error::UnsupportedVersion(encoding.version)), + } + } + + /// Write the location list table to the `.debug_loc` section. + fn write_loc<W: Writer>( + &self, + w: &mut DebugLoc<W>, + refs: &mut Vec<DebugInfoReference>, + encoding: Encoding, + unit_offsets: Option<&UnitOffsets>, + ) -> Result<LocationListOffsets> { + let address_size = encoding.address_size; + let mut offsets = Vec::new(); + for loc_list in self.locations.iter() { + offsets.push(w.offset()); + for loc in &loc_list.0 { + // Note that we must ensure none of the ranges have both begin == 0 and end == 0. + // We do this by ensuring that begin != end, which is a bit more restrictive + // than required, but still seems reasonable. + match *loc { + Location::BaseAddress { address } => { + let marker = !0 >> (64 - address_size * 8); + w.write_udata(marker, address_size)?; + w.write_address(address, address_size)?; + } + Location::OffsetPair { + begin, + end, + ref data, + } => { + if begin == end { + return Err(Error::InvalidRange); + } + w.write_udata(begin, address_size)?; + w.write_udata(end, address_size)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + Location::StartEnd { + begin, + end, + ref data, + } => { + if begin == end { + return Err(Error::InvalidRange); + } + w.write_address(begin, address_size)?; + w.write_address(end, address_size)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + Location::StartLength { + begin, + length, + ref data, + } => { + let end = match begin { + Address::Constant(begin) => Address::Constant(begin + length), + Address::Symbol { symbol, addend } => Address::Symbol { + symbol, + addend: addend + length as i64, + }, + }; + if begin == end { + return Err(Error::InvalidRange); + } + w.write_address(begin, address_size)?; + w.write_address(end, address_size)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + Location::DefaultLocation { .. } => { + return Err(Error::InvalidRange); + } + } + } + w.write_udata(0, address_size)?; + w.write_udata(0, address_size)?; + } + Ok(LocationListOffsets { + base_id: self.base_id, + offsets, + }) + } + + /// Write the location list table to the `.debug_loclists` section. + fn write_loclists<W: Writer>( + &self, + w: &mut DebugLocLists<W>, + refs: &mut Vec<DebugInfoReference>, + encoding: Encoding, + unit_offsets: Option<&UnitOffsets>, + ) -> Result<LocationListOffsets> { + let mut offsets = Vec::new(); + + if encoding.version != 5 { + return Err(Error::NeedVersion(5)); + } + + let length_offset = w.write_initial_length(encoding.format)?; + let length_base = w.len(); + + w.write_u16(encoding.version)?; + w.write_u8(encoding.address_size)?; + w.write_u8(0)?; // segment_selector_size + w.write_u32(0)?; // offset_entry_count (when set to zero DW_FORM_rnglistx can't be used, see section 7.28) + // FIXME implement DW_FORM_rnglistx writing and implement the offset entry list + + for loc_list in self.locations.iter() { + offsets.push(w.offset()); + for loc in &loc_list.0 { + match *loc { + Location::BaseAddress { address } => { + w.write_u8(crate::constants::DW_LLE_base_address.0)?; + w.write_address(address, encoding.address_size)?; + } + Location::OffsetPair { + begin, + end, + ref data, + } => { + w.write_u8(crate::constants::DW_LLE_offset_pair.0)?; + w.write_uleb128(begin)?; + w.write_uleb128(end)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + Location::StartEnd { + begin, + end, + ref data, + } => { + w.write_u8(crate::constants::DW_LLE_start_end.0)?; + w.write_address(begin, encoding.address_size)?; + w.write_address(end, encoding.address_size)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + Location::StartLength { + begin, + length, + ref data, + } => { + w.write_u8(crate::constants::DW_LLE_start_length.0)?; + w.write_address(begin, encoding.address_size)?; + w.write_uleb128(length)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + Location::DefaultLocation { ref data } => { + w.write_u8(crate::constants::DW_LLE_default_location.0)?; + write_expression(&mut w.0, refs, encoding, unit_offsets, data)?; + } + } + } + + w.write_u8(crate::constants::DW_LLE_end_of_list.0)?; + } + + let length = (w.len() - length_base) as u64; + w.write_initial_length_at(length_offset, length, encoding.format)?; + + Ok(LocationListOffsets { + base_id: self.base_id, + offsets, + }) + } +} + +/// A locations list that will be stored in a `.debug_loc` or `.debug_loclists` section. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct LocationList(pub Vec<Location>); + +/// A single location. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum Location { + /// DW_LLE_base_address + BaseAddress { + /// Base address. + address: Address, + }, + /// DW_LLE_offset_pair + OffsetPair { + /// Start of range relative to base address. + begin: u64, + /// End of range relative to base address. + end: u64, + /// Location description. + data: Expression, + }, + /// DW_LLE_start_end + StartEnd { + /// Start of range. + begin: Address, + /// End of range. + end: Address, + /// Location description. + data: Expression, + }, + /// DW_LLE_start_length + StartLength { + /// Start of range. + begin: Address, + /// Length of range. + length: u64, + /// Location description. + data: Expression, + }, + /// DW_LLE_default_location + DefaultLocation { + /// Location description. + data: Expression, + }, +} + +fn write_expression<W: Writer>( + w: &mut W, + refs: &mut Vec<DebugInfoReference>, + encoding: Encoding, + unit_offsets: Option<&UnitOffsets>, + val: &Expression, +) -> Result<()> { + let size = val.size(encoding, unit_offsets) as u64; + if encoding.version <= 4 { + w.write_udata(size, 2)?; + } else { + w.write_uleb128(size)?; + } + val.write(w, Some(refs), encoding, unit_offsets)?; + Ok(()) +} + +#[cfg(feature = "read")] +mod convert { + use super::*; + + use crate::read::{self, Reader}; + use crate::write::{ConvertError, ConvertResult, ConvertUnitContext}; + + impl LocationList { + /// Create a location list by reading the data from the give location list iter. + pub(crate) fn from<R: Reader<Offset = usize>>( + mut from: read::RawLocListIter<R>, + context: &ConvertUnitContext<R>, + ) -> ConvertResult<Self> { + let mut have_base_address = context.base_address != Address::Constant(0); + let convert_address = + |x| (context.convert_address)(x).ok_or(ConvertError::InvalidAddress); + let convert_expression = |x| { + Expression::from( + x, + context.unit.encoding(), + Some(context.dwarf), + Some(context.unit), + Some(context.entry_ids), + context.convert_address, + ) + }; + let mut loc_list = Vec::new(); + while let Some(from_loc) = from.next()? { + let loc = match from_loc { + read::RawLocListEntry::AddressOrOffsetPair { begin, end, data } => { + // These were parsed as addresses, even if they are offsets. + let begin = convert_address(begin)?; + let end = convert_address(end)?; + let data = convert_expression(data)?; + match (begin, end) { + (Address::Constant(begin_offset), Address::Constant(end_offset)) => { + if have_base_address { + Location::OffsetPair { + begin: begin_offset, + end: end_offset, + data, + } + } else { + Location::StartEnd { begin, end, data } + } + } + _ => { + if have_base_address { + // At least one of begin/end is an address, but we also have + // a base address. Adding addresses is undefined. + return Err(ConvertError::InvalidRangeRelativeAddress); + } + Location::StartEnd { begin, end, data } + } + } + } + read::RawLocListEntry::BaseAddress { addr } => { + have_base_address = true; + let address = convert_address(addr)?; + Location::BaseAddress { address } + } + read::RawLocListEntry::BaseAddressx { addr } => { + have_base_address = true; + let address = convert_address(context.dwarf.address(context.unit, addr)?)?; + Location::BaseAddress { address } + } + read::RawLocListEntry::StartxEndx { begin, end, data } => { + let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; + let end = convert_address(context.dwarf.address(context.unit, end)?)?; + let data = convert_expression(data)?; + Location::StartEnd { begin, end, data } + } + read::RawLocListEntry::StartxLength { + begin, + length, + data, + } => { + let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; + let data = convert_expression(data)?; + Location::StartLength { + begin, + length, + data, + } + } + read::RawLocListEntry::OffsetPair { begin, end, data } => { + let data = convert_expression(data)?; + Location::OffsetPair { begin, end, data } + } + read::RawLocListEntry::StartEnd { begin, end, data } => { + let begin = convert_address(begin)?; + let end = convert_address(end)?; + let data = convert_expression(data)?; + Location::StartEnd { begin, end, data } + } + read::RawLocListEntry::StartLength { + begin, + length, + data, + } => { + let begin = convert_address(begin)?; + let data = convert_expression(data)?; + Location::StartLength { + begin, + length, + data, + } + } + read::RawLocListEntry::DefaultLocation { data } => { + let data = convert_expression(data)?; + Location::DefaultLocation { data } + } + }; + // In some cases, existing data may contain begin == end, filtering + // these out. + match loc { + Location::StartLength { length, .. } if length == 0 => continue, + Location::StartEnd { begin, end, .. } if begin == end => continue, + Location::OffsetPair { begin, end, .. } if begin == end => continue, + _ => (), + } + loc_list.push(loc); + } + Ok(LocationList(loc_list)) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::common::{ + DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase, + DebugStrOffsetsBase, Format, + }; + use crate::read; + use crate::write::{ + ConvertUnitContext, EndianVec, LineStringTable, RangeListTable, StringTable, + }; + use crate::LittleEndian; + use std::collections::HashMap; + + #[test] + fn test_loc_list() { + let mut line_strings = LineStringTable::default(); + let mut strings = StringTable::default(); + let mut expression = Expression::new(); + expression.op_constu(0); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let mut loc_list = LocationList(vec![ + Location::StartLength { + begin: Address::Constant(6666), + length: 7777, + data: expression.clone(), + }, + Location::StartEnd { + begin: Address::Constant(4444), + end: Address::Constant(5555), + data: expression.clone(), + }, + Location::BaseAddress { + address: Address::Constant(1111), + }, + Location::OffsetPair { + begin: 2222, + end: 3333, + data: expression.clone(), + }, + ]); + if version >= 5 { + loc_list.0.push(Location::DefaultLocation { + data: expression.clone(), + }); + } + + let mut locations = LocationListTable::default(); + let loc_list_id = locations.add(loc_list.clone()); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let loc_list_offsets = locations.write(&mut sections, encoding, None).unwrap(); + assert!(sections.debug_loc_refs.is_empty()); + assert!(sections.debug_loclists_refs.is_empty()); + + let read_debug_loc = + read::DebugLoc::new(sections.debug_loc.slice(), LittleEndian); + let read_debug_loclists = + read::DebugLocLists::new(sections.debug_loclists.slice(), LittleEndian); + let read_loc = read::LocationLists::new(read_debug_loc, read_debug_loclists); + let offset = loc_list_offsets.get(loc_list_id); + let read_loc_list = read_loc.raw_locations(offset, encoding).unwrap(); + + let dwarf = read::Dwarf { + locations: read_loc, + ..Default::default() + }; + let unit = read::Unit { + header: read::UnitHeader::new( + encoding, + 0, + read::UnitType::Compilation, + DebugAbbrevOffset(0), + DebugInfoOffset(0).into(), + read::EndianSlice::default(), + ), + abbreviations: read::Abbreviations::default(), + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase(0), + addr_base: DebugAddrBase(0), + loclists_base: DebugLocListsBase(0), + rnglists_base: DebugRngListsBase(0), + line_program: None, + dwo_id: None, + }; + let context = ConvertUnitContext { + dwarf: &dwarf, + unit: &unit, + line_strings: &mut line_strings, + strings: &mut strings, + ranges: &mut RangeListTable::default(), + locations: &mut locations, + convert_address: &|address| Some(Address::Constant(address)), + base_address: Address::Constant(0), + line_program_offset: None, + line_program_files: Vec::new(), + entry_ids: &HashMap::new(), + }; + let convert_loc_list = LocationList::from(read_loc_list, &context).unwrap(); + + if version <= 4 { + loc_list.0[0] = Location::StartEnd { + begin: Address::Constant(6666), + end: Address::Constant(6666 + 7777), + data: expression.clone(), + }; + } + assert_eq!(loc_list, convert_loc_list); + } + } + } + } +} diff --git a/vendor/gimli/src/write/mod.rs b/vendor/gimli/src/write/mod.rs new file mode 100644 index 000000000..47ba6319d --- /dev/null +++ b/vendor/gimli/src/write/mod.rs @@ -0,0 +1,425 @@ +//! Write DWARF debugging information. +//! +//! ## API Structure +//! +//! This module works by building up a representation of the debugging information +//! in memory, and then writing it all at once. It supports two major use cases: +//! +//! * Use the [`DwarfUnit`](./struct.DwarfUnit.html) type when writing DWARF +//! for a single compilation unit. +//! +//! * Use the [`Dwarf`](./struct.Dwarf.html) type when writing DWARF for multiple +//! compilation units. +//! +//! The module also supports reading in DWARF debugging information and writing it out +//! again, possibly after modifying it. Create a [`read::Dwarf`](../read/struct.Dwarf.html) +//! instance, and then use [`Dwarf::from`](./struct.Dwarf.html#method.from) to convert +//! it to a writable instance. +//! +//! ## Example Usage +//! +//! Write a compilation unit containing only the top level DIE. +//! +//! ```rust +//! use gimli::write::{ +//! Address, AttributeValue, DwarfUnit, EndianVec, Error, Range, RangeList, Sections, +//! }; +//! +//! fn example() -> Result<(), Error> { +//! // Choose the encoding parameters. +//! let encoding = gimli::Encoding { +//! format: gimli::Format::Dwarf32, +//! version: 5, +//! address_size: 8, +//! }; +//! // Create a container for a single compilation unit. +//! let mut dwarf = DwarfUnit::new(encoding); +//! // Set a range attribute on the root DIE. +//! let range_list = RangeList(vec![Range::StartLength { +//! begin: Address::Constant(0x100), +//! length: 42, +//! }]); +//! let range_list_id = dwarf.unit.ranges.add(range_list); +//! let root = dwarf.unit.root(); +//! dwarf.unit.get_mut(root).set( +//! gimli::DW_AT_ranges, +//! AttributeValue::RangeListRef(range_list_id), +//! ); +//! // Create a `Vec` for each DWARF section. +//! let mut sections = Sections::new(EndianVec::new(gimli::LittleEndian)); +//! // Finally, write the DWARF data to the sections. +//! dwarf.write(&mut sections)?; +//! sections.for_each(|id, data| { +//! // Here you can add the data to the output object file. +//! Ok(()) +//! }) +//! } +//! # fn main() { +//! # example().unwrap(); +//! # } + +use std::error; +use std::fmt; +use std::result; + +use crate::constants; + +mod endian_vec; +pub use self::endian_vec::*; + +mod writer; +pub use self::writer::*; + +#[macro_use] +mod section; +pub use self::section::*; + +macro_rules! define_id { + ($name:ident, $docs:expr) => { + #[doc=$docs] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct $name { + base_id: BaseId, + index: usize, + } + + impl $name { + #[inline] + fn new(base_id: BaseId, index: usize) -> Self { + $name { base_id, index } + } + } + }; +} + +macro_rules! define_offsets { + ($offsets:ident: $id:ident => $offset:ident, $off_doc:expr) => { + #[doc=$off_doc] + #[derive(Debug)] + pub struct $offsets { + base_id: BaseId, + // We know ids start at 0. + offsets: Vec<$offset>, + } + + impl $offsets { + /// Return an empty list of offsets. + #[inline] + pub fn none() -> Self { + $offsets { + base_id: BaseId::default(), + offsets: Vec::new(), + } + } + + /// Get the offset + /// + /// # Panics + /// + /// Panics if `id` is invalid. + #[inline] + pub fn get(&self, id: $id) -> $offset { + debug_assert_eq!(self.base_id, id.base_id); + self.offsets[id.index] + } + + /// Return the number of offsets. + #[inline] + pub fn count(&self) -> usize { + self.offsets.len() + } + } + }; +} + +mod abbrev; +pub use self::abbrev::*; + +mod cfi; +pub use self::cfi::*; + +mod dwarf; +pub use self::dwarf::*; + +mod line; +pub use self::line::*; + +mod loc; +pub use self::loc::*; + +mod op; +pub use self::op::*; + +mod range; +pub use self::range::*; + +mod str; +pub use self::str::*; + +mod unit; +pub use self::unit::*; + +/// An error that occurred when writing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Error { + /// The given offset is out of bounds. + OffsetOutOfBounds, + /// The given length is out of bounds. + LengthOutOfBounds, + /// The attribute value is an invalid for writing. + InvalidAttributeValue, + /// The value is too large for the encoding form. + ValueTooLarge, + /// Unsupported word size. + UnsupportedWordSize(u8), + /// Unsupported DWARF version. + UnsupportedVersion(u16), + /// The unit length is too large for the requested DWARF format. + InitialLengthOverflow, + /// The address is invalid. + InvalidAddress, + /// The reference is invalid. + InvalidReference, + /// A requested feature requires a different DWARF version. + NeedVersion(u16), + /// Strings in line number program have mismatched forms. + LineStringFormMismatch, + /// The range is empty or otherwise invalid. + InvalidRange, + /// The line number program encoding is incompatible with the unit encoding. + IncompatibleLineProgramEncoding, + /// Could not encode code offset for a frame instruction. + InvalidFrameCodeOffset(u32), + /// Could not encode data offset for a frame instruction. + InvalidFrameDataOffset(i32), + /// Unsupported eh_frame pointer encoding. + UnsupportedPointerEncoding(constants::DwEhPe), + /// Unsupported reference in CFI expression. + UnsupportedCfiExpressionReference, + /// Unsupported forward reference in expression. + UnsupportedExpressionForwardReference, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + match *self { + Error::OffsetOutOfBounds => write!(f, "The given offset is out of bounds."), + Error::LengthOutOfBounds => write!(f, "The given length is out of bounds."), + Error::InvalidAttributeValue => { + write!(f, "The attribute value is an invalid for writing.") + } + Error::ValueTooLarge => write!(f, "The value is too large for the encoding form."), + Error::UnsupportedWordSize(size) => write!(f, "Unsupported word size: {}", size), + Error::UnsupportedVersion(version) => { + write!(f, "Unsupported DWARF version: {}", version) + } + Error::InitialLengthOverflow => write!( + f, + "The unit length is too large for the requested DWARF format." + ), + Error::InvalidAddress => write!(f, "The address is invalid."), + Error::InvalidReference => write!(f, "The reference is invalid."), + Error::NeedVersion(version) => write!( + f, + "A requested feature requires a DWARF version {}.", + version + ), + Error::LineStringFormMismatch => { + write!(f, "Strings in line number program have mismatched forms.") + } + Error::InvalidRange => write!(f, "The range is empty or otherwise invalid."), + Error::IncompatibleLineProgramEncoding => write!( + f, + "The line number program encoding is incompatible with the unit encoding." + ), + Error::InvalidFrameCodeOffset(offset) => write!( + f, + "Could not encode code offset ({}) for a frame instruction.", + offset, + ), + Error::InvalidFrameDataOffset(offset) => write!( + f, + "Could not encode data offset ({}) for a frame instruction.", + offset, + ), + Error::UnsupportedPointerEncoding(eh_pe) => { + write!(f, "Unsupported eh_frame pointer encoding ({}).", eh_pe) + } + Error::UnsupportedCfiExpressionReference => { + write!(f, "Unsupported reference in CFI expression.") + } + Error::UnsupportedExpressionForwardReference => { + write!(f, "Unsupported forward reference in expression.") + } + } + } +} + +impl error::Error for Error {} + +/// The result of a write. +pub type Result<T> = result::Result<T, Error>; + +/// An address. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Address { + /// A fixed address that does not require relocation. + Constant(u64), + /// An address that is relative to a symbol which may be relocated. + Symbol { + /// The symbol that the address is relative to. + /// + /// The meaning of this value is decided by the writer, but + /// will typically be an index into a symbol table. + symbol: usize, + /// The offset of the address relative to the symbol. + /// + /// This will typically be used as the addend in a relocation. + addend: i64, + }, +} + +/// A reference to a `.debug_info` entry. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Reference { + /// An external symbol. + /// + /// The meaning of this value is decided by the writer, but + /// will typically be an index into a symbol table. + Symbol(usize), + /// An entry in the same section. + /// + /// This only supports references in units that are emitted together. + Entry(UnitId, UnitEntryId), +} + +// This type is only used in debug assertions. +#[cfg(not(debug_assertions))] +type BaseId = (); + +#[cfg(debug_assertions)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct BaseId(usize); + +#[cfg(debug_assertions)] +impl Default for BaseId { + fn default() -> Self { + use std::sync::atomic; + static BASE_ID: atomic::AtomicUsize = atomic::AtomicUsize::new(0); + BaseId(BASE_ID.fetch_add(1, atomic::Ordering::Relaxed)) + } +} + +#[cfg(feature = "read")] +mod convert { + use super::*; + use crate::read; + + pub(crate) use super::unit::convert::*; + + /// An error that occurred when converting a read value into a write value. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum ConvertError { + /// An error occurred when reading. + Read(read::Error), + /// Writing of this attribute value is not implemented yet. + UnsupportedAttributeValue, + /// This attribute value is an invalid name/form combination. + InvalidAttributeValue, + /// A `.debug_info` reference does not refer to a valid entry. + InvalidDebugInfoOffset, + /// An address could not be converted. + InvalidAddress, + /// Writing this line number instruction is not implemented yet. + UnsupportedLineInstruction, + /// Writing this form of line string is not implemented yet. + UnsupportedLineStringForm, + /// A `.debug_line` file index is invalid. + InvalidFileIndex, + /// A `.debug_line` directory index is invalid. + InvalidDirectoryIndex, + /// A `.debug_line` line base is invalid. + InvalidLineBase, + /// A `.debug_line` reference is invalid. + InvalidLineRef, + /// A `.debug_info` unit entry reference is invalid. + InvalidUnitRef, + /// A `.debug_info` reference is invalid. + InvalidDebugInfoRef, + /// Invalid relative address in a range list. + InvalidRangeRelativeAddress, + /// Writing this CFI instruction is not implemented yet. + UnsupportedCfiInstruction, + /// Writing indirect pointers is not implemented yet. + UnsupportedIndirectAddress, + /// Writing this expression operation is not implemented yet. + UnsupportedOperation, + /// Operation branch target is invalid. + InvalidBranchTarget, + /// Writing this unit type is not supported yet. + UnsupportedUnitType, + } + + impl fmt::Display for ConvertError { + fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { + use self::ConvertError::*; + match *self { + Read(ref e) => e.fmt(f), + UnsupportedAttributeValue => { + write!(f, "Writing of this attribute value is not implemented yet.") + } + InvalidAttributeValue => write!( + f, + "This attribute value is an invalid name/form combination." + ), + InvalidDebugInfoOffset => write!( + f, + "A `.debug_info` reference does not refer to a valid entry." + ), + InvalidAddress => write!(f, "An address could not be converted."), + UnsupportedLineInstruction => write!( + f, + "Writing this line number instruction is not implemented yet." + ), + UnsupportedLineStringForm => write!( + f, + "Writing this form of line string is not implemented yet." + ), + InvalidFileIndex => write!(f, "A `.debug_line` file index is invalid."), + InvalidDirectoryIndex => write!(f, "A `.debug_line` directory index is invalid."), + InvalidLineBase => write!(f, "A `.debug_line` line base is invalid."), + InvalidLineRef => write!(f, "A `.debug_line` reference is invalid."), + InvalidUnitRef => write!(f, "A `.debug_info` unit entry reference is invalid."), + InvalidDebugInfoRef => write!(f, "A `.debug_info` reference is invalid."), + InvalidRangeRelativeAddress => { + write!(f, "Invalid relative address in a range list.") + } + UnsupportedCfiInstruction => { + write!(f, "Writing this CFI instruction is not implemented yet.") + } + UnsupportedIndirectAddress => { + write!(f, "Writing indirect pointers is not implemented yet.") + } + UnsupportedOperation => write!( + f, + "Writing this expression operation is not implemented yet." + ), + InvalidBranchTarget => write!(f, "Operation branch target is invalid."), + UnsupportedUnitType => write!(f, "Writing this unit type is not supported yet."), + } + } + } + + impl error::Error for ConvertError {} + + impl From<read::Error> for ConvertError { + fn from(e: read::Error) -> Self { + ConvertError::Read(e) + } + } + + /// The result of a conversion. + pub type ConvertResult<T> = result::Result<T, ConvertError>; +} +#[cfg(feature = "read")] +pub use self::convert::*; diff --git a/vendor/gimli/src/write/op.rs b/vendor/gimli/src/write/op.rs new file mode 100644 index 000000000..d1cacb356 --- /dev/null +++ b/vendor/gimli/src/write/op.rs @@ -0,0 +1,1617 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; + +use crate::common::{Encoding, Register}; +use crate::constants::{self, DwOp}; +use crate::leb128::write::{sleb128_size, uleb128_size}; +use crate::write::{ + Address, DebugInfoReference, Error, Reference, Result, UnitEntryId, UnitOffsets, Writer, +}; + +/// The bytecode for a DWARF expression or location description. +#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] +pub struct Expression { + operations: Vec<Operation>, +} + +impl Expression { + /// Create an empty expression. + #[inline] + pub fn new() -> Self { + Self::default() + } + + /// Create an expression from raw bytecode. + /// + /// This does not support operations that require references, such as `DW_OP_addr`. + #[inline] + pub fn raw(bytecode: Vec<u8>) -> Self { + Expression { + operations: vec![Operation::Raw(bytecode)], + } + } + + /// Add an operation to the expression. + /// + /// This should only be used for operations that have no explicit operands. + pub fn op(&mut self, opcode: DwOp) { + self.operations.push(Operation::Simple(opcode)); + } + + /// Add a `DW_OP_addr` operation to the expression. + pub fn op_addr(&mut self, address: Address) { + self.operations.push(Operation::Address(address)); + } + + /// Add a `DW_OP_constu` operation to the expression. + /// + /// This may be emitted as a smaller equivalent operation. + pub fn op_constu(&mut self, value: u64) { + self.operations.push(Operation::UnsignedConstant(value)); + } + + /// Add a `DW_OP_consts` operation to the expression. + /// + /// This may be emitted as a smaller equivalent operation. + pub fn op_consts(&mut self, value: i64) { + self.operations.push(Operation::SignedConstant(value)); + } + + /// Add a `DW_OP_const_type` or `DW_OP_GNU_const_type` operation to the expression. + pub fn op_const_type(&mut self, base: UnitEntryId, value: Box<[u8]>) { + self.operations.push(Operation::ConstantType(base, value)); + } + + /// Add a `DW_OP_fbreg` operation to the expression. + pub fn op_fbreg(&mut self, offset: i64) { + self.operations.push(Operation::FrameOffset(offset)); + } + + /// Add a `DW_OP_bregx` operation to the expression. + /// + /// This may be emitted as a smaller equivalent operation. + pub fn op_breg(&mut self, register: Register, offset: i64) { + self.operations + .push(Operation::RegisterOffset(register, offset)); + } + + /// Add a `DW_OP_regval_type` or `DW_OP_GNU_regval_type` operation to the expression. + /// + /// This may be emitted as a smaller equivalent operation. + pub fn op_regval_type(&mut self, register: Register, base: UnitEntryId) { + self.operations + .push(Operation::RegisterType(register, base)); + } + + /// Add a `DW_OP_pick` operation to the expression. + /// + /// This may be emitted as a `DW_OP_dup` or `DW_OP_over` operation. + pub fn op_pick(&mut self, index: u8) { + self.operations.push(Operation::Pick(index)); + } + + /// Add a `DW_OP_deref` operation to the expression. + pub fn op_deref(&mut self) { + self.operations.push(Operation::Deref { space: false }); + } + + /// Add a `DW_OP_xderef` operation to the expression. + pub fn op_xderef(&mut self) { + self.operations.push(Operation::Deref { space: true }); + } + + /// Add a `DW_OP_deref_size` operation to the expression. + pub fn op_deref_size(&mut self, size: u8) { + self.operations + .push(Operation::DerefSize { size, space: false }); + } + + /// Add a `DW_OP_xderef_size` operation to the expression. + pub fn op_xderef_size(&mut self, size: u8) { + self.operations + .push(Operation::DerefSize { size, space: true }); + } + + /// Add a `DW_OP_deref_type` or `DW_OP_GNU_deref_type` operation to the expression. + pub fn op_deref_type(&mut self, size: u8, base: UnitEntryId) { + self.operations.push(Operation::DerefType { + size, + base, + space: false, + }); + } + + /// Add a `DW_OP_xderef_type` operation to the expression. + pub fn op_xderef_type(&mut self, size: u8, base: UnitEntryId) { + self.operations.push(Operation::DerefType { + size, + base, + space: true, + }); + } + + /// Add a `DW_OP_plus_uconst` operation to the expression. + pub fn op_plus_uconst(&mut self, value: u64) { + self.operations.push(Operation::PlusConstant(value)); + } + + /// Add a `DW_OP_skip` operation to the expression. + /// + /// Returns the index of the operation. The caller must call `set_target` with + /// this index to set the target of the branch. + pub fn op_skip(&mut self) -> usize { + let index = self.next_index(); + self.operations.push(Operation::Skip(!0)); + index + } + + /// Add a `DW_OP_bra` operation to the expression. + /// + /// Returns the index of the operation. The caller must call `set_target` with + /// this index to set the target of the branch. + pub fn op_bra(&mut self) -> usize { + let index = self.next_index(); + self.operations.push(Operation::Branch(!0)); + index + } + + /// Return the index that will be assigned to the next operation. + /// + /// This can be passed to `set_target`. + #[inline] + pub fn next_index(&self) -> usize { + self.operations.len() + } + + /// Set the target of a `DW_OP_skip` or `DW_OP_bra` operation . + pub fn set_target(&mut self, operation: usize, new_target: usize) { + debug_assert!(new_target <= self.next_index()); + debug_assert_ne!(operation, new_target); + match self.operations[operation] { + Operation::Skip(ref mut target) | Operation::Branch(ref mut target) => { + *target = new_target; + } + _ => unimplemented!(), + } + } + + /// Add a `DW_OP_call4` operation to the expression. + pub fn op_call(&mut self, entry: UnitEntryId) { + self.operations.push(Operation::Call(entry)); + } + + /// Add a `DW_OP_call_ref` operation to the expression. + pub fn op_call_ref(&mut self, entry: Reference) { + self.operations.push(Operation::CallRef(entry)); + } + + /// Add a `DW_OP_convert` or `DW_OP_GNU_convert` operation to the expression. + /// + /// `base` is the DIE of the base type, or `None` for the generic type. + pub fn op_convert(&mut self, base: Option<UnitEntryId>) { + self.operations.push(Operation::Convert(base)); + } + + /// Add a `DW_OP_reinterpret` or `DW_OP_GNU_reinterpret` operation to the expression. + /// + /// `base` is the DIE of the base type, or `None` for the generic type. + pub fn op_reinterpret(&mut self, base: Option<UnitEntryId>) { + self.operations.push(Operation::Reinterpret(base)); + } + + /// Add a `DW_OP_entry_value` or `DW_OP_GNU_entry_value` operation to the expression. + pub fn op_entry_value(&mut self, expression: Expression) { + self.operations.push(Operation::EntryValue(expression)); + } + + /// Add a `DW_OP_regx` operation to the expression. + /// + /// This may be emitted as a smaller equivalent operation. + pub fn op_reg(&mut self, register: Register) { + self.operations.push(Operation::Register(register)); + } + + /// Add a `DW_OP_implicit_value` operation to the expression. + pub fn op_implicit_value(&mut self, data: Box<[u8]>) { + self.operations.push(Operation::ImplicitValue(data)); + } + + /// Add a `DW_OP_implicit_pointer` or `DW_OP_GNU_implicit_pointer` operation to the expression. + pub fn op_implicit_pointer(&mut self, entry: Reference, byte_offset: i64) { + self.operations + .push(Operation::ImplicitPointer { entry, byte_offset }); + } + + /// Add a `DW_OP_piece` operation to the expression. + pub fn op_piece(&mut self, size_in_bytes: u64) { + self.operations.push(Operation::Piece { size_in_bytes }); + } + + /// Add a `DW_OP_bit_piece` operation to the expression. + pub fn op_bit_piece(&mut self, size_in_bits: u64, bit_offset: u64) { + self.operations.push(Operation::BitPiece { + size_in_bits, + bit_offset, + }); + } + + /// Add a `DW_OP_GNU_parameter_ref` operation to the expression. + pub fn op_gnu_parameter_ref(&mut self, entry: UnitEntryId) { + self.operations.push(Operation::ParameterRef(entry)); + } + + /// Add a `DW_OP_WASM_location 0x0` operation to the expression. + pub fn op_wasm_local(&mut self, index: u32) { + self.operations.push(Operation::WasmLocal(index)); + } + + /// Add a `DW_OP_WASM_location 0x1` operation to the expression. + pub fn op_wasm_global(&mut self, index: u32) { + self.operations.push(Operation::WasmGlobal(index)); + } + + /// Add a `DW_OP_WASM_location 0x2` operation to the expression. + pub fn op_wasm_stack(&mut self, index: u32) { + self.operations.push(Operation::WasmStack(index)); + } + + pub(crate) fn size(&self, encoding: Encoding, unit_offsets: Option<&UnitOffsets>) -> usize { + let mut size = 0; + for operation in &self.operations { + size += operation.size(encoding, unit_offsets); + } + size + } + + pub(crate) fn write<W: Writer>( + &self, + w: &mut W, + mut refs: Option<&mut Vec<DebugInfoReference>>, + encoding: Encoding, + unit_offsets: Option<&UnitOffsets>, + ) -> Result<()> { + // TODO: only calculate offsets if needed? + let mut offsets = Vec::with_capacity(self.operations.len()); + let mut offset = w.len(); + for operation in &self.operations { + offsets.push(offset); + offset += operation.size(encoding, unit_offsets); + } + offsets.push(offset); + for (operation, offset) in self.operations.iter().zip(offsets.iter().copied()) { + let refs = match refs { + Some(ref mut refs) => Some(&mut **refs), + None => None, + }; + debug_assert_eq!(w.len(), offset); + operation.write(w, refs, encoding, unit_offsets, &offsets)?; + } + Ok(()) + } +} + +/// A single DWARF operation. +// +// This type is intentionally not public so that we can change the +// representation of expressions as needed. +// +// Variants are listed in the order they appear in Section 2.5. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum Operation { + /// Raw bytecode. + /// + /// Does not support references. + Raw(Vec<u8>), + /// An operation that has no explicit operands. + /// + /// Represents: + /// - `DW_OP_drop`, `DW_OP_swap`, `DW_OP_rot` + /// - `DW_OP_push_object_address`, `DW_OP_form_tls_address`, `DW_OP_call_frame_cfa` + /// - `DW_OP_abs`, `DW_OP_and`, `DW_OP_div`, `DW_OP_minus`, `DW_OP_mod`, `DW_OP_mul`, + /// `DW_OP_neg`, `DW_OP_not`, `DW_OP_or`, `DW_OP_plus`, `DW_OP_shl`, `DW_OP_shr`, + /// `DW_OP_shra`, `DW_OP_xor` + /// - `DW_OP_le`, `DW_OP_ge`, `DW_OP_eq`, `DW_OP_lt`, `DW_OP_gt`, `DW_OP_ne` + /// - `DW_OP_nop` + /// - `DW_OP_stack_value` + Simple(DwOp), + /// Relocate the address if needed, and push it on the stack. + /// + /// Represents `DW_OP_addr`. + Address(Address), + /// Push an unsigned constant value on the stack. + /// + /// Represents `DW_OP_constu`. + UnsignedConstant(u64), + /// Push a signed constant value on the stack. + /// + /// Represents `DW_OP_consts`. + SignedConstant(i64), + /* TODO: requires .debug_addr write support + /// Read the address at the given index in `.debug_addr, relocate the address if needed, + /// and push it on the stack. + /// + /// Represents `DW_OP_addrx`. + AddressIndex(DebugAddrIndex<Offset>), + /// Read the address at the given index in `.debug_addr, and push it on the stack. + /// Do not relocate the address. + /// + /// Represents `DW_OP_constx`. + ConstantIndex(DebugAddrIndex<Offset>), + */ + /// Interpret the value bytes as a constant of a given type, and push it on the stack. + /// + /// Represents `DW_OP_const_type`. + ConstantType(UnitEntryId, Box<[u8]>), + /// Compute the frame base (using `DW_AT_frame_base`), add the + /// given offset, and then push the resulting sum on the stack. + /// + /// Represents `DW_OP_fbreg`. + FrameOffset(i64), + /// Find the contents of the given register, add the offset, and then + /// push the resulting sum on the stack. + /// + /// Represents `DW_OP_bregx`. + RegisterOffset(Register, i64), + /// Interpret the contents of the given register as a value of the given type, + /// and push it on the stack. + /// + /// Represents `DW_OP_regval_type`. + RegisterType(Register, UnitEntryId), + /// Copy the item at a stack index and push it on top of the stack. + /// + /// Represents `DW_OP_pick`, `DW_OP_dup`, and `DW_OP_over`. + Pick(u8), + /// Pop the topmost value of the stack, dereference it, and push the + /// resulting value. + /// + /// Represents `DW_OP_deref` and `DW_OP_xderef`. + Deref { + /// True if the dereference operation takes an address space + /// argument from the stack; false otherwise. + space: bool, + }, + /// Pop the topmost value of the stack, dereference it to obtain a value + /// of the given size, and push the resulting value. + /// + /// Represents `DW_OP_deref_size` and `DW_OP_xderef_size`. + DerefSize { + /// True if the dereference operation takes an address space + /// argument from the stack; false otherwise. + space: bool, + /// The size of the data to dereference. + size: u8, + }, + /// Pop the topmost value of the stack, dereference it to obtain a value + /// of the given type, and push the resulting value. + /// + /// Represents `DW_OP_deref_type` and `DW_OP_xderef_type`. + DerefType { + /// True if the dereference operation takes an address space + /// argument from the stack; false otherwise. + space: bool, + /// The size of the data to dereference. + size: u8, + /// The DIE of the base type, or `None` for the generic type. + base: UnitEntryId, + }, + /// Add an unsigned constant to the topmost value on the stack. + /// + /// Represents `DW_OP_plus_uconst`. + PlusConstant(u64), + /// Unconditional branch to the target location. + /// + /// The value is the index within the expression of the operation to branch to. + /// This will be converted to a relative offset when writing. + /// + /// Represents `DW_OP_skip`. + Skip(usize), + /// Branch to the target location if the top of stack is nonzero. + /// + /// The value is the index within the expression of the operation to branch to. + /// This will be converted to a relative offset when writing. + /// + /// Represents `DW_OP_bra`. + Branch(usize), + /// Evaluate a DWARF expression as a subroutine. + /// + /// The expression comes from the `DW_AT_location` attribute of the indicated DIE. + /// + /// Represents `DW_OP_call4`. + Call(UnitEntryId), + /// Evaluate an external DWARF expression as a subroutine. + /// + /// The expression comes from the `DW_AT_location` attribute of the indicated DIE, + /// which may be in another compilation unit or shared object. + /// + /// Represents `DW_OP_call_ref`. + CallRef(Reference), + /// Pop the top stack entry, convert it to a different type, and push it on the stack. + /// + /// Represents `DW_OP_convert`. + Convert(Option<UnitEntryId>), + /// Pop the top stack entry, reinterpret the bits in its value as a different type, + /// and push it on the stack. + /// + /// Represents `DW_OP_reinterpret`. + Reinterpret(Option<UnitEntryId>), + /// Evaluate an expression at the entry to the current subprogram, and push it on the stack. + /// + /// Represents `DW_OP_entry_value`. + EntryValue(Expression), + // FIXME: EntryRegister + /// Indicate that this piece's location is in the given register. + /// + /// Completes the piece or expression. + /// + /// Represents `DW_OP_regx`. + Register(Register), + /// The object has no location, but has a known constant value. + /// + /// Completes the piece or expression. + /// + /// Represents `DW_OP_implicit_value`. + ImplicitValue(Box<[u8]>), + /// The object is a pointer to a value which has no actual location, such as + /// an implicit value or a stack value. + /// + /// Completes the piece or expression. + /// + /// Represents `DW_OP_implicit_pointer`. + ImplicitPointer { + /// The DIE of the value that this is an implicit pointer into. + entry: Reference, + /// The byte offset into the value that the implicit pointer points to. + byte_offset: i64, + }, + /// Terminate a piece. + /// + /// Represents `DW_OP_piece`. + Piece { + /// The size of this piece in bytes. + size_in_bytes: u64, + }, + /// Terminate a piece with a size in bits. + /// + /// Represents `DW_OP_bit_piece`. + BitPiece { + /// The size of this piece in bits. + size_in_bits: u64, + /// The bit offset of this piece. + bit_offset: u64, + }, + /// This represents a parameter that was optimized out. + /// + /// The entry is the definition of the parameter, and is matched to + /// the `DW_TAG_GNU_call_site_parameter` in the caller that also + /// points to the same definition of the parameter. + /// + /// Represents `DW_OP_GNU_parameter_ref`. + ParameterRef(UnitEntryId), + /// The index of a local in the currently executing function. + /// + /// Represents `DW_OP_WASM_location 0x00`. + WasmLocal(u32), + /// The index of a global. + /// + /// Represents `DW_OP_WASM_location 0x01`. + WasmGlobal(u32), + /// The index of an item on the operand stack. + /// + /// Represents `DW_OP_WASM_location 0x02`. + WasmStack(u32), +} + +impl Operation { + fn size(&self, encoding: Encoding, unit_offsets: Option<&UnitOffsets>) -> usize { + let base_size = |base| { + // Errors are handled during writes. + match unit_offsets { + Some(offsets) => uleb128_size(offsets.unit_offset(base)), + None => 0, + } + }; + 1 + match *self { + Operation::Raw(ref bytecode) => return bytecode.len(), + Operation::Simple(_) => 0, + Operation::Address(_) => encoding.address_size as usize, + Operation::UnsignedConstant(value) => { + if value < 32 { + 0 + } else { + uleb128_size(value) + } + } + Operation::SignedConstant(value) => sleb128_size(value), + Operation::ConstantType(base, ref value) => base_size(base) + 1 + value.len(), + Operation::FrameOffset(offset) => sleb128_size(offset), + Operation::RegisterOffset(register, offset) => { + if register.0 < 32 { + sleb128_size(offset) + } else { + uleb128_size(register.0.into()) + sleb128_size(offset) + } + } + Operation::RegisterType(register, base) => { + uleb128_size(register.0.into()) + base_size(base) + } + Operation::Pick(index) => { + if index > 1 { + 1 + } else { + 0 + } + } + Operation::Deref { .. } => 0, + Operation::DerefSize { .. } => 1, + Operation::DerefType { base, .. } => 1 + base_size(base), + Operation::PlusConstant(value) => uleb128_size(value), + Operation::Skip(_) => 2, + Operation::Branch(_) => 2, + Operation::Call(_) => 4, + Operation::CallRef(_) => encoding.format.word_size() as usize, + Operation::Convert(base) => match base { + Some(base) => base_size(base), + None => 1, + }, + Operation::Reinterpret(base) => match base { + Some(base) => base_size(base), + None => 1, + }, + Operation::EntryValue(ref expression) => { + let length = expression.size(encoding, unit_offsets); + uleb128_size(length as u64) + length + } + Operation::Register(register) => { + if register.0 < 32 { + 0 + } else { + uleb128_size(register.0.into()) + } + } + Operation::ImplicitValue(ref data) => uleb128_size(data.len() as u64) + data.len(), + Operation::ImplicitPointer { byte_offset, .. } => { + encoding.format.word_size() as usize + sleb128_size(byte_offset) + } + Operation::Piece { size_in_bytes } => uleb128_size(size_in_bytes), + Operation::BitPiece { + size_in_bits, + bit_offset, + } => uleb128_size(size_in_bits) + uleb128_size(bit_offset), + Operation::ParameterRef(_) => 4, + Operation::WasmLocal(index) + | Operation::WasmGlobal(index) + | Operation::WasmStack(index) => 1 + uleb128_size(index.into()), + } + } + + pub(crate) fn write<W: Writer>( + &self, + w: &mut W, + refs: Option<&mut Vec<DebugInfoReference>>, + encoding: Encoding, + unit_offsets: Option<&UnitOffsets>, + offsets: &[usize], + ) -> Result<()> { + let entry_offset = |entry| match unit_offsets { + Some(offsets) => { + let offset = offsets.unit_offset(entry); + if offset == 0 { + Err(Error::UnsupportedExpressionForwardReference) + } else { + Ok(offset) + } + } + None => Err(Error::UnsupportedCfiExpressionReference), + }; + match *self { + Operation::Raw(ref bytecode) => w.write(bytecode)?, + Operation::Simple(opcode) => w.write_u8(opcode.0)?, + Operation::Address(address) => { + w.write_u8(constants::DW_OP_addr.0)?; + w.write_address(address, encoding.address_size)?; + } + Operation::UnsignedConstant(value) => { + if value < 32 { + w.write_u8(constants::DW_OP_lit0.0 + value as u8)?; + } else { + w.write_u8(constants::DW_OP_constu.0)?; + w.write_uleb128(value)?; + } + } + Operation::SignedConstant(value) => { + w.write_u8(constants::DW_OP_consts.0)?; + w.write_sleb128(value)?; + } + Operation::ConstantType(base, ref value) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_const_type.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_const_type.0)?; + } + w.write_uleb128(entry_offset(base)?)?; + w.write_udata(value.len() as u64, 1)?; + w.write(&value)?; + } + Operation::FrameOffset(offset) => { + w.write_u8(constants::DW_OP_fbreg.0)?; + w.write_sleb128(offset)?; + } + Operation::RegisterOffset(register, offset) => { + if register.0 < 32 { + w.write_u8(constants::DW_OP_breg0.0 + register.0 as u8)?; + } else { + w.write_u8(constants::DW_OP_bregx.0)?; + w.write_uleb128(register.0.into())?; + } + w.write_sleb128(offset)?; + } + Operation::RegisterType(register, base) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_regval_type.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_regval_type.0)?; + } + w.write_uleb128(register.0.into())?; + w.write_uleb128(entry_offset(base)?)?; + } + Operation::Pick(index) => match index { + 0 => w.write_u8(constants::DW_OP_dup.0)?, + 1 => w.write_u8(constants::DW_OP_over.0)?, + _ => { + w.write_u8(constants::DW_OP_pick.0)?; + w.write_u8(index)?; + } + }, + Operation::Deref { space } => { + if space { + w.write_u8(constants::DW_OP_xderef.0)?; + } else { + w.write_u8(constants::DW_OP_deref.0)?; + } + } + Operation::DerefSize { space, size } => { + if space { + w.write_u8(constants::DW_OP_xderef_size.0)?; + } else { + w.write_u8(constants::DW_OP_deref_size.0)?; + } + w.write_u8(size)?; + } + Operation::DerefType { space, size, base } => { + if space { + w.write_u8(constants::DW_OP_xderef_type.0)?; + } else { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_deref_type.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_deref_type.0)?; + } + } + w.write_u8(size)?; + w.write_uleb128(entry_offset(base)?)?; + } + Operation::PlusConstant(value) => { + w.write_u8(constants::DW_OP_plus_uconst.0)?; + w.write_uleb128(value)?; + } + Operation::Skip(target) => { + w.write_u8(constants::DW_OP_skip.0)?; + let offset = offsets[target] as i64 - (w.len() as i64 + 2); + w.write_sdata(offset, 2)?; + } + Operation::Branch(target) => { + w.write_u8(constants::DW_OP_bra.0)?; + let offset = offsets[target] as i64 - (w.len() as i64 + 2); + w.write_sdata(offset, 2)?; + } + Operation::Call(entry) => { + w.write_u8(constants::DW_OP_call4.0)?; + // TODO: this probably won't work in practice, because we may + // only know the offsets of base type DIEs at this point. + w.write_udata(entry_offset(entry)?, 4)?; + } + Operation::CallRef(entry) => { + w.write_u8(constants::DW_OP_call_ref.0)?; + let size = encoding.format.word_size(); + match entry { + Reference::Symbol(symbol) => w.write_reference(symbol, size)?, + Reference::Entry(unit, entry) => { + let refs = refs.ok_or(Error::InvalidReference)?; + refs.push(DebugInfoReference { + offset: w.len(), + unit, + entry, + size, + }); + w.write_udata(0, size)?; + } + } + } + Operation::Convert(base) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_convert.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_convert.0)?; + } + match base { + Some(base) => w.write_uleb128(entry_offset(base)?)?, + None => w.write_u8(0)?, + } + } + Operation::Reinterpret(base) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_reinterpret.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_reinterpret.0)?; + } + match base { + Some(base) => w.write_uleb128(entry_offset(base)?)?, + None => w.write_u8(0)?, + } + } + Operation::EntryValue(ref expression) => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_entry_value.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_entry_value.0)?; + } + let length = expression.size(encoding, unit_offsets); + w.write_uleb128(length as u64)?; + expression.write(w, refs, encoding, unit_offsets)?; + } + Operation::Register(register) => { + if register.0 < 32 { + w.write_u8(constants::DW_OP_reg0.0 + register.0 as u8)?; + } else { + w.write_u8(constants::DW_OP_regx.0)?; + w.write_uleb128(register.0.into())?; + } + } + Operation::ImplicitValue(ref data) => { + w.write_u8(constants::DW_OP_implicit_value.0)?; + w.write_uleb128(data.len() as u64)?; + w.write(&data)?; + } + Operation::ImplicitPointer { entry, byte_offset } => { + if encoding.version >= 5 { + w.write_u8(constants::DW_OP_implicit_pointer.0)?; + } else { + w.write_u8(constants::DW_OP_GNU_implicit_pointer.0)?; + } + let size = encoding.format.word_size(); + match entry { + Reference::Symbol(symbol) => { + w.write_reference(symbol, size)?; + } + Reference::Entry(unit, entry) => { + let refs = refs.ok_or(Error::InvalidReference)?; + refs.push(DebugInfoReference { + offset: w.len(), + unit, + entry, + size, + }); + w.write_udata(0, size)?; + } + } + w.write_sleb128(byte_offset)?; + } + Operation::Piece { size_in_bytes } => { + w.write_u8(constants::DW_OP_piece.0)?; + w.write_uleb128(size_in_bytes)?; + } + Operation::BitPiece { + size_in_bits, + bit_offset, + } => { + w.write_u8(constants::DW_OP_bit_piece.0)?; + w.write_uleb128(size_in_bits)?; + w.write_uleb128(bit_offset)?; + } + Operation::ParameterRef(entry) => { + w.write_u8(constants::DW_OP_GNU_parameter_ref.0)?; + w.write_udata(entry_offset(entry)?, 4)?; + } + Operation::WasmLocal(index) => { + w.write(&[constants::DW_OP_WASM_location.0, 0])?; + w.write_uleb128(index.into())?; + } + Operation::WasmGlobal(index) => { + w.write(&[constants::DW_OP_WASM_location.0, 1])?; + w.write_uleb128(index.into())?; + } + Operation::WasmStack(index) => { + w.write(&[constants::DW_OP_WASM_location.0, 2])?; + w.write_uleb128(index.into())?; + } + } + Ok(()) + } +} + +#[cfg(feature = "read")] +pub(crate) mod convert { + use super::*; + use crate::common::UnitSectionOffset; + use crate::read::{self, Reader}; + use crate::write::{ConvertError, ConvertResult, UnitEntryId, UnitId}; + use std::collections::HashMap; + + impl Expression { + /// Create an expression from the input expression. + pub fn from<R: Reader<Offset = usize>>( + from_expression: read::Expression<R>, + encoding: Encoding, + dwarf: Option<&read::Dwarf<R>>, + unit: Option<&read::Unit<R>>, + entry_ids: Option<&HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>>, + convert_address: &dyn Fn(u64) -> Option<Address>, + ) -> ConvertResult<Expression> { + let convert_unit_offset = |offset: read::UnitOffset| -> ConvertResult<_> { + let entry_ids = entry_ids.ok_or(ConvertError::UnsupportedOperation)?; + let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; + let id = entry_ids + .get(&offset.to_unit_section_offset(unit)) + .ok_or(ConvertError::InvalidUnitRef)?; + Ok(id.1) + }; + let convert_debug_info_offset = |offset| -> ConvertResult<_> { + // TODO: support relocations + let entry_ids = entry_ids.ok_or(ConvertError::UnsupportedOperation)?; + let id = entry_ids + .get(&UnitSectionOffset::DebugInfoOffset(offset)) + .ok_or(ConvertError::InvalidDebugInfoRef)?; + Ok(Reference::Entry(id.0, id.1)) + }; + + // Calculate offsets for use in branch/skip operations. + let mut offsets = Vec::new(); + let mut offset = 0; + let mut from_operations = from_expression.clone().operations(encoding); + while let Some(_) = from_operations.next()? { + offsets.push(offset); + offset = from_operations.offset_from(&from_expression); + } + offsets.push(from_expression.0.len()); + + let mut from_operations = from_expression.clone().operations(encoding); + let mut operations = Vec::new(); + while let Some(from_operation) = from_operations.next()? { + let operation = match from_operation { + read::Operation::Deref { + base_type, + size, + space, + } => { + if base_type.0 != 0 { + let base = convert_unit_offset(base_type)?; + Operation::DerefType { space, size, base } + } else if size != encoding.address_size { + Operation::DerefSize { space, size } + } else { + Operation::Deref { space } + } + } + read::Operation::Drop => Operation::Simple(constants::DW_OP_drop), + read::Operation::Pick { index } => Operation::Pick(index), + read::Operation::Swap => Operation::Simple(constants::DW_OP_swap), + read::Operation::Rot => Operation::Simple(constants::DW_OP_rot), + read::Operation::Abs => Operation::Simple(constants::DW_OP_abs), + read::Operation::And => Operation::Simple(constants::DW_OP_and), + read::Operation::Div => Operation::Simple(constants::DW_OP_div), + read::Operation::Minus => Operation::Simple(constants::DW_OP_minus), + read::Operation::Mod => Operation::Simple(constants::DW_OP_mod), + read::Operation::Mul => Operation::Simple(constants::DW_OP_mul), + read::Operation::Neg => Operation::Simple(constants::DW_OP_neg), + read::Operation::Not => Operation::Simple(constants::DW_OP_not), + read::Operation::Or => Operation::Simple(constants::DW_OP_or), + read::Operation::Plus => Operation::Simple(constants::DW_OP_plus), + read::Operation::PlusConstant { value } => Operation::PlusConstant(value), + read::Operation::Shl => Operation::Simple(constants::DW_OP_shl), + read::Operation::Shr => Operation::Simple(constants::DW_OP_shr), + read::Operation::Shra => Operation::Simple(constants::DW_OP_shra), + read::Operation::Xor => Operation::Simple(constants::DW_OP_xor), + read::Operation::Eq => Operation::Simple(constants::DW_OP_eq), + read::Operation::Ge => Operation::Simple(constants::DW_OP_ge), + read::Operation::Gt => Operation::Simple(constants::DW_OP_gt), + read::Operation::Le => Operation::Simple(constants::DW_OP_le), + read::Operation::Lt => Operation::Simple(constants::DW_OP_lt), + read::Operation::Ne => Operation::Simple(constants::DW_OP_ne), + read::Operation::Bra { target } => { + let offset = from_operations + .offset_from(&from_expression) + .wrapping_add(i64::from(target) as usize); + let index = offsets + .binary_search(&offset) + .map_err(|_| ConvertError::InvalidBranchTarget)?; + Operation::Branch(index) + } + read::Operation::Skip { target } => { + let offset = from_operations + .offset_from(&from_expression) + .wrapping_add(i64::from(target) as usize); + let index = offsets + .binary_search(&offset) + .map_err(|_| ConvertError::InvalidBranchTarget)?; + Operation::Skip(index) + } + read::Operation::UnsignedConstant { value } => { + Operation::UnsignedConstant(value) + } + read::Operation::SignedConstant { value } => Operation::SignedConstant(value), + read::Operation::Register { register } => Operation::Register(register), + read::Operation::RegisterOffset { + register, + offset, + base_type, + } => { + if base_type.0 != 0 { + Operation::RegisterType(register, convert_unit_offset(base_type)?) + } else { + Operation::RegisterOffset(register, offset) + } + } + read::Operation::FrameOffset { offset } => Operation::FrameOffset(offset), + read::Operation::Nop => Operation::Simple(constants::DW_OP_nop), + read::Operation::PushObjectAddress => { + Operation::Simple(constants::DW_OP_push_object_address) + } + read::Operation::Call { offset } => match offset { + read::DieReference::UnitRef(offset) => { + Operation::Call(convert_unit_offset(offset)?) + } + read::DieReference::DebugInfoRef(offset) => { + Operation::CallRef(convert_debug_info_offset(offset)?) + } + }, + read::Operation::TLS => Operation::Simple(constants::DW_OP_form_tls_address), + read::Operation::CallFrameCFA => { + Operation::Simple(constants::DW_OP_call_frame_cfa) + } + read::Operation::Piece { + size_in_bits, + bit_offset: None, + } => Operation::Piece { + size_in_bytes: size_in_bits / 8, + }, + read::Operation::Piece { + size_in_bits, + bit_offset: Some(bit_offset), + } => Operation::BitPiece { + size_in_bits, + bit_offset, + }, + read::Operation::ImplicitValue { data } => { + Operation::ImplicitValue(data.to_slice()?.into_owned().into()) + } + read::Operation::StackValue => Operation::Simple(constants::DW_OP_stack_value), + read::Operation::ImplicitPointer { value, byte_offset } => { + let entry = convert_debug_info_offset(value)?; + Operation::ImplicitPointer { entry, byte_offset } + } + read::Operation::EntryValue { expression } => { + let expression = Expression::from( + read::Expression(expression), + encoding, + dwarf, + unit, + entry_ids, + convert_address, + )?; + Operation::EntryValue(expression) + } + read::Operation::ParameterRef { offset } => { + let entry = convert_unit_offset(offset)?; + Operation::ParameterRef(entry) + } + read::Operation::Address { address } => { + let address = + convert_address(address).ok_or(ConvertError::InvalidAddress)?; + Operation::Address(address) + } + read::Operation::AddressIndex { index } => { + let dwarf = dwarf.ok_or(ConvertError::UnsupportedOperation)?; + let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; + let val = dwarf.address(unit, index)?; + let address = convert_address(val).ok_or(ConvertError::InvalidAddress)?; + Operation::Address(address) + } + read::Operation::ConstantIndex { index } => { + let dwarf = dwarf.ok_or(ConvertError::UnsupportedOperation)?; + let unit = unit.ok_or(ConvertError::UnsupportedOperation)?; + let val = dwarf.address(unit, index)?; + Operation::UnsignedConstant(val) + } + read::Operation::TypedLiteral { base_type, value } => { + let entry = convert_unit_offset(base_type)?; + Operation::ConstantType(entry, value.to_slice()?.into_owned().into()) + } + read::Operation::Convert { base_type } => { + if base_type.0 == 0 { + Operation::Convert(None) + } else { + let entry = convert_unit_offset(base_type)?; + Operation::Convert(Some(entry)) + } + } + read::Operation::Reinterpret { base_type } => { + if base_type.0 == 0 { + Operation::Reinterpret(None) + } else { + let entry = convert_unit_offset(base_type)?; + Operation::Reinterpret(Some(entry)) + } + } + read::Operation::WasmLocal { index } => Operation::WasmLocal(index), + read::Operation::WasmGlobal { index } => Operation::WasmGlobal(index), + read::Operation::WasmStack { index } => Operation::WasmStack(index), + }; + operations.push(operation); + } + Ok(Expression { operations }) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::common::{ + DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase, + DebugStrOffsetsBase, Format, SectionId, + }; + use crate::read; + use crate::write::{ + DebugLineStrOffsets, DebugStrOffsets, EndianVec, LineProgram, Sections, Unit, UnitTable, + }; + use crate::LittleEndian; + use std::collections::HashMap; + + #[test] + fn test_operation() { + for &version in &[3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let mut units = UnitTable::default(); + let unit_id = units.add(Unit::new(encoding, LineProgram::none())); + let unit = units.get_mut(unit_id); + let entry_id = unit.add(unit.root(), constants::DW_TAG_base_type); + let reference = Reference::Entry(unit_id, entry_id); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + let debug_info_offsets = units + .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets) + .unwrap(); + let unit_offsets = debug_info_offsets.unit_offsets(unit_id); + let debug_info_offset = unit_offsets.debug_info_offset(entry_id); + let entry_offset = + read::UnitOffset(unit_offsets.unit_offset(entry_id) as usize); + + let mut reg_expression = Expression::new(); + reg_expression.op_reg(Register(23)); + + let operations: &[(&dyn Fn(&mut Expression), Operation, read::Operation<_>)] = + &[ + ( + &|x| x.op_deref(), + Operation::Deref { space: false }, + read::Operation::Deref { + base_type: read::UnitOffset(0), + size: address_size, + space: false, + }, + ), + ( + &|x| x.op_xderef(), + Operation::Deref { space: true }, + read::Operation::Deref { + base_type: read::UnitOffset(0), + size: address_size, + space: true, + }, + ), + ( + &|x| x.op_deref_size(2), + Operation::DerefSize { + space: false, + size: 2, + }, + read::Operation::Deref { + base_type: read::UnitOffset(0), + size: 2, + space: false, + }, + ), + ( + &|x| x.op_xderef_size(2), + Operation::DerefSize { + space: true, + size: 2, + }, + read::Operation::Deref { + base_type: read::UnitOffset(0), + size: 2, + space: true, + }, + ), + ( + &|x| x.op_deref_type(2, entry_id), + Operation::DerefType { + space: false, + size: 2, + base: entry_id, + }, + read::Operation::Deref { + base_type: entry_offset, + size: 2, + space: false, + }, + ), + ( + &|x| x.op_xderef_type(2, entry_id), + Operation::DerefType { + space: true, + size: 2, + base: entry_id, + }, + read::Operation::Deref { + base_type: entry_offset, + size: 2, + space: true, + }, + ), + ( + &|x| x.op(constants::DW_OP_drop), + Operation::Simple(constants::DW_OP_drop), + read::Operation::Drop, + ), + ( + &|x| x.op_pick(0), + Operation::Pick(0), + read::Operation::Pick { index: 0 }, + ), + ( + &|x| x.op_pick(1), + Operation::Pick(1), + read::Operation::Pick { index: 1 }, + ), + ( + &|x| x.op_pick(2), + Operation::Pick(2), + read::Operation::Pick { index: 2 }, + ), + ( + &|x| x.op(constants::DW_OP_swap), + Operation::Simple(constants::DW_OP_swap), + read::Operation::Swap, + ), + ( + &|x| x.op(constants::DW_OP_rot), + Operation::Simple(constants::DW_OP_rot), + read::Operation::Rot, + ), + ( + &|x| x.op(constants::DW_OP_abs), + Operation::Simple(constants::DW_OP_abs), + read::Operation::Abs, + ), + ( + &|x| x.op(constants::DW_OP_and), + Operation::Simple(constants::DW_OP_and), + read::Operation::And, + ), + ( + &|x| x.op(constants::DW_OP_div), + Operation::Simple(constants::DW_OP_div), + read::Operation::Div, + ), + ( + &|x| x.op(constants::DW_OP_minus), + Operation::Simple(constants::DW_OP_minus), + read::Operation::Minus, + ), + ( + &|x| x.op(constants::DW_OP_mod), + Operation::Simple(constants::DW_OP_mod), + read::Operation::Mod, + ), + ( + &|x| x.op(constants::DW_OP_mul), + Operation::Simple(constants::DW_OP_mul), + read::Operation::Mul, + ), + ( + &|x| x.op(constants::DW_OP_neg), + Operation::Simple(constants::DW_OP_neg), + read::Operation::Neg, + ), + ( + &|x| x.op(constants::DW_OP_not), + Operation::Simple(constants::DW_OP_not), + read::Operation::Not, + ), + ( + &|x| x.op(constants::DW_OP_or), + Operation::Simple(constants::DW_OP_or), + read::Operation::Or, + ), + ( + &|x| x.op(constants::DW_OP_plus), + Operation::Simple(constants::DW_OP_plus), + read::Operation::Plus, + ), + ( + &|x| x.op_plus_uconst(23), + Operation::PlusConstant(23), + read::Operation::PlusConstant { value: 23 }, + ), + ( + &|x| x.op(constants::DW_OP_shl), + Operation::Simple(constants::DW_OP_shl), + read::Operation::Shl, + ), + ( + &|x| x.op(constants::DW_OP_shr), + Operation::Simple(constants::DW_OP_shr), + read::Operation::Shr, + ), + ( + &|x| x.op(constants::DW_OP_shra), + Operation::Simple(constants::DW_OP_shra), + read::Operation::Shra, + ), + ( + &|x| x.op(constants::DW_OP_xor), + Operation::Simple(constants::DW_OP_xor), + read::Operation::Xor, + ), + ( + &|x| x.op(constants::DW_OP_eq), + Operation::Simple(constants::DW_OP_eq), + read::Operation::Eq, + ), + ( + &|x| x.op(constants::DW_OP_ge), + Operation::Simple(constants::DW_OP_ge), + read::Operation::Ge, + ), + ( + &|x| x.op(constants::DW_OP_gt), + Operation::Simple(constants::DW_OP_gt), + read::Operation::Gt, + ), + ( + &|x| x.op(constants::DW_OP_le), + Operation::Simple(constants::DW_OP_le), + read::Operation::Le, + ), + ( + &|x| x.op(constants::DW_OP_lt), + Operation::Simple(constants::DW_OP_lt), + read::Operation::Lt, + ), + ( + &|x| x.op(constants::DW_OP_ne), + Operation::Simple(constants::DW_OP_ne), + read::Operation::Ne, + ), + ( + &|x| x.op_constu(23), + Operation::UnsignedConstant(23), + read::Operation::UnsignedConstant { value: 23 }, + ), + ( + &|x| x.op_consts(-23), + Operation::SignedConstant(-23), + read::Operation::SignedConstant { value: -23 }, + ), + ( + &|x| x.op_reg(Register(23)), + Operation::Register(Register(23)), + read::Operation::Register { + register: Register(23), + }, + ), + ( + &|x| x.op_reg(Register(123)), + Operation::Register(Register(123)), + read::Operation::Register { + register: Register(123), + }, + ), + ( + &|x| x.op_breg(Register(23), 34), + Operation::RegisterOffset(Register(23), 34), + read::Operation::RegisterOffset { + register: Register(23), + offset: 34, + base_type: read::UnitOffset(0), + }, + ), + ( + &|x| x.op_breg(Register(123), 34), + Operation::RegisterOffset(Register(123), 34), + read::Operation::RegisterOffset { + register: Register(123), + offset: 34, + base_type: read::UnitOffset(0), + }, + ), + ( + &|x| x.op_regval_type(Register(23), entry_id), + Operation::RegisterType(Register(23), entry_id), + read::Operation::RegisterOffset { + register: Register(23), + offset: 0, + base_type: entry_offset, + }, + ), + ( + &|x| x.op_fbreg(34), + Operation::FrameOffset(34), + read::Operation::FrameOffset { offset: 34 }, + ), + ( + &|x| x.op(constants::DW_OP_nop), + Operation::Simple(constants::DW_OP_nop), + read::Operation::Nop, + ), + ( + &|x| x.op(constants::DW_OP_push_object_address), + Operation::Simple(constants::DW_OP_push_object_address), + read::Operation::PushObjectAddress, + ), + ( + &|x| x.op_call(entry_id), + Operation::Call(entry_id), + read::Operation::Call { + offset: read::DieReference::UnitRef(entry_offset), + }, + ), + ( + &|x| x.op_call_ref(reference), + Operation::CallRef(reference), + read::Operation::Call { + offset: read::DieReference::DebugInfoRef(debug_info_offset), + }, + ), + ( + &|x| x.op(constants::DW_OP_form_tls_address), + Operation::Simple(constants::DW_OP_form_tls_address), + read::Operation::TLS, + ), + ( + &|x| x.op(constants::DW_OP_call_frame_cfa), + Operation::Simple(constants::DW_OP_call_frame_cfa), + read::Operation::CallFrameCFA, + ), + ( + &|x| x.op_piece(23), + Operation::Piece { size_in_bytes: 23 }, + read::Operation::Piece { + size_in_bits: 23 * 8, + bit_offset: None, + }, + ), + ( + &|x| x.op_bit_piece(23, 34), + Operation::BitPiece { + size_in_bits: 23, + bit_offset: 34, + }, + read::Operation::Piece { + size_in_bits: 23, + bit_offset: Some(34), + }, + ), + ( + &|x| x.op_implicit_value(vec![23].into()), + Operation::ImplicitValue(vec![23].into()), + read::Operation::ImplicitValue { + data: read::EndianSlice::new(&[23], LittleEndian), + }, + ), + ( + &|x| x.op(constants::DW_OP_stack_value), + Operation::Simple(constants::DW_OP_stack_value), + read::Operation::StackValue, + ), + ( + &|x| x.op_implicit_pointer(reference, 23), + Operation::ImplicitPointer { + entry: reference, + byte_offset: 23, + }, + read::Operation::ImplicitPointer { + value: debug_info_offset, + byte_offset: 23, + }, + ), + ( + &|x| x.op_entry_value(reg_expression.clone()), + Operation::EntryValue(reg_expression.clone()), + read::Operation::EntryValue { + expression: read::EndianSlice::new( + &[constants::DW_OP_reg23.0], + LittleEndian, + ), + }, + ), + ( + &|x| x.op_gnu_parameter_ref(entry_id), + Operation::ParameterRef(entry_id), + read::Operation::ParameterRef { + offset: entry_offset, + }, + ), + ( + &|x| x.op_addr(Address::Constant(23)), + Operation::Address(Address::Constant(23)), + read::Operation::Address { address: 23 }, + ), + ( + &|x| x.op_const_type(entry_id, vec![23].into()), + Operation::ConstantType(entry_id, vec![23].into()), + read::Operation::TypedLiteral { + base_type: entry_offset, + value: read::EndianSlice::new(&[23], LittleEndian), + }, + ), + ( + &|x| x.op_convert(None), + Operation::Convert(None), + read::Operation::Convert { + base_type: read::UnitOffset(0), + }, + ), + ( + &|x| x.op_convert(Some(entry_id)), + Operation::Convert(Some(entry_id)), + read::Operation::Convert { + base_type: entry_offset, + }, + ), + ( + &|x| x.op_reinterpret(None), + Operation::Reinterpret(None), + read::Operation::Reinterpret { + base_type: read::UnitOffset(0), + }, + ), + ( + &|x| x.op_reinterpret(Some(entry_id)), + Operation::Reinterpret(Some(entry_id)), + read::Operation::Reinterpret { + base_type: entry_offset, + }, + ), + ( + &|x| x.op_wasm_local(1000), + Operation::WasmLocal(1000), + read::Operation::WasmLocal { index: 1000 }, + ), + ( + &|x| x.op_wasm_global(1000), + Operation::WasmGlobal(1000), + read::Operation::WasmGlobal { index: 1000 }, + ), + ( + &|x| x.op_wasm_stack(1000), + Operation::WasmStack(1000), + read::Operation::WasmStack { index: 1000 }, + ), + ]; + + let mut expression = Expression::new(); + let start_index = expression.next_index(); + for (f, o, _) in operations { + f(&mut expression); + assert_eq!(expression.operations.last(), Some(o)); + } + + let bra_index = expression.op_bra(); + let skip_index = expression.op_skip(); + expression.op(constants::DW_OP_nop); + let end_index = expression.next_index(); + expression.set_target(bra_index, start_index); + expression.set_target(skip_index, end_index); + + let mut w = EndianVec::new(LittleEndian); + let mut refs = Vec::new(); + expression + .write(&mut w, Some(&mut refs), encoding, Some(&unit_offsets)) + .unwrap(); + for r in &refs { + assert_eq!(r.unit, unit_id); + assert_eq!(r.entry, entry_id); + w.write_offset_at( + r.offset, + debug_info_offset.0, + SectionId::DebugInfo, + r.size, + ) + .unwrap(); + } + + let read_expression = + read::Expression(read::EndianSlice::new(w.slice(), LittleEndian)); + let mut read_operations = read_expression.operations(encoding); + for (_, _, operation) in operations { + assert_eq!(read_operations.next(), Ok(Some(*operation))); + } + + // 4 = DW_OP_skip + i16 + DW_OP_nop + assert_eq!( + read_operations.next(), + Ok(Some(read::Operation::Bra { + target: -(w.len() as i16) + 4 + })) + ); + // 1 = DW_OP_nop + assert_eq!( + read_operations.next(), + Ok(Some(read::Operation::Skip { target: 1 })) + ); + assert_eq!(read_operations.next(), Ok(Some(read::Operation::Nop))); + assert_eq!(read_operations.next(), Ok(None)); + + // Fake the unit. + let unit = read::Unit { + header: read::UnitHeader::new( + encoding, + 0, + read::UnitType::Compilation, + DebugAbbrevOffset(0), + DebugInfoOffset(0).into(), + read::EndianSlice::new(&[], LittleEndian), + ), + abbreviations: read::Abbreviations::default(), + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase(0), + addr_base: DebugAddrBase(0), + loclists_base: DebugLocListsBase(0), + rnglists_base: DebugRngListsBase(0), + line_program: None, + dwo_id: None, + }; + + let mut entry_ids = HashMap::new(); + entry_ids.insert(debug_info_offset.into(), (unit_id, entry_id)); + let convert_expression = Expression::from( + read_expression, + encoding, + None, /* dwarf */ + Some(&unit), + Some(&entry_ids), + &|address| Some(Address::Constant(address)), + ) + .unwrap(); + let mut convert_operations = convert_expression.operations.iter(); + for (_, operation, _) in operations { + assert_eq!(convert_operations.next(), Some(operation)); + } + assert_eq!( + convert_operations.next(), + Some(&Operation::Branch(start_index)) + ); + assert_eq!(convert_operations.next(), Some(&Operation::Skip(end_index))); + assert_eq!( + convert_operations.next(), + Some(&Operation::Simple(constants::DW_OP_nop)) + ); + } + } + } + } +} diff --git a/vendor/gimli/src/write/range.rs b/vendor/gimli/src/write/range.rs new file mode 100644 index 000000000..b44ce1b7b --- /dev/null +++ b/vendor/gimli/src/write/range.rs @@ -0,0 +1,415 @@ +use alloc::vec::Vec; +use indexmap::IndexSet; +use std::ops::{Deref, DerefMut}; + +use crate::common::{Encoding, RangeListsOffset, SectionId}; +use crate::write::{Address, BaseId, Error, Result, Section, Sections, Writer}; + +define_section!( + DebugRanges, + RangeListsOffset, + "A writable `.debug_ranges` section." +); +define_section!( + DebugRngLists, + RangeListsOffset, + "A writable `.debug_rnglists` section." +); + +define_offsets!( + RangeListOffsets: RangeListId => RangeListsOffset, + "The section offsets of a series of range lists within the `.debug_ranges` or `.debug_rnglists` sections." +); + +define_id!( + RangeListId, + "An identifier for a range list in a `RangeListTable`." +); + +/// A table of range lists that will be stored in a `.debug_ranges` or `.debug_rnglists` section. +#[derive(Debug, Default)] +pub struct RangeListTable { + base_id: BaseId, + ranges: IndexSet<RangeList>, +} + +impl RangeListTable { + /// Add a range list to the table. + pub fn add(&mut self, range_list: RangeList) -> RangeListId { + let (index, _) = self.ranges.insert_full(range_list); + RangeListId::new(self.base_id, index) + } + + /// Write the range list table to the appropriate section for the given DWARF version. + pub(crate) fn write<W: Writer>( + &self, + sections: &mut Sections<W>, + encoding: Encoding, + ) -> Result<RangeListOffsets> { + if self.ranges.is_empty() { + return Ok(RangeListOffsets::none()); + } + + match encoding.version { + 2..=4 => self.write_ranges(&mut sections.debug_ranges, encoding.address_size), + 5 => self.write_rnglists(&mut sections.debug_rnglists, encoding), + _ => Err(Error::UnsupportedVersion(encoding.version)), + } + } + + /// Write the range list table to the `.debug_ranges` section. + fn write_ranges<W: Writer>( + &self, + w: &mut DebugRanges<W>, + address_size: u8, + ) -> Result<RangeListOffsets> { + let mut offsets = Vec::new(); + for range_list in self.ranges.iter() { + offsets.push(w.offset()); + for range in &range_list.0 { + // Note that we must ensure none of the ranges have both begin == 0 and end == 0. + // We do this by ensuring that begin != end, which is a bit more restrictive + // than required, but still seems reasonable. + match *range { + Range::BaseAddress { address } => { + let marker = !0 >> (64 - address_size * 8); + w.write_udata(marker, address_size)?; + w.write_address(address, address_size)?; + } + Range::OffsetPair { begin, end } => { + if begin == end { + return Err(Error::InvalidRange); + } + w.write_udata(begin, address_size)?; + w.write_udata(end, address_size)?; + } + Range::StartEnd { begin, end } => { + if begin == end { + return Err(Error::InvalidRange); + } + w.write_address(begin, address_size)?; + w.write_address(end, address_size)?; + } + Range::StartLength { begin, length } => { + let end = match begin { + Address::Constant(begin) => Address::Constant(begin + length), + Address::Symbol { symbol, addend } => Address::Symbol { + symbol, + addend: addend + length as i64, + }, + }; + if begin == end { + return Err(Error::InvalidRange); + } + w.write_address(begin, address_size)?; + w.write_address(end, address_size)?; + } + } + } + w.write_udata(0, address_size)?; + w.write_udata(0, address_size)?; + } + Ok(RangeListOffsets { + base_id: self.base_id, + offsets, + }) + } + + /// Write the range list table to the `.debug_rnglists` section. + fn write_rnglists<W: Writer>( + &self, + w: &mut DebugRngLists<W>, + encoding: Encoding, + ) -> Result<RangeListOffsets> { + let mut offsets = Vec::new(); + + if encoding.version != 5 { + return Err(Error::NeedVersion(5)); + } + + let length_offset = w.write_initial_length(encoding.format)?; + let length_base = w.len(); + + w.write_u16(encoding.version)?; + w.write_u8(encoding.address_size)?; + w.write_u8(0)?; // segment_selector_size + w.write_u32(0)?; // offset_entry_count (when set to zero DW_FORM_rnglistx can't be used, see section 7.28) + // FIXME implement DW_FORM_rnglistx writing and implement the offset entry list + + for range_list in self.ranges.iter() { + offsets.push(w.offset()); + for range in &range_list.0 { + match *range { + Range::BaseAddress { address } => { + w.write_u8(crate::constants::DW_RLE_base_address.0)?; + w.write_address(address, encoding.address_size)?; + } + Range::OffsetPair { begin, end } => { + w.write_u8(crate::constants::DW_RLE_offset_pair.0)?; + w.write_uleb128(begin)?; + w.write_uleb128(end)?; + } + Range::StartEnd { begin, end } => { + w.write_u8(crate::constants::DW_RLE_start_end.0)?; + w.write_address(begin, encoding.address_size)?; + w.write_address(end, encoding.address_size)?; + } + Range::StartLength { begin, length } => { + w.write_u8(crate::constants::DW_RLE_start_length.0)?; + w.write_address(begin, encoding.address_size)?; + w.write_uleb128(length)?; + } + } + } + + w.write_u8(crate::constants::DW_RLE_end_of_list.0)?; + } + + let length = (w.len() - length_base) as u64; + w.write_initial_length_at(length_offset, length, encoding.format)?; + + Ok(RangeListOffsets { + base_id: self.base_id, + offsets, + }) + } +} + +/// A range list that will be stored in a `.debug_ranges` or `.debug_rnglists` section. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct RangeList(pub Vec<Range>); + +/// A single range. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum Range { + /// DW_RLE_base_address + BaseAddress { + /// Base address. + address: Address, + }, + /// DW_RLE_offset_pair + OffsetPair { + /// Start of range relative to base address. + begin: u64, + /// End of range relative to base address. + end: u64, + }, + /// DW_RLE_start_end + StartEnd { + /// Start of range. + begin: Address, + /// End of range. + end: Address, + }, + /// DW_RLE_start_length + StartLength { + /// Start of range. + begin: Address, + /// Length of range. + length: u64, + }, +} + +#[cfg(feature = "read")] +mod convert { + use super::*; + + use crate::read::{self, Reader}; + use crate::write::{ConvertError, ConvertResult, ConvertUnitContext}; + + impl RangeList { + /// Create a range list by reading the data from the give range list iter. + pub(crate) fn from<R: Reader<Offset = usize>>( + mut from: read::RawRngListIter<R>, + context: &ConvertUnitContext<R>, + ) -> ConvertResult<Self> { + let mut have_base_address = context.base_address != Address::Constant(0); + let convert_address = + |x| (context.convert_address)(x).ok_or(ConvertError::InvalidAddress); + let mut ranges = Vec::new(); + while let Some(from_range) = from.next()? { + let range = match from_range { + read::RawRngListEntry::AddressOrOffsetPair { begin, end } => { + // These were parsed as addresses, even if they are offsets. + let begin = convert_address(begin)?; + let end = convert_address(end)?; + match (begin, end) { + (Address::Constant(begin_offset), Address::Constant(end_offset)) => { + if have_base_address { + Range::OffsetPair { + begin: begin_offset, + end: end_offset, + } + } else { + Range::StartEnd { begin, end } + } + } + _ => { + if have_base_address { + // At least one of begin/end is an address, but we also have + // a base address. Adding addresses is undefined. + return Err(ConvertError::InvalidRangeRelativeAddress); + } + Range::StartEnd { begin, end } + } + } + } + read::RawRngListEntry::BaseAddress { addr } => { + have_base_address = true; + let address = convert_address(addr)?; + Range::BaseAddress { address } + } + read::RawRngListEntry::BaseAddressx { addr } => { + have_base_address = true; + let address = convert_address(context.dwarf.address(context.unit, addr)?)?; + Range::BaseAddress { address } + } + read::RawRngListEntry::StartxEndx { begin, end } => { + let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; + let end = convert_address(context.dwarf.address(context.unit, end)?)?; + Range::StartEnd { begin, end } + } + read::RawRngListEntry::StartxLength { begin, length } => { + let begin = convert_address(context.dwarf.address(context.unit, begin)?)?; + Range::StartLength { begin, length } + } + read::RawRngListEntry::OffsetPair { begin, end } => { + Range::OffsetPair { begin, end } + } + read::RawRngListEntry::StartEnd { begin, end } => { + let begin = convert_address(begin)?; + let end = convert_address(end)?; + Range::StartEnd { begin, end } + } + read::RawRngListEntry::StartLength { begin, length } => { + let begin = convert_address(begin)?; + Range::StartLength { begin, length } + } + }; + // Filtering empty ranges out. + match range { + Range::StartLength { length, .. } if length == 0 => continue, + Range::StartEnd { begin, end, .. } if begin == end => continue, + Range::OffsetPair { begin, end, .. } if begin == end => continue, + _ => (), + } + ranges.push(range); + } + Ok(RangeList(ranges)) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::common::{ + DebugAbbrevOffset, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DebugRngListsBase, + DebugStrOffsetsBase, Format, + }; + use crate::read; + use crate::write::{ + ConvertUnitContext, EndianVec, LineStringTable, LocationListTable, Range, RangeListTable, + StringTable, + }; + use crate::LittleEndian; + use std::collections::HashMap; + + #[test] + fn test_range() { + let mut line_strings = LineStringTable::default(); + let mut strings = StringTable::default(); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let mut range_list = RangeList(vec![ + Range::StartLength { + begin: Address::Constant(6666), + length: 7777, + }, + Range::StartEnd { + begin: Address::Constant(4444), + end: Address::Constant(5555), + }, + Range::BaseAddress { + address: Address::Constant(1111), + }, + Range::OffsetPair { + begin: 2222, + end: 3333, + }, + ]); + + let mut ranges = RangeListTable::default(); + let range_list_id = ranges.add(range_list.clone()); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let range_list_offsets = ranges.write(&mut sections, encoding).unwrap(); + + let read_debug_ranges = + read::DebugRanges::new(sections.debug_ranges.slice(), LittleEndian); + let read_debug_rnglists = + read::DebugRngLists::new(sections.debug_rnglists.slice(), LittleEndian); + let read_ranges = read::RangeLists::new(read_debug_ranges, read_debug_rnglists); + let offset = range_list_offsets.get(range_list_id); + let read_range_list = read_ranges.raw_ranges(offset, encoding).unwrap(); + + let dwarf = read::Dwarf { + ranges: read_ranges, + ..Default::default() + }; + let unit = read::Unit { + header: read::UnitHeader::new( + encoding, + 0, + read::UnitType::Compilation, + DebugAbbrevOffset(0), + DebugInfoOffset(0).into(), + read::EndianSlice::default(), + ), + abbreviations: read::Abbreviations::default(), + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase(0), + addr_base: DebugAddrBase(0), + loclists_base: DebugLocListsBase(0), + rnglists_base: DebugRngListsBase(0), + line_program: None, + dwo_id: None, + }; + let context = ConvertUnitContext { + dwarf: &dwarf, + unit: &unit, + line_strings: &mut line_strings, + strings: &mut strings, + ranges: &mut ranges, + locations: &mut LocationListTable::default(), + convert_address: &|address| Some(Address::Constant(address)), + base_address: Address::Constant(0), + line_program_offset: None, + line_program_files: Vec::new(), + entry_ids: &HashMap::new(), + }; + let convert_range_list = RangeList::from(read_range_list, &context).unwrap(); + + if version <= 4 { + range_list.0[0] = Range::StartEnd { + begin: Address::Constant(6666), + end: Address::Constant(6666 + 7777), + }; + } + assert_eq!(range_list, convert_range_list); + } + } + } + } +} diff --git a/vendor/gimli/src/write/section.rs b/vendor/gimli/src/write/section.rs new file mode 100644 index 000000000..e8f3378cd --- /dev/null +++ b/vendor/gimli/src/write/section.rs @@ -0,0 +1,172 @@ +use std::ops::DerefMut; +use std::result; +use std::vec::Vec; + +use crate::common::SectionId; +use crate::write::{ + DebugAbbrev, DebugFrame, DebugInfo, DebugInfoReference, DebugLine, DebugLineStr, DebugLoc, + DebugLocLists, DebugRanges, DebugRngLists, DebugStr, EhFrame, Writer, +}; + +macro_rules! define_section { + ($name:ident, $offset:ident, $docs:expr) => { + #[doc=$docs] + #[derive(Debug, Default)] + pub struct $name<W: Writer>(pub W); + + impl<W: Writer> $name<W> { + /// Return the offset of the next write. + pub fn offset(&self) -> $offset { + $offset(self.len()) + } + } + + impl<W: Writer> From<W> for $name<W> { + #[inline] + fn from(w: W) -> Self { + $name(w) + } + } + + impl<W: Writer> Deref for $name<W> { + type Target = W; + + #[inline] + fn deref(&self) -> &W { + &self.0 + } + } + + impl<W: Writer> DerefMut for $name<W> { + #[inline] + fn deref_mut(&mut self) -> &mut W { + &mut self.0 + } + } + + impl<W: Writer> Section<W> for $name<W> { + #[inline] + fn id(&self) -> SectionId { + SectionId::$name + } + } + }; +} + +/// Functionality common to all writable DWARF sections. +pub trait Section<W: Writer>: DerefMut<Target = W> { + /// Returns the DWARF section kind for this type. + fn id(&self) -> SectionId; + + /// Returns the ELF section name for this type. + fn name(&self) -> &'static str { + self.id().name() + } +} + +/// All of the writable DWARF sections. +#[derive(Debug, Default)] +pub struct Sections<W: Writer> { + /// The `.debug_abbrev` section. + pub debug_abbrev: DebugAbbrev<W>, + /// The `.debug_info` section. + pub debug_info: DebugInfo<W>, + /// The `.debug_line` section. + pub debug_line: DebugLine<W>, + /// The `.debug_line_str` section. + pub debug_line_str: DebugLineStr<W>, + /// The `.debug_ranges` section. + pub debug_ranges: DebugRanges<W>, + /// The `.debug_rnglists` section. + pub debug_rnglists: DebugRngLists<W>, + /// The `.debug_loc` section. + pub debug_loc: DebugLoc<W>, + /// The `.debug_loclists` section. + pub debug_loclists: DebugLocLists<W>, + /// The `.debug_str` section. + pub debug_str: DebugStr<W>, + /// The `.debug_frame` section. + pub debug_frame: DebugFrame<W>, + /// The `.eh_frame` section. + pub eh_frame: EhFrame<W>, + /// Unresolved references in the `.debug_info` section. + pub(crate) debug_info_refs: Vec<DebugInfoReference>, + /// Unresolved references in the `.debug_loc` section. + pub(crate) debug_loc_refs: Vec<DebugInfoReference>, + /// Unresolved references in the `.debug_loclists` section. + pub(crate) debug_loclists_refs: Vec<DebugInfoReference>, +} + +impl<W: Writer + Clone> Sections<W> { + /// Create a new `Sections` using clones of the given `section`. + pub fn new(section: W) -> Self { + Sections { + debug_abbrev: DebugAbbrev(section.clone()), + debug_info: DebugInfo(section.clone()), + debug_line: DebugLine(section.clone()), + debug_line_str: DebugLineStr(section.clone()), + debug_ranges: DebugRanges(section.clone()), + debug_rnglists: DebugRngLists(section.clone()), + debug_loc: DebugLoc(section.clone()), + debug_loclists: DebugLocLists(section.clone()), + debug_str: DebugStr(section.clone()), + debug_frame: DebugFrame(section.clone()), + eh_frame: EhFrame(section.clone()), + debug_info_refs: Vec::new(), + debug_loc_refs: Vec::new(), + debug_loclists_refs: Vec::new(), + } + } +} + +impl<W: Writer> Sections<W> { + /// For each section, call `f` once with a shared reference. + pub fn for_each<F, E>(&self, mut f: F) -> result::Result<(), E> + where + F: FnMut(SectionId, &W) -> result::Result<(), E>, + { + macro_rules! f { + ($s:expr) => { + f($s.id(), &$s) + }; + } + // Ordered so that earlier sections do not reference later sections. + f!(self.debug_abbrev)?; + f!(self.debug_str)?; + f!(self.debug_line_str)?; + f!(self.debug_line)?; + f!(self.debug_ranges)?; + f!(self.debug_rnglists)?; + f!(self.debug_loc)?; + f!(self.debug_loclists)?; + f!(self.debug_info)?; + f!(self.debug_frame)?; + f!(self.eh_frame)?; + Ok(()) + } + + /// For each section, call `f` once with a mutable reference. + pub fn for_each_mut<F, E>(&mut self, mut f: F) -> result::Result<(), E> + where + F: FnMut(SectionId, &mut W) -> result::Result<(), E>, + { + macro_rules! f { + ($s:expr) => { + f($s.id(), &mut $s) + }; + } + // Ordered so that earlier sections do not reference later sections. + f!(self.debug_abbrev)?; + f!(self.debug_str)?; + f!(self.debug_line_str)?; + f!(self.debug_line)?; + f!(self.debug_ranges)?; + f!(self.debug_rnglists)?; + f!(self.debug_loc)?; + f!(self.debug_loclists)?; + f!(self.debug_info)?; + f!(self.debug_frame)?; + f!(self.eh_frame)?; + Ok(()) + } +} diff --git a/vendor/gimli/src/write/str.rs b/vendor/gimli/src/write/str.rs new file mode 100644 index 000000000..83285c035 --- /dev/null +++ b/vendor/gimli/src/write/str.rs @@ -0,0 +1,172 @@ +use alloc::vec::Vec; +use indexmap::IndexSet; +use std::ops::{Deref, DerefMut}; + +use crate::common::{DebugLineStrOffset, DebugStrOffset, SectionId}; +use crate::write::{BaseId, Result, Section, Writer}; + +// Requirements: +// - values are `[u8]`, null bytes are not allowed +// - insertion returns a fixed id +// - inserting a duplicate returns the id of the existing value +// - able to convert an id to a section offset +// Optional? +// - able to get an existing value given an id +// +// Limitations of current implementation (using IndexSet): +// - inserting requires either an allocation for duplicates, +// or a double lookup for non-duplicates +// - doesn't preserve offsets when updating an existing `.debug_str` section +// +// Possible changes: +// - calculate offsets as we add values, and use that as the id. +// This would avoid the need for DebugStrOffsets but would make it +// hard to implement `get`. +macro_rules! define_string_table { + ($name:ident, $id:ident, $section:ident, $offsets:ident, $docs:expr) => { + #[doc=$docs] + #[derive(Debug, Default)] + pub struct $name { + base_id: BaseId, + strings: IndexSet<Vec<u8>>, + } + + impl $name { + /// Add a string to the string table and return its id. + /// + /// If the string already exists, then return the id of the existing string. + /// + /// # Panics + /// + /// Panics if `bytes` contains a null byte. + pub fn add<T>(&mut self, bytes: T) -> $id + where + T: Into<Vec<u8>>, + { + let bytes = bytes.into(); + assert!(!bytes.contains(&0)); + let (index, _) = self.strings.insert_full(bytes); + $id::new(self.base_id, index) + } + + /// Return the number of strings in the table. + #[inline] + pub fn count(&self) -> usize { + self.strings.len() + } + + /// Get a reference to a string in the table. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + pub fn get(&self, id: $id) -> &[u8] { + debug_assert_eq!(self.base_id, id.base_id); + self.strings.get_index(id.index).map(Vec::as_slice).unwrap() + } + + /// Write the string table to the `.debug_str` section. + /// + /// Returns the offsets at which the strings are written. + pub fn write<W: Writer>(&self, w: &mut $section<W>) -> Result<$offsets> { + let mut offsets = Vec::new(); + for bytes in self.strings.iter() { + offsets.push(w.offset()); + w.write(bytes)?; + w.write_u8(0)?; + } + + Ok($offsets { + base_id: self.base_id, + offsets, + }) + } + } + }; +} + +define_id!(StringId, "An identifier for a string in a `StringTable`."); + +define_string_table!( + StringTable, + StringId, + DebugStr, + DebugStrOffsets, + "A table of strings that will be stored in a `.debug_str` section." +); + +define_section!(DebugStr, DebugStrOffset, "A writable `.debug_str` section."); + +define_offsets!( + DebugStrOffsets: StringId => DebugStrOffset, + "The section offsets of all strings within a `.debug_str` section." +); + +define_id!( + LineStringId, + "An identifier for a string in a `LineStringTable`." +); + +define_string_table!( + LineStringTable, + LineStringId, + DebugLineStr, + DebugLineStrOffsets, + "A table of strings that will be stored in a `.debug_line_str` section." +); + +define_section!( + DebugLineStr, + DebugLineStrOffset, + "A writable `.debug_line_str` section." +); + +define_offsets!( + DebugLineStrOffsets: LineStringId => DebugLineStrOffset, + "The section offsets of all strings within a `.debug_line_str` section." +); + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::read; + use crate::write::EndianVec; + use crate::LittleEndian; + + #[test] + fn test_string_table() { + let mut strings = StringTable::default(); + assert_eq!(strings.count(), 0); + let id1 = strings.add(&b"one"[..]); + let id2 = strings.add(&b"two"[..]); + assert_eq!(strings.add(&b"one"[..]), id1); + assert_eq!(strings.add(&b"two"[..]), id2); + assert_eq!(strings.get(id1), &b"one"[..]); + assert_eq!(strings.get(id2), &b"two"[..]); + assert_eq!(strings.count(), 2); + + let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian)); + let offsets = strings.write(&mut debug_str).unwrap(); + assert_eq!(debug_str.slice(), b"one\0two\0"); + assert_eq!(offsets.get(id1), DebugStrOffset(0)); + assert_eq!(offsets.get(id2), DebugStrOffset(4)); + assert_eq!(offsets.count(), 2); + } + + #[test] + fn test_string_table_read() { + let mut strings = StringTable::default(); + let id1 = strings.add(&b"one"[..]); + let id2 = strings.add(&b"two"[..]); + + let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian)); + let offsets = strings.write(&mut debug_str).unwrap(); + + let read_debug_str = read::DebugStr::new(debug_str.slice(), LittleEndian); + let str1 = read_debug_str.get_str(offsets.get(id1)).unwrap(); + let str2 = read_debug_str.get_str(offsets.get(id2)).unwrap(); + assert_eq!(str1.slice(), &b"one"[..]); + assert_eq!(str2.slice(), &b"two"[..]); + } +} diff --git a/vendor/gimli/src/write/unit.rs b/vendor/gimli/src/write/unit.rs new file mode 100644 index 000000000..bf85ff421 --- /dev/null +++ b/vendor/gimli/src/write/unit.rs @@ -0,0 +1,3157 @@ +use alloc::vec::Vec; +use std::ops::{Deref, DerefMut}; +use std::{slice, usize}; + +use crate::common::{ + DebugAbbrevOffset, DebugInfoOffset, DebugLineOffset, DebugMacinfoOffset, DebugMacroOffset, + DebugStrOffset, DebugTypeSignature, DwoId, Encoding, Format, SectionId, +}; +use crate::constants; +use crate::leb128::write::{sleb128_size, uleb128_size}; +use crate::write::{ + Abbreviation, AbbreviationTable, Address, AttributeSpecification, BaseId, DebugLineStrOffsets, + DebugStrOffsets, Error, Expression, FileId, LineProgram, LineStringId, LocationListId, + LocationListOffsets, LocationListTable, RangeListId, RangeListOffsets, RangeListTable, + Reference, Result, Section, Sections, StringId, Writer, +}; + +define_id!(UnitId, "An identifier for a unit in a `UnitTable`."); + +define_id!(UnitEntryId, "An identifier for an entry in a `Unit`."); + +/// A table of units that will be stored in the `.debug_info` section. +#[derive(Debug, Default)] +pub struct UnitTable { + base_id: BaseId, + units: Vec<Unit>, +} + +impl UnitTable { + /// Create a new unit and add it to the table. + /// + /// `address_size` must be in bytes. + /// + /// Returns the `UnitId` of the new unit. + #[inline] + pub fn add(&mut self, unit: Unit) -> UnitId { + let id = UnitId::new(self.base_id, self.units.len()); + self.units.push(unit); + id + } + + /// Return the number of units. + #[inline] + pub fn count(&self) -> usize { + self.units.len() + } + + /// Return the id of a unit. + /// + /// # Panics + /// + /// Panics if `index >= self.count()`. + #[inline] + pub fn id(&self, index: usize) -> UnitId { + assert!(index < self.count()); + UnitId::new(self.base_id, index) + } + + /// Get a reference to a unit. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + #[inline] + pub fn get(&self, id: UnitId) -> &Unit { + debug_assert_eq!(self.base_id, id.base_id); + &self.units[id.index] + } + + /// Get a mutable reference to a unit. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + #[inline] + pub fn get_mut(&mut self, id: UnitId) -> &mut Unit { + debug_assert_eq!(self.base_id, id.base_id); + &mut self.units[id.index] + } + + /// Write the units to the given sections. + /// + /// `strings` must contain the `.debug_str` offsets of the corresponding + /// `StringTable`. + pub fn write<W: Writer>( + &mut self, + sections: &mut Sections<W>, + line_strings: &DebugLineStrOffsets, + strings: &DebugStrOffsets, + ) -> Result<DebugInfoOffsets> { + let mut offsets = DebugInfoOffsets { + base_id: self.base_id, + units: Vec::new(), + }; + for unit in &mut self.units { + // TODO: maybe share abbreviation tables + let abbrev_offset = sections.debug_abbrev.offset(); + let mut abbrevs = AbbreviationTable::default(); + + offsets.units.push(unit.write( + sections, + abbrev_offset, + &mut abbrevs, + line_strings, + strings, + )?); + + abbrevs.write(&mut sections.debug_abbrev)?; + } + + write_section_refs( + &mut sections.debug_info_refs, + &mut sections.debug_info.0, + &offsets, + )?; + write_section_refs( + &mut sections.debug_loc_refs, + &mut sections.debug_loc.0, + &offsets, + )?; + write_section_refs( + &mut sections.debug_loclists_refs, + &mut sections.debug_loclists.0, + &offsets, + )?; + + Ok(offsets) + } +} + +fn write_section_refs<W: Writer>( + references: &mut Vec<DebugInfoReference>, + w: &mut W, + offsets: &DebugInfoOffsets, +) -> Result<()> { + for r in references.drain(..) { + let entry_offset = offsets.entry(r.unit, r.entry).0; + debug_assert_ne!(entry_offset, 0); + w.write_offset_at(r.offset, entry_offset, SectionId::DebugInfo, r.size)?; + } + Ok(()) +} + +/// A unit's debugging information. +#[derive(Debug)] +pub struct Unit { + base_id: BaseId, + /// The encoding parameters for this unit. + encoding: Encoding, + /// The line number program for this unit. + pub line_program: LineProgram, + /// A table of range lists used by this unit. + pub ranges: RangeListTable, + /// A table of location lists used by this unit. + pub locations: LocationListTable, + /// All entries in this unit. The order is unrelated to the tree order. + // Requirements: + // - entries form a tree + // - entries can be added in any order + // - entries have a fixed id + // - able to quickly lookup an entry from its id + // Limitations of current implemention: + // - mutable iteration of children is messy due to borrow checker + entries: Vec<DebuggingInformationEntry>, + /// The index of the root entry in entries. + root: UnitEntryId, +} + +impl Unit { + /// Create a new `Unit`. + pub fn new(encoding: Encoding, line_program: LineProgram) -> Self { + let base_id = BaseId::default(); + let ranges = RangeListTable::default(); + let locations = LocationListTable::default(); + let mut entries = Vec::new(); + let root = DebuggingInformationEntry::new( + base_id, + &mut entries, + None, + constants::DW_TAG_compile_unit, + ); + Unit { + base_id, + encoding, + line_program, + ranges, + locations, + entries, + root, + } + } + + /// Return the encoding parameters for this unit. + #[inline] + pub fn encoding(&self) -> Encoding { + self.encoding + } + + /// Return the DWARF version for this unit. + #[inline] + pub fn version(&self) -> u16 { + self.encoding.version + } + + /// Return the address size in bytes for this unit. + #[inline] + pub fn address_size(&self) -> u8 { + self.encoding.address_size + } + + /// Return the DWARF format for this unit. + #[inline] + pub fn format(&self) -> Format { + self.encoding.format + } + + /// Return the number of `DebuggingInformationEntry`s created for this unit. + /// + /// This includes entries that no longer have a parent. + #[inline] + pub fn count(&self) -> usize { + self.entries.len() + } + + /// Return the id of the root entry. + #[inline] + pub fn root(&self) -> UnitEntryId { + self.root + } + + /// Add a new `DebuggingInformationEntry` to this unit and return its id. + /// + /// The `parent` must be within the same unit. + /// + /// # Panics + /// + /// Panics if `parent` is invalid. + #[inline] + pub fn add(&mut self, parent: UnitEntryId, tag: constants::DwTag) -> UnitEntryId { + debug_assert_eq!(self.base_id, parent.base_id); + DebuggingInformationEntry::new(self.base_id, &mut self.entries, Some(parent), tag) + } + + /// Get a reference to an entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + #[inline] + pub fn get(&self, id: UnitEntryId) -> &DebuggingInformationEntry { + debug_assert_eq!(self.base_id, id.base_id); + &self.entries[id.index] + } + + /// Get a mutable reference to an entry. + /// + /// # Panics + /// + /// Panics if `id` is invalid. + #[inline] + pub fn get_mut(&mut self, id: UnitEntryId) -> &mut DebuggingInformationEntry { + debug_assert_eq!(self.base_id, id.base_id); + &mut self.entries[id.index] + } + + /// Return true if `self.line_program` is used by a DIE. + fn line_program_in_use(&self) -> bool { + if self.line_program.is_none() { + return false; + } + if !self.line_program.is_empty() { + return true; + } + + for entry in &self.entries { + for attr in &entry.attrs { + if let AttributeValue::FileIndex(Some(_)) = attr.value { + return true; + } + } + } + + false + } + + /// Write the unit to the given sections. + pub(crate) fn write<W: Writer>( + &mut self, + sections: &mut Sections<W>, + abbrev_offset: DebugAbbrevOffset, + abbrevs: &mut AbbreviationTable, + line_strings: &DebugLineStrOffsets, + strings: &DebugStrOffsets, + ) -> Result<UnitOffsets> { + let line_program = if self.line_program_in_use() { + self.entries[self.root.index] + .set(constants::DW_AT_stmt_list, AttributeValue::LineProgramRef); + Some(self.line_program.write( + &mut sections.debug_line, + self.encoding, + line_strings, + strings, + )?) + } else { + self.entries[self.root.index].delete(constants::DW_AT_stmt_list); + None + }; + + // TODO: use .debug_types for type units in DWARF v4. + let w = &mut sections.debug_info; + + let mut offsets = UnitOffsets { + base_id: self.base_id, + unit: w.offset(), + // Entries can be written in any order, so create the complete vec now. + entries: vec![EntryOffset::none(); self.entries.len()], + }; + + let length_offset = w.write_initial_length(self.format())?; + let length_base = w.len(); + + w.write_u16(self.version())?; + if 2 <= self.version() && self.version() <= 4 { + w.write_offset( + abbrev_offset.0, + SectionId::DebugAbbrev, + self.format().word_size(), + )?; + w.write_u8(self.address_size())?; + } else if self.version() == 5 { + w.write_u8(constants::DW_UT_compile.0)?; + w.write_u8(self.address_size())?; + w.write_offset( + abbrev_offset.0, + SectionId::DebugAbbrev, + self.format().word_size(), + )?; + } else { + return Err(Error::UnsupportedVersion(self.version())); + } + + // Calculate all DIE offsets, so that we are able to output references to them. + // However, references to base types in expressions use ULEB128, so base types + // must be moved to the front before we can calculate offsets. + self.reorder_base_types(); + let mut offset = w.len(); + self.entries[self.root.index].calculate_offsets( + self, + &mut offset, + &mut offsets, + abbrevs, + )?; + + let range_lists = self.ranges.write(sections, self.encoding)?; + // Location lists can't be written until we have DIE offsets. + let loc_lists = self + .locations + .write(sections, self.encoding, Some(&offsets))?; + + let w = &mut sections.debug_info; + let mut unit_refs = Vec::new(); + self.entries[self.root.index].write( + w, + &mut sections.debug_info_refs, + &mut unit_refs, + self, + &mut offsets, + abbrevs, + line_program, + line_strings, + strings, + &range_lists, + &loc_lists, + )?; + + let length = (w.len() - length_base) as u64; + w.write_initial_length_at(length_offset, length, self.format())?; + + for (offset, entry) in unit_refs { + // This does not need relocation. + w.write_udata_at( + offset.0, + offsets.unit_offset(entry), + self.format().word_size(), + )?; + } + + Ok(offsets) + } + + /// Reorder base types to come first so that typed stack operations + /// can get their offset. + fn reorder_base_types(&mut self) { + let root = &self.entries[self.root.index]; + let mut root_children = Vec::with_capacity(root.children.len()); + for entry in &root.children { + if self.entries[entry.index].tag == constants::DW_TAG_base_type { + root_children.push(*entry); + } + } + for entry in &root.children { + if self.entries[entry.index].tag != constants::DW_TAG_base_type { + root_children.push(*entry); + } + } + self.entries[self.root.index].children = root_children; + } +} + +/// A Debugging Information Entry (DIE). +/// +/// DIEs have a set of attributes and optionally have children DIEs as well. +/// +/// DIEs form a tree without any cycles. This is enforced by specifying the +/// parent when creating a DIE, and disallowing changes of parent. +#[derive(Debug)] +pub struct DebuggingInformationEntry { + id: UnitEntryId, + parent: Option<UnitEntryId>, + tag: constants::DwTag, + /// Whether to emit `DW_AT_sibling`. + sibling: bool, + attrs: Vec<Attribute>, + children: Vec<UnitEntryId>, +} + +impl DebuggingInformationEntry { + /// Create a new `DebuggingInformationEntry`. + /// + /// # Panics + /// + /// Panics if `parent` is invalid. + #[allow(clippy::new_ret_no_self)] + fn new( + base_id: BaseId, + entries: &mut Vec<DebuggingInformationEntry>, + parent: Option<UnitEntryId>, + tag: constants::DwTag, + ) -> UnitEntryId { + let id = UnitEntryId::new(base_id, entries.len()); + entries.push(DebuggingInformationEntry { + id, + parent, + tag, + sibling: false, + attrs: Vec::new(), + children: Vec::new(), + }); + if let Some(parent) = parent { + debug_assert_eq!(base_id, parent.base_id); + assert_ne!(parent, id); + entries[parent.index].children.push(id); + } + id + } + + /// Return the id of this entry. + #[inline] + pub fn id(&self) -> UnitEntryId { + self.id + } + + /// Return the parent of this entry. + #[inline] + pub fn parent(&self) -> Option<UnitEntryId> { + self.parent + } + + /// Return the tag of this entry. + #[inline] + pub fn tag(&self) -> constants::DwTag { + self.tag + } + + /// Return `true` if a `DW_AT_sibling` attribute will be emitted. + #[inline] + pub fn sibling(&self) -> bool { + self.sibling + } + + /// Set whether a `DW_AT_sibling` attribute will be emitted. + /// + /// The attribute will only be emitted if the DIE has children. + #[inline] + pub fn set_sibling(&mut self, sibling: bool) { + self.sibling = sibling; + } + + /// Iterate over the attributes of this entry. + #[inline] + pub fn attrs(&self) -> slice::Iter<Attribute> { + self.attrs.iter() + } + + /// Iterate over the attributes of this entry for modification. + #[inline] + pub fn attrs_mut(&mut self) -> slice::IterMut<Attribute> { + self.attrs.iter_mut() + } + + /// Get an attribute. + pub fn get(&self, name: constants::DwAt) -> Option<&AttributeValue> { + self.attrs + .iter() + .find(|attr| attr.name == name) + .map(|attr| &attr.value) + } + + /// Get an attribute for modification. + pub fn get_mut(&mut self, name: constants::DwAt) -> Option<&mut AttributeValue> { + self.attrs + .iter_mut() + .find(|attr| attr.name == name) + .map(|attr| &mut attr.value) + } + + /// Set an attribute. + /// + /// Replaces any existing attribute with the same name. + /// + /// # Panics + /// + /// Panics if `name` is `DW_AT_sibling`. Use `set_sibling` instead. + pub fn set(&mut self, name: constants::DwAt, value: AttributeValue) { + assert_ne!(name, constants::DW_AT_sibling); + if let Some(attr) = self.attrs.iter_mut().find(|attr| attr.name == name) { + attr.value = value; + return; + } + self.attrs.push(Attribute { name, value }); + } + + /// Delete an attribute. + /// + /// Replaces any existing attribute with the same name. + pub fn delete(&mut self, name: constants::DwAt) { + self.attrs.retain(|x| x.name != name); + } + + /// Iterate over the children of this entry. + /// + /// Note: use `Unit::add` to add a new child to this entry. + #[inline] + pub fn children(&self) -> slice::Iter<UnitEntryId> { + self.children.iter() + } + + /// Delete a child entry and all of its children. + pub fn delete_child(&mut self, id: UnitEntryId) { + self.children.retain(|&child| child != id); + } + + /// Return the type abbreviation for this DIE. + fn abbreviation(&self, encoding: Encoding) -> Result<Abbreviation> { + let mut attrs = Vec::new(); + + if self.sibling && !self.children.is_empty() { + let form = match encoding.format { + Format::Dwarf32 => constants::DW_FORM_ref4, + Format::Dwarf64 => constants::DW_FORM_ref8, + }; + attrs.push(AttributeSpecification::new(constants::DW_AT_sibling, form)); + } + + for attr in &self.attrs { + attrs.push(attr.specification(encoding)?); + } + + Ok(Abbreviation::new( + self.tag, + !self.children.is_empty(), + attrs, + )) + } + + fn calculate_offsets( + &self, + unit: &Unit, + offset: &mut usize, + offsets: &mut UnitOffsets, + abbrevs: &mut AbbreviationTable, + ) -> Result<()> { + offsets.entries[self.id.index].offset = DebugInfoOffset(*offset); + offsets.entries[self.id.index].abbrev = abbrevs.add(self.abbreviation(unit.encoding())?); + *offset += self.size(unit, offsets); + if !self.children.is_empty() { + for child in &self.children { + unit.entries[child.index].calculate_offsets(unit, offset, offsets, abbrevs)?; + } + // Null child + *offset += 1; + } + Ok(()) + } + + fn size(&self, unit: &Unit, offsets: &UnitOffsets) -> usize { + let mut size = uleb128_size(offsets.abbrev(self.id)); + if self.sibling && !self.children.is_empty() { + size += unit.format().word_size() as usize; + } + for attr in &self.attrs { + size += attr.value.size(unit, offsets); + } + size + } + + /// Write the entry to the given sections. + #[allow(clippy::too_many_arguments)] + fn write<W: Writer>( + &self, + w: &mut DebugInfo<W>, + debug_info_refs: &mut Vec<DebugInfoReference>, + unit_refs: &mut Vec<(DebugInfoOffset, UnitEntryId)>, + unit: &Unit, + offsets: &mut UnitOffsets, + abbrevs: &mut AbbreviationTable, + line_program: Option<DebugLineOffset>, + line_strings: &DebugLineStrOffsets, + strings: &DebugStrOffsets, + range_lists: &RangeListOffsets, + loc_lists: &LocationListOffsets, + ) -> Result<()> { + debug_assert_eq!(offsets.debug_info_offset(self.id), w.offset()); + w.write_uleb128(offsets.abbrev(self.id))?; + + let sibling_offset = if self.sibling && !self.children.is_empty() { + let offset = w.offset(); + w.write_udata(0, unit.format().word_size())?; + Some(offset) + } else { + None + }; + + for attr in &self.attrs { + attr.value.write( + w, + debug_info_refs, + unit_refs, + unit, + offsets, + line_program, + line_strings, + strings, + range_lists, + loc_lists, + )?; + } + + if !self.children.is_empty() { + for child in &self.children { + unit.entries[child.index].write( + w, + debug_info_refs, + unit_refs, + unit, + offsets, + abbrevs, + line_program, + line_strings, + strings, + range_lists, + loc_lists, + )?; + } + // Null child + w.write_u8(0)?; + } + + if let Some(offset) = sibling_offset { + let next_offset = (w.offset().0 - offsets.unit.0) as u64; + // This does not need relocation. + w.write_udata_at(offset.0, next_offset, unit.format().word_size())?; + } + Ok(()) + } +} + +/// An attribute in a `DebuggingInformationEntry`, consisting of a name and +/// associated value. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Attribute { + name: constants::DwAt, + value: AttributeValue, +} + +impl Attribute { + /// Get the name of this attribute. + #[inline] + pub fn name(&self) -> constants::DwAt { + self.name + } + + /// Get the value of this attribute. + #[inline] + pub fn get(&self) -> &AttributeValue { + &self.value + } + + /// Set the value of this attribute. + #[inline] + pub fn set(&mut self, value: AttributeValue) { + self.value = value; + } + + /// Return the type specification for this attribute. + fn specification(&self, encoding: Encoding) -> Result<AttributeSpecification> { + Ok(AttributeSpecification::new( + self.name, + self.value.form(encoding)?, + )) + } +} + +/// The value of an attribute in a `DebuggingInformationEntry`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AttributeValue { + /// "Refers to some location in the address space of the described program." + Address(Address), + + /// A slice of an arbitrary number of bytes. + Block(Vec<u8>), + + /// A one byte constant data value. How to interpret the byte depends on context. + /// + /// From section 7 of the standard: "Depending on context, it may be a + /// signed integer, an unsigned integer, a floating-point constant, or + /// anything else." + Data1(u8), + + /// A two byte constant data value. How to interpret the bytes depends on context. + /// + /// This value will be converted to the target endian before writing. + /// + /// From section 7 of the standard: "Depending on context, it may be a + /// signed integer, an unsigned integer, a floating-point constant, or + /// anything else." + Data2(u16), + + /// A four byte constant data value. How to interpret the bytes depends on context. + /// + /// This value will be converted to the target endian before writing. + /// + /// From section 7 of the standard: "Depending on context, it may be a + /// signed integer, an unsigned integer, a floating-point constant, or + /// anything else." + Data4(u32), + + /// An eight byte constant data value. How to interpret the bytes depends on context. + /// + /// This value will be converted to the target endian before writing. + /// + /// From section 7 of the standard: "Depending on context, it may be a + /// signed integer, an unsigned integer, a floating-point constant, or + /// anything else." + Data8(u64), + + /// A signed integer constant. + Sdata(i64), + + /// An unsigned integer constant. + Udata(u64), + + /// "The information bytes contain a DWARF expression (see Section 2.5) or + /// location description (see Section 2.6)." + Exprloc(Expression), + + /// A boolean that indicates presence or absence of the attribute. + Flag(bool), + + /// An attribute that is always present. + FlagPresent, + + /// A reference to a `DebuggingInformationEntry` in this unit. + UnitRef(UnitEntryId), + + /// A reference to a `DebuggingInformationEntry` in a potentially different unit. + DebugInfoRef(Reference), + + /// An offset into the `.debug_info` section of the supplementary object file. + /// + /// The API does not currently assist with generating this offset. + /// This variant will be removed from the API once support for writing + /// supplementary object files is implemented. + DebugInfoRefSup(DebugInfoOffset), + + /// A reference to a line number program. + LineProgramRef, + + /// A reference to a location list. + LocationListRef(LocationListId), + + /// An offset into the `.debug_macinfo` section. + /// + /// The API does not currently assist with generating this offset. + /// This variant will be removed from the API once support for writing + /// `.debug_macinfo` sections is implemented. + DebugMacinfoRef(DebugMacinfoOffset), + + /// An offset into the `.debug_macro` section. + /// + /// The API does not currently assist with generating this offset. + /// This variant will be removed from the API once support for writing + /// `.debug_macro` sections is implemented. + DebugMacroRef(DebugMacroOffset), + + /// A reference to a range list. + RangeListRef(RangeListId), + + /// A type signature. + /// + /// The API does not currently assist with generating this signature. + /// This variant will be removed from the API once support for writing + /// `.debug_types` sections is implemented. + DebugTypesRef(DebugTypeSignature), + + /// A reference to a string in the `.debug_str` section. + StringRef(StringId), + + /// An offset into the `.debug_str` section of the supplementary object file. + /// + /// The API does not currently assist with generating this offset. + /// This variant will be removed from the API once support for writing + /// supplementary object files is implemented. + DebugStrRefSup(DebugStrOffset), + + /// A reference to a string in the `.debug_line_str` section. + LineStringRef(LineStringId), + + /// A slice of bytes representing a string. Must not include null bytes. + /// Not guaranteed to be UTF-8 or anything like that. + String(Vec<u8>), + + /// The value of a `DW_AT_encoding` attribute. + Encoding(constants::DwAte), + + /// The value of a `DW_AT_decimal_sign` attribute. + DecimalSign(constants::DwDs), + + /// The value of a `DW_AT_endianity` attribute. + Endianity(constants::DwEnd), + + /// The value of a `DW_AT_accessibility` attribute. + Accessibility(constants::DwAccess), + + /// The value of a `DW_AT_visibility` attribute. + Visibility(constants::DwVis), + + /// The value of a `DW_AT_virtuality` attribute. + Virtuality(constants::DwVirtuality), + + /// The value of a `DW_AT_language` attribute. + Language(constants::DwLang), + + /// The value of a `DW_AT_address_class` attribute. + AddressClass(constants::DwAddr), + + /// The value of a `DW_AT_identifier_case` attribute. + IdentifierCase(constants::DwId), + + /// The value of a `DW_AT_calling_convention` attribute. + CallingConvention(constants::DwCc), + + /// The value of a `DW_AT_inline` attribute. + Inline(constants::DwInl), + + /// The value of a `DW_AT_ordering` attribute. + Ordering(constants::DwOrd), + + /// An index into the filename entries from the line number information + /// table for the unit containing this value. + FileIndex(Option<FileId>), +} + +impl AttributeValue { + /// Return the form that will be used to encode this value. + pub fn form(&self, encoding: Encoding) -> Result<constants::DwForm> { + // TODO: missing forms: + // - DW_FORM_indirect + // - DW_FORM_implicit_const + // - FW_FORM_block1/block2/block4 + // - DW_FORM_str/strx1/strx2/strx3/strx4 + // - DW_FORM_addrx/addrx1/addrx2/addrx3/addrx4 + // - DW_FORM_data16 + // - DW_FORM_line_strp + // - DW_FORM_loclistx + // - DW_FORM_rnglistx + let form = match *self { + AttributeValue::Address(_) => constants::DW_FORM_addr, + AttributeValue::Block(_) => constants::DW_FORM_block, + AttributeValue::Data1(_) => constants::DW_FORM_data1, + AttributeValue::Data2(_) => constants::DW_FORM_data2, + AttributeValue::Data4(_) => constants::DW_FORM_data4, + AttributeValue::Data8(_) => constants::DW_FORM_data8, + AttributeValue::Exprloc(_) => constants::DW_FORM_exprloc, + AttributeValue::Flag(_) => constants::DW_FORM_flag, + AttributeValue::FlagPresent => constants::DW_FORM_flag_present, + AttributeValue::UnitRef(_) => { + // Using a fixed size format lets us write a placeholder before we know + // the value. + match encoding.format { + Format::Dwarf32 => constants::DW_FORM_ref4, + Format::Dwarf64 => constants::DW_FORM_ref8, + } + } + AttributeValue::DebugInfoRef(_) => constants::DW_FORM_ref_addr, + AttributeValue::DebugInfoRefSup(_) => { + // TODO: should this depend on the size of supplementary section? + match encoding.format { + Format::Dwarf32 => constants::DW_FORM_ref_sup4, + Format::Dwarf64 => constants::DW_FORM_ref_sup8, + } + } + AttributeValue::LineProgramRef + | AttributeValue::LocationListRef(_) + | AttributeValue::DebugMacinfoRef(_) + | AttributeValue::DebugMacroRef(_) + | AttributeValue::RangeListRef(_) => { + if encoding.version == 2 || encoding.version == 3 { + match encoding.format { + Format::Dwarf32 => constants::DW_FORM_data4, + Format::Dwarf64 => constants::DW_FORM_data8, + } + } else { + constants::DW_FORM_sec_offset + } + } + AttributeValue::DebugTypesRef(_) => constants::DW_FORM_ref_sig8, + AttributeValue::StringRef(_) => constants::DW_FORM_strp, + AttributeValue::DebugStrRefSup(_) => constants::DW_FORM_strp_sup, + AttributeValue::LineStringRef(_) => constants::DW_FORM_line_strp, + AttributeValue::String(_) => constants::DW_FORM_string, + AttributeValue::Encoding(_) + | AttributeValue::DecimalSign(_) + | AttributeValue::Endianity(_) + | AttributeValue::Accessibility(_) + | AttributeValue::Visibility(_) + | AttributeValue::Virtuality(_) + | AttributeValue::Language(_) + | AttributeValue::AddressClass(_) + | AttributeValue::IdentifierCase(_) + | AttributeValue::CallingConvention(_) + | AttributeValue::Inline(_) + | AttributeValue::Ordering(_) + | AttributeValue::FileIndex(_) + | AttributeValue::Udata(_) => constants::DW_FORM_udata, + AttributeValue::Sdata(_) => constants::DW_FORM_sdata, + }; + Ok(form) + } + + fn size(&self, unit: &Unit, offsets: &UnitOffsets) -> usize { + macro_rules! debug_assert_form { + ($form:expr) => { + debug_assert_eq!(self.form(unit.encoding()).unwrap(), $form) + }; + } + match *self { + AttributeValue::Address(_) => { + debug_assert_form!(constants::DW_FORM_addr); + unit.address_size() as usize + } + AttributeValue::Block(ref val) => { + debug_assert_form!(constants::DW_FORM_block); + uleb128_size(val.len() as u64) + val.len() + } + AttributeValue::Data1(_) => { + debug_assert_form!(constants::DW_FORM_data1); + 1 + } + AttributeValue::Data2(_) => { + debug_assert_form!(constants::DW_FORM_data2); + 2 + } + AttributeValue::Data4(_) => { + debug_assert_form!(constants::DW_FORM_data4); + 4 + } + AttributeValue::Data8(_) => { + debug_assert_form!(constants::DW_FORM_data8); + 8 + } + AttributeValue::Sdata(val) => { + debug_assert_form!(constants::DW_FORM_sdata); + sleb128_size(val) + } + AttributeValue::Udata(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val) + } + AttributeValue::Exprloc(ref val) => { + debug_assert_form!(constants::DW_FORM_exprloc); + let size = val.size(unit.encoding(), Some(offsets)); + uleb128_size(size as u64) + size + } + AttributeValue::Flag(_) => { + debug_assert_form!(constants::DW_FORM_flag); + 1 + } + AttributeValue::FlagPresent => { + debug_assert_form!(constants::DW_FORM_flag_present); + 0 + } + AttributeValue::UnitRef(_) => { + match unit.format() { + Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref4), + Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref8), + } + unit.format().word_size() as usize + } + AttributeValue::DebugInfoRef(_) => { + debug_assert_form!(constants::DW_FORM_ref_addr); + if unit.version() == 2 { + unit.address_size() as usize + } else { + unit.format().word_size() as usize + } + } + AttributeValue::DebugInfoRefSup(_) => { + match unit.format() { + Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref_sup4), + Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref_sup8), + } + unit.format().word_size() as usize + } + AttributeValue::LineProgramRef => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::LocationListRef(_) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::DebugMacinfoRef(_) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::DebugMacroRef(_) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::RangeListRef(_) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + unit.format().word_size() as usize + } + AttributeValue::DebugTypesRef(_) => { + debug_assert_form!(constants::DW_FORM_ref_sig8); + 8 + } + AttributeValue::StringRef(_) => { + debug_assert_form!(constants::DW_FORM_strp); + unit.format().word_size() as usize + } + AttributeValue::DebugStrRefSup(_) => { + debug_assert_form!(constants::DW_FORM_strp_sup); + unit.format().word_size() as usize + } + AttributeValue::LineStringRef(_) => { + debug_assert_form!(constants::DW_FORM_line_strp); + unit.format().word_size() as usize + } + AttributeValue::String(ref val) => { + debug_assert_form!(constants::DW_FORM_string); + val.len() + 1 + } + AttributeValue::Encoding(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::DecimalSign(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Endianity(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Accessibility(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Visibility(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Virtuality(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Language(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::AddressClass(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::IdentifierCase(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::CallingConvention(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Inline(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::Ordering(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.0 as u64) + } + AttributeValue::FileIndex(val) => { + debug_assert_form!(constants::DW_FORM_udata); + uleb128_size(val.map(FileId::raw).unwrap_or(0)) + } + } + } + + /// Write the attribute value to the given sections. + #[allow(clippy::cyclomatic_complexity, clippy::too_many_arguments)] + fn write<W: Writer>( + &self, + w: &mut DebugInfo<W>, + debug_info_refs: &mut Vec<DebugInfoReference>, + unit_refs: &mut Vec<(DebugInfoOffset, UnitEntryId)>, + unit: &Unit, + offsets: &UnitOffsets, + line_program: Option<DebugLineOffset>, + line_strings: &DebugLineStrOffsets, + strings: &DebugStrOffsets, + range_lists: &RangeListOffsets, + loc_lists: &LocationListOffsets, + ) -> Result<()> { + macro_rules! debug_assert_form { + ($form:expr) => { + debug_assert_eq!(self.form(unit.encoding()).unwrap(), $form) + }; + } + match *self { + AttributeValue::Address(val) => { + debug_assert_form!(constants::DW_FORM_addr); + w.write_address(val, unit.address_size())?; + } + AttributeValue::Block(ref val) => { + debug_assert_form!(constants::DW_FORM_block); + w.write_uleb128(val.len() as u64)?; + w.write(&val)?; + } + AttributeValue::Data1(val) => { + debug_assert_form!(constants::DW_FORM_data1); + w.write_u8(val)?; + } + AttributeValue::Data2(val) => { + debug_assert_form!(constants::DW_FORM_data2); + w.write_u16(val)?; + } + AttributeValue::Data4(val) => { + debug_assert_form!(constants::DW_FORM_data4); + w.write_u32(val)?; + } + AttributeValue::Data8(val) => { + debug_assert_form!(constants::DW_FORM_data8); + w.write_u64(val)?; + } + AttributeValue::Sdata(val) => { + debug_assert_form!(constants::DW_FORM_sdata); + w.write_sleb128(val)?; + } + AttributeValue::Udata(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(val)?; + } + AttributeValue::Exprloc(ref val) => { + debug_assert_form!(constants::DW_FORM_exprloc); + w.write_uleb128(val.size(unit.encoding(), Some(offsets)) as u64)?; + val.write( + &mut w.0, + Some(debug_info_refs), + unit.encoding(), + Some(offsets), + )?; + } + AttributeValue::Flag(val) => { + debug_assert_form!(constants::DW_FORM_flag); + w.write_u8(val as u8)?; + } + AttributeValue::FlagPresent => { + debug_assert_form!(constants::DW_FORM_flag_present); + } + AttributeValue::UnitRef(id) => { + match unit.format() { + Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref4), + Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref8), + } + unit_refs.push((w.offset(), id)); + w.write_udata(0, unit.format().word_size())?; + } + AttributeValue::DebugInfoRef(reference) => { + debug_assert_form!(constants::DW_FORM_ref_addr); + let size = if unit.version() == 2 { + unit.address_size() + } else { + unit.format().word_size() + }; + match reference { + Reference::Symbol(symbol) => w.write_reference(symbol, size)?, + Reference::Entry(unit, entry) => { + debug_info_refs.push(DebugInfoReference { + offset: w.len(), + unit, + entry, + size, + }); + w.write_udata(0, size)?; + } + } + } + AttributeValue::DebugInfoRefSup(val) => { + match unit.format() { + Format::Dwarf32 => debug_assert_form!(constants::DW_FORM_ref_sup4), + Format::Dwarf64 => debug_assert_form!(constants::DW_FORM_ref_sup8), + } + w.write_udata(val.0 as u64, unit.format().word_size())?; + } + AttributeValue::LineProgramRef => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + match line_program { + Some(line_program) => { + w.write_offset( + line_program.0, + SectionId::DebugLine, + unit.format().word_size(), + )?; + } + None => return Err(Error::InvalidAttributeValue), + } + } + AttributeValue::LocationListRef(val) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + let section = if unit.version() <= 4 { + SectionId::DebugLoc + } else { + SectionId::DebugLocLists + }; + w.write_offset(loc_lists.get(val).0, section, unit.format().word_size())?; + } + AttributeValue::DebugMacinfoRef(val) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + w.write_offset(val.0, SectionId::DebugMacinfo, unit.format().word_size())?; + } + AttributeValue::DebugMacroRef(val) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + w.write_offset(val.0, SectionId::DebugMacro, unit.format().word_size())?; + } + AttributeValue::RangeListRef(val) => { + if unit.version() >= 4 { + debug_assert_form!(constants::DW_FORM_sec_offset); + } + let section = if unit.version() <= 4 { + SectionId::DebugRanges + } else { + SectionId::DebugRngLists + }; + w.write_offset(range_lists.get(val).0, section, unit.format().word_size())?; + } + AttributeValue::DebugTypesRef(val) => { + debug_assert_form!(constants::DW_FORM_ref_sig8); + w.write_u64(val.0)?; + } + AttributeValue::StringRef(val) => { + debug_assert_form!(constants::DW_FORM_strp); + w.write_offset( + strings.get(val).0, + SectionId::DebugStr, + unit.format().word_size(), + )?; + } + AttributeValue::DebugStrRefSup(val) => { + debug_assert_form!(constants::DW_FORM_strp_sup); + w.write_udata(val.0 as u64, unit.format().word_size())?; + } + AttributeValue::LineStringRef(val) => { + debug_assert_form!(constants::DW_FORM_line_strp); + w.write_offset( + line_strings.get(val).0, + SectionId::DebugLineStr, + unit.format().word_size(), + )?; + } + AttributeValue::String(ref val) => { + debug_assert_form!(constants::DW_FORM_string); + w.write(&val)?; + w.write_u8(0)?; + } + AttributeValue::Encoding(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::DecimalSign(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Endianity(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Accessibility(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Visibility(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Virtuality(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Language(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::AddressClass(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(val.0)?; + } + AttributeValue::IdentifierCase(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::CallingConvention(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Inline(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::Ordering(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(u64::from(val.0))?; + } + AttributeValue::FileIndex(val) => { + debug_assert_form!(constants::DW_FORM_udata); + w.write_uleb128(val.map(FileId::raw).unwrap_or(0))?; + } + } + Ok(()) + } +} + +define_section!( + DebugInfo, + DebugInfoOffset, + "A writable `.debug_info` section." +); + +/// The section offsets of all elements within a `.debug_info` section. +#[derive(Debug, Default)] +pub struct DebugInfoOffsets { + base_id: BaseId, + units: Vec<UnitOffsets>, +} + +impl DebugInfoOffsets { + #[cfg(test)] + pub(crate) fn unit_offsets(&self, unit: UnitId) -> &UnitOffsets { + debug_assert_eq!(self.base_id, unit.base_id); + &self.units[unit.index] + } + + /// Get the `.debug_info` section offset for the given unit. + #[inline] + pub fn unit(&self, unit: UnitId) -> DebugInfoOffset { + debug_assert_eq!(self.base_id, unit.base_id); + self.units[unit.index].unit + } + + /// Get the `.debug_info` section offset for the given entry. + #[inline] + pub fn entry(&self, unit: UnitId, entry: UnitEntryId) -> DebugInfoOffset { + debug_assert_eq!(self.base_id, unit.base_id); + self.units[unit.index].debug_info_offset(entry) + } +} + +/// The section offsets of all elements of a unit within a `.debug_info` section. +#[derive(Debug)] +pub(crate) struct UnitOffsets { + base_id: BaseId, + unit: DebugInfoOffset, + entries: Vec<EntryOffset>, +} + +impl UnitOffsets { + #[cfg(test)] + fn none() -> Self { + UnitOffsets { + base_id: BaseId::default(), + unit: DebugInfoOffset(0), + entries: Vec::new(), + } + } + + /// Get the .debug_info offset for the given entry. + #[inline] + pub(crate) fn debug_info_offset(&self, entry: UnitEntryId) -> DebugInfoOffset { + debug_assert_eq!(self.base_id, entry.base_id); + let offset = self.entries[entry.index].offset; + debug_assert_ne!(offset.0, 0); + offset + } + + /// Get the unit offset for the given entry. + #[inline] + pub(crate) fn unit_offset(&self, entry: UnitEntryId) -> u64 { + let offset = self.debug_info_offset(entry); + (offset.0 - self.unit.0) as u64 + } + + /// Get the abbreviation code for the given entry. + #[inline] + pub(crate) fn abbrev(&self, entry: UnitEntryId) -> u64 { + debug_assert_eq!(self.base_id, entry.base_id); + self.entries[entry.index].abbrev + } +} + +#[derive(Debug, Clone, Copy)] +pub(crate) struct EntryOffset { + offset: DebugInfoOffset, + abbrev: u64, +} + +impl EntryOffset { + fn none() -> Self { + EntryOffset { + offset: DebugInfoOffset(0), + abbrev: 0, + } + } +} + +/// A reference to a `.debug_info` entry that has yet to be resolved. +#[derive(Debug, Clone, Copy)] +pub(crate) struct DebugInfoReference { + /// The offset within the section of the reference. + pub offset: usize, + /// The size of the reference. + pub size: u8, + /// The unit containing the entry. + pub unit: UnitId, + /// The entry being referenced. + pub entry: UnitEntryId, +} + +#[cfg(feature = "read")] +pub(crate) mod convert { + use super::*; + use crate::common::UnitSectionOffset; + use crate::read::{self, Reader}; + use crate::write::{self, ConvertError, ConvertResult, LocationList, RangeList}; + use std::collections::HashMap; + + pub(crate) struct ConvertUnit<R: Reader<Offset = usize>> { + from_unit: read::Unit<R>, + base_id: BaseId, + encoding: Encoding, + entries: Vec<DebuggingInformationEntry>, + entry_offsets: Vec<read::UnitOffset>, + root: UnitEntryId, + } + + pub(crate) struct ConvertUnitContext<'a, R: Reader<Offset = usize>> { + pub dwarf: &'a read::Dwarf<R>, + pub unit: &'a read::Unit<R>, + pub line_strings: &'a mut write::LineStringTable, + pub strings: &'a mut write::StringTable, + pub ranges: &'a mut write::RangeListTable, + pub locations: &'a mut write::LocationListTable, + pub convert_address: &'a dyn Fn(u64) -> Option<Address>, + pub base_address: Address, + pub line_program_offset: Option<DebugLineOffset>, + pub line_program_files: Vec<FileId>, + pub entry_ids: &'a HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>, + } + + impl UnitTable { + /// Create a unit table by reading the data in the given sections. + /// + /// This also updates the given tables with the values that are referenced from + /// attributes in this 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: Reader<Offset = usize>>( + dwarf: &read::Dwarf<R>, + line_strings: &mut write::LineStringTable, + strings: &mut write::StringTable, + convert_address: &dyn Fn(u64) -> Option<Address>, + ) -> ConvertResult<UnitTable> { + let base_id = BaseId::default(); + let mut unit_entries = Vec::new(); + let mut entry_ids = HashMap::new(); + + let mut from_units = dwarf.units(); + while let Some(from_unit) = from_units.next()? { + let unit_id = UnitId::new(base_id, unit_entries.len()); + unit_entries.push(Unit::convert_entries( + from_unit, + unit_id, + &mut entry_ids, + dwarf, + )?); + } + + // Attributes must be converted in a separate pass so that we can handle + // references to other compilation units. + let mut units = Vec::new(); + for unit_entries in unit_entries.drain(..) { + units.push(Unit::convert_attributes( + unit_entries, + &entry_ids, + dwarf, + line_strings, + strings, + convert_address, + )?); + } + + Ok(UnitTable { base_id, units }) + } + } + + impl Unit { + /// Create a unit by reading the data in the input sections. + /// + /// Does not add entry attributes. + #[allow(clippy::too_many_arguments)] + pub(crate) fn convert_entries<R: Reader<Offset = usize>>( + from_header: read::UnitHeader<R>, + unit_id: UnitId, + entry_ids: &mut HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>, + dwarf: &read::Dwarf<R>, + ) -> ConvertResult<ConvertUnit<R>> { + match from_header.type_() { + read::UnitType::Compilation => (), + _ => return Err(ConvertError::UnsupportedUnitType), + } + let base_id = BaseId::default(); + + let from_unit = dwarf.unit(from_header)?; + let encoding = from_unit.encoding(); + + let mut entries = Vec::new(); + let mut entry_offsets = Vec::new(); + + let mut from_tree = from_unit.entries_tree(None)?; + let from_root = from_tree.root()?; + let root = DebuggingInformationEntry::convert_entry( + from_root, + &from_unit, + base_id, + &mut entries, + &mut entry_offsets, + entry_ids, + None, + unit_id, + )?; + + Ok(ConvertUnit { + from_unit, + base_id, + encoding, + entries, + entry_offsets, + root, + }) + } + + /// Create entry attributes by reading the data in the input sections. + fn convert_attributes<R: Reader<Offset = usize>>( + unit: ConvertUnit<R>, + entry_ids: &HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>, + dwarf: &read::Dwarf<R>, + line_strings: &mut write::LineStringTable, + strings: &mut write::StringTable, + convert_address: &dyn Fn(u64) -> Option<Address>, + ) -> ConvertResult<Unit> { + let from_unit = unit.from_unit; + let base_address = + convert_address(from_unit.low_pc).ok_or(ConvertError::InvalidAddress)?; + + let (line_program_offset, line_program, line_program_files) = + match from_unit.line_program { + Some(ref from_program) => { + let from_program = from_program.clone(); + let line_program_offset = from_program.header().offset(); + let (line_program, line_program_files) = LineProgram::from( + from_program, + dwarf, + line_strings, + strings, + convert_address, + )?; + (Some(line_program_offset), line_program, line_program_files) + } + None => (None, LineProgram::none(), Vec::new()), + }; + + let mut ranges = RangeListTable::default(); + let mut locations = LocationListTable::default(); + + let mut context = ConvertUnitContext { + entry_ids, + dwarf, + unit: &from_unit, + line_strings, + strings, + ranges: &mut ranges, + locations: &mut locations, + convert_address, + base_address, + line_program_offset, + line_program_files, + }; + + let mut entries = unit.entries; + for entry in &mut entries { + entry.convert_attributes(&mut context, &unit.entry_offsets)?; + } + + Ok(Unit { + base_id: unit.base_id, + encoding: unit.encoding, + line_program, + ranges, + locations, + entries, + root: unit.root, + }) + } + } + + impl DebuggingInformationEntry { + /// Create an entry by reading the data in the input sections. + /// + /// Does not add the entry attributes. + fn convert_entry<R: Reader<Offset = usize>>( + from: read::EntriesTreeNode<R>, + from_unit: &read::Unit<R>, + base_id: BaseId, + entries: &mut Vec<DebuggingInformationEntry>, + entry_offsets: &mut Vec<read::UnitOffset>, + entry_ids: &mut HashMap<UnitSectionOffset, (UnitId, UnitEntryId)>, + parent: Option<UnitEntryId>, + unit_id: UnitId, + ) -> ConvertResult<UnitEntryId> { + let from_entry = from.entry(); + let id = DebuggingInformationEntry::new(base_id, entries, parent, from_entry.tag()); + let offset = from_entry.offset(); + entry_offsets.push(offset); + entry_ids.insert(offset.to_unit_section_offset(from_unit), (unit_id, id)); + + let mut from_children = from.children(); + while let Some(from_child) = from_children.next()? { + DebuggingInformationEntry::convert_entry( + from_child, + from_unit, + base_id, + entries, + entry_offsets, + entry_ids, + Some(id), + unit_id, + )?; + } + Ok(id) + } + + /// Create an entry's attributes by reading the data in the input sections. + fn convert_attributes<R: Reader<Offset = usize>>( + &mut self, + context: &mut ConvertUnitContext<R>, + entry_offsets: &[read::UnitOffset], + ) -> ConvertResult<()> { + let offset = entry_offsets[self.id.index]; + let from = context.unit.entry(offset)?; + let mut from_attrs = from.attrs(); + while let Some(from_attr) = from_attrs.next()? { + if from_attr.name() == constants::DW_AT_sibling { + // This may point to a null entry, so we have to treat it differently. + self.set_sibling(true); + } else if let Some(attr) = Attribute::from(context, &from_attr)? { + self.set(attr.name, attr.value); + } + } + Ok(()) + } + } + + impl Attribute { + /// Create an attribute by reading the data in the given sections. + pub(crate) fn from<R: Reader<Offset = usize>>( + context: &mut ConvertUnitContext<R>, + from: &read::Attribute<R>, + ) -> ConvertResult<Option<Attribute>> { + let value = AttributeValue::from(context, from.value())?; + Ok(value.map(|value| Attribute { + name: from.name(), + value, + })) + } + } + + impl AttributeValue { + /// Create an attribute value by reading the data in the given sections. + pub(crate) fn from<R: Reader<Offset = usize>>( + context: &mut ConvertUnitContext<R>, + from: read::AttributeValue<R>, + ) -> ConvertResult<Option<AttributeValue>> { + let to = match from { + read::AttributeValue::Addr(val) => match (context.convert_address)(val) { + Some(val) => AttributeValue::Address(val), + None => return Err(ConvertError::InvalidAddress), + }, + read::AttributeValue::Block(r) => AttributeValue::Block(r.to_slice()?.into()), + read::AttributeValue::Data1(val) => AttributeValue::Data1(val), + read::AttributeValue::Data2(val) => AttributeValue::Data2(val), + read::AttributeValue::Data4(val) => AttributeValue::Data4(val), + read::AttributeValue::Data8(val) => AttributeValue::Data8(val), + read::AttributeValue::Sdata(val) => AttributeValue::Sdata(val), + read::AttributeValue::Udata(val) => AttributeValue::Udata(val), + read::AttributeValue::Exprloc(expression) => { + let expression = Expression::from( + expression, + context.unit.encoding(), + Some(context.dwarf), + Some(context.unit), + Some(context.entry_ids), + context.convert_address, + )?; + AttributeValue::Exprloc(expression) + } + // TODO: it would be nice to preserve the flag form. + read::AttributeValue::Flag(val) => AttributeValue::Flag(val), + read::AttributeValue::DebugAddrBase(_base) => { + // We convert all address indices to addresses, + // so this is unneeded. + return Ok(None); + } + read::AttributeValue::DebugAddrIndex(index) => { + let val = context.dwarf.address(context.unit, index)?; + match (context.convert_address)(val) { + Some(val) => AttributeValue::Address(val), + None => return Err(ConvertError::InvalidAddress), + } + } + read::AttributeValue::UnitRef(val) => { + if !context.unit.header.is_valid_offset(val) { + return Err(ConvertError::InvalidUnitRef); + } + let id = context + .entry_ids + .get(&val.to_unit_section_offset(context.unit)) + .ok_or(ConvertError::InvalidUnitRef)?; + AttributeValue::UnitRef(id.1) + } + read::AttributeValue::DebugInfoRef(val) => { + // TODO: support relocation of this value + let id = context + .entry_ids + .get(&UnitSectionOffset::DebugInfoOffset(val)) + .ok_or(ConvertError::InvalidDebugInfoRef)?; + AttributeValue::DebugInfoRef(Reference::Entry(id.0, id.1)) + } + read::AttributeValue::DebugInfoRefSup(val) => AttributeValue::DebugInfoRefSup(val), + read::AttributeValue::DebugLineRef(val) => { + // There should only be the line program in the CU DIE which we've already + // converted, so check if it matches that. + if Some(val) == context.line_program_offset { + AttributeValue::LineProgramRef + } else { + return Err(ConvertError::InvalidLineRef); + } + } + read::AttributeValue::DebugMacinfoRef(val) => AttributeValue::DebugMacinfoRef(val), + read::AttributeValue::DebugMacroRef(val) => AttributeValue::DebugMacroRef(val), + read::AttributeValue::LocationListsRef(val) => { + let iter = context + .dwarf + .locations + .raw_locations(val, context.unit.encoding())?; + let loc_list = LocationList::from(iter, context)?; + let loc_id = context.locations.add(loc_list); + AttributeValue::LocationListRef(loc_id) + } + read::AttributeValue::DebugLocListsBase(_base) => { + // We convert all location list indices to offsets, + // so this is unneeded. + return Ok(None); + } + read::AttributeValue::DebugLocListsIndex(index) => { + let offset = context.dwarf.locations_offset(context.unit, index)?; + let iter = context + .dwarf + .locations + .raw_locations(offset, context.unit.encoding())?; + let loc_list = LocationList::from(iter, context)?; + let loc_id = context.locations.add(loc_list); + AttributeValue::LocationListRef(loc_id) + } + read::AttributeValue::RangeListsRef(offset) => { + let offset = context.dwarf.ranges_offset_from_raw(context.unit, offset); + let iter = context.dwarf.raw_ranges(context.unit, offset)?; + let range_list = RangeList::from(iter, context)?; + let range_id = context.ranges.add(range_list); + AttributeValue::RangeListRef(range_id) + } + read::AttributeValue::DebugRngListsBase(_base) => { + // We convert all range list indices to offsets, + // so this is unneeded. + return Ok(None); + } + read::AttributeValue::DebugRngListsIndex(index) => { + let offset = context.dwarf.ranges_offset(context.unit, index)?; + let iter = context + .dwarf + .ranges + .raw_ranges(offset, context.unit.encoding())?; + let range_list = RangeList::from(iter, context)?; + let range_id = context.ranges.add(range_list); + AttributeValue::RangeListRef(range_id) + } + read::AttributeValue::DebugTypesRef(val) => AttributeValue::DebugTypesRef(val), + read::AttributeValue::DebugStrRef(offset) => { + let r = context.dwarf.string(offset)?; + let id = context.strings.add(r.to_slice()?); + AttributeValue::StringRef(id) + } + read::AttributeValue::DebugStrRefSup(val) => AttributeValue::DebugStrRefSup(val), + read::AttributeValue::DebugStrOffsetsBase(_base) => { + // We convert all string offsets to `.debug_str` references, + // so this is unneeded. + return Ok(None); + } + read::AttributeValue::DebugStrOffsetsIndex(index) => { + let offset = context.dwarf.string_offset(context.unit, index)?; + let r = context.dwarf.string(offset)?; + let id = context.strings.add(r.to_slice()?); + AttributeValue::StringRef(id) + } + read::AttributeValue::DebugLineStrRef(offset) => { + let r = context.dwarf.line_string(offset)?; + let id = context.line_strings.add(r.to_slice()?); + AttributeValue::LineStringRef(id) + } + read::AttributeValue::String(r) => AttributeValue::String(r.to_slice()?.into()), + read::AttributeValue::Encoding(val) => AttributeValue::Encoding(val), + read::AttributeValue::DecimalSign(val) => AttributeValue::DecimalSign(val), + read::AttributeValue::Endianity(val) => AttributeValue::Endianity(val), + read::AttributeValue::Accessibility(val) => AttributeValue::Accessibility(val), + read::AttributeValue::Visibility(val) => AttributeValue::Visibility(val), + read::AttributeValue::Virtuality(val) => AttributeValue::Virtuality(val), + read::AttributeValue::Language(val) => AttributeValue::Language(val), + read::AttributeValue::AddressClass(val) => AttributeValue::AddressClass(val), + read::AttributeValue::IdentifierCase(val) => AttributeValue::IdentifierCase(val), + read::AttributeValue::CallingConvention(val) => { + AttributeValue::CallingConvention(val) + } + read::AttributeValue::Inline(val) => AttributeValue::Inline(val), + read::AttributeValue::Ordering(val) => AttributeValue::Ordering(val), + read::AttributeValue::FileIndex(val) => { + if val == 0 { + // 0 means not specified, even for version 5. + AttributeValue::FileIndex(None) + } else { + match context.line_program_files.get(val as usize) { + Some(id) => AttributeValue::FileIndex(Some(*id)), + None => return Err(ConvertError::InvalidFileIndex), + } + } + } + // Should always be a more specific section reference. + read::AttributeValue::SecOffset(_) => { + return Err(ConvertError::InvalidAttributeValue); + } + read::AttributeValue::DwoId(DwoId(val)) => AttributeValue::Udata(val), + }; + Ok(Some(to)) + } + } +} + +#[cfg(test)] +#[cfg(feature = "read")] +mod tests { + use super::*; + use crate::common::{ + DebugAddrBase, DebugLocListsBase, DebugRngListsBase, DebugStrOffsetsBase, LineEncoding, + }; + use crate::constants; + use crate::read; + use crate::write::{ + DebugLine, DebugLineStr, DebugStr, DwarfUnit, EndianVec, LineString, LineStringTable, + Location, LocationList, LocationListTable, Range, RangeList, RangeListOffsets, + RangeListTable, StringTable, + }; + use crate::LittleEndian; + use std::collections::HashMap; + use std::mem; + + #[test] + #[allow(clippy::cyclomatic_complexity)] + fn test_unit_table() { + let mut strings = StringTable::default(); + + let mut units = UnitTable::default(); + let unit_id1 = units.add(Unit::new( + Encoding { + version: 4, + address_size: 8, + format: Format::Dwarf32, + }, + LineProgram::none(), + )); + let unit2 = units.add(Unit::new( + Encoding { + version: 2, + address_size: 4, + format: Format::Dwarf64, + }, + LineProgram::none(), + )); + let unit3 = units.add(Unit::new( + Encoding { + version: 5, + address_size: 4, + format: Format::Dwarf32, + }, + LineProgram::none(), + )); + assert_eq!(units.count(), 3); + { + let unit1 = units.get_mut(unit_id1); + assert_eq!(unit1.version(), 4); + assert_eq!(unit1.address_size(), 8); + assert_eq!(unit1.format(), Format::Dwarf32); + assert_eq!(unit1.count(), 1); + + let root_id = unit1.root(); + assert_eq!(root_id, UnitEntryId::new(unit1.base_id, 0)); + { + let root = unit1.get_mut(root_id); + assert_eq!(root.id(), root_id); + assert!(root.parent().is_none()); + assert_eq!(root.tag(), constants::DW_TAG_compile_unit); + + // Test get/get_mut + assert!(root.get(constants::DW_AT_producer).is_none()); + assert!(root.get_mut(constants::DW_AT_producer).is_none()); + let mut producer = AttributeValue::String(b"root"[..].into()); + root.set(constants::DW_AT_producer, producer.clone()); + assert_eq!(root.get(constants::DW_AT_producer), Some(&producer)); + assert_eq!(root.get_mut(constants::DW_AT_producer), Some(&mut producer)); + + // Test attrs + let mut attrs = root.attrs(); + let attr = attrs.next().unwrap(); + assert_eq!(attr.name(), constants::DW_AT_producer); + assert_eq!(attr.get(), &producer); + assert!(attrs.next().is_none()); + } + + let child1 = unit1.add(root_id, constants::DW_TAG_subprogram); + assert_eq!(child1, UnitEntryId::new(unit1.base_id, 1)); + { + let child1 = unit1.get_mut(child1); + assert_eq!(child1.parent(), Some(root_id)); + + let tmp = AttributeValue::String(b"tmp"[..].into()); + child1.set(constants::DW_AT_name, tmp.clone()); + assert_eq!(child1.get(constants::DW_AT_name), Some(&tmp)); + + // Test attrs_mut + let name = AttributeValue::StringRef(strings.add(&b"child1"[..])); + { + let attr = child1.attrs_mut().next().unwrap(); + assert_eq!(attr.name(), constants::DW_AT_name); + attr.set(name.clone()); + } + assert_eq!(child1.get(constants::DW_AT_name), Some(&name)); + } + + let child2 = unit1.add(root_id, constants::DW_TAG_subprogram); + assert_eq!(child2, UnitEntryId::new(unit1.base_id, 2)); + { + let child2 = unit1.get_mut(child2); + assert_eq!(child2.parent(), Some(root_id)); + + let tmp = AttributeValue::String(b"tmp"[..].into()); + child2.set(constants::DW_AT_name, tmp.clone()); + assert_eq!(child2.get(constants::DW_AT_name), Some(&tmp)); + + // Test replace + let name = AttributeValue::StringRef(strings.add(&b"child2"[..])); + child2.set(constants::DW_AT_name, name.clone()); + assert_eq!(child2.get(constants::DW_AT_name), Some(&name)); + } + + { + let root = unit1.get(root_id); + assert_eq!( + root.children().cloned().collect::<Vec<_>>(), + vec![child1, child2] + ); + } + } + { + let unit2 = units.get(unit2); + assert_eq!(unit2.version(), 2); + assert_eq!(unit2.address_size(), 4); + assert_eq!(unit2.format(), Format::Dwarf64); + assert_eq!(unit2.count(), 1); + + let root = unit2.root(); + assert_eq!(root, UnitEntryId::new(unit2.base_id, 0)); + let root = unit2.get(root); + assert_eq!(root.id(), UnitEntryId::new(unit2.base_id, 0)); + assert!(root.parent().is_none()); + assert_eq!(root.tag(), constants::DW_TAG_compile_unit); + } + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = strings.write(&mut sections.debug_str).unwrap(); + units + .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets) + .unwrap(); + + println!("{:?}", sections.debug_str); + println!("{:?}", sections.debug_info); + println!("{:?}", sections.debug_abbrev); + + let dwarf = read::Dwarf { + debug_abbrev: read::DebugAbbrev::new(sections.debug_abbrev.slice(), LittleEndian), + debug_info: read::DebugInfo::new(sections.debug_info.slice(), LittleEndian), + debug_str: read::DebugStr::new(sections.debug_str.slice(), LittleEndian), + ..Default::default() + }; + let mut read_units = dwarf.units(); + + { + let read_unit1 = read_units.next().unwrap().unwrap(); + let unit1 = units.get(unit_id1); + assert_eq!(unit1.version(), read_unit1.version()); + assert_eq!(unit1.address_size(), read_unit1.address_size()); + assert_eq!(unit1.format(), read_unit1.format()); + + let read_unit1 = dwarf.unit(read_unit1).unwrap(); + let mut read_entries = read_unit1.entries(); + + let root = unit1.get(unit1.root()); + { + let (depth, read_root) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 0); + assert_eq!(root.tag(), read_root.tag()); + assert!(read_root.has_children()); + + let producer = match root.get(constants::DW_AT_producer).unwrap() { + AttributeValue::String(ref producer) => &**producer, + otherwise => panic!("unexpected {:?}", otherwise), + }; + assert_eq!(producer, b"root"); + let read_producer = read_root + .attr_value(constants::DW_AT_producer) + .unwrap() + .unwrap(); + assert_eq!( + dwarf + .attr_string(&read_unit1, read_producer) + .unwrap() + .slice(), + producer + ); + } + + let mut children = root.children().cloned(); + + { + let child = children.next().unwrap(); + assert_eq!(child, UnitEntryId::new(unit1.base_id, 1)); + let child = unit1.get(child); + let (depth, read_child) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 1); + assert_eq!(child.tag(), read_child.tag()); + assert!(!read_child.has_children()); + + let name = match child.get(constants::DW_AT_name).unwrap() { + AttributeValue::StringRef(name) => *name, + otherwise => panic!("unexpected {:?}", otherwise), + }; + let name = strings.get(name); + assert_eq!(name, b"child1"); + let read_name = read_child + .attr_value(constants::DW_AT_name) + .unwrap() + .unwrap(); + assert_eq!( + dwarf.attr_string(&read_unit1, read_name).unwrap().slice(), + name + ); + } + + { + let child = children.next().unwrap(); + assert_eq!(child, UnitEntryId::new(unit1.base_id, 2)); + let child = unit1.get(child); + let (depth, read_child) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 0); + assert_eq!(child.tag(), read_child.tag()); + assert!(!read_child.has_children()); + + let name = match child.get(constants::DW_AT_name).unwrap() { + AttributeValue::StringRef(name) => *name, + otherwise => panic!("unexpected {:?}", otherwise), + }; + let name = strings.get(name); + assert_eq!(name, b"child2"); + let read_name = read_child + .attr_value(constants::DW_AT_name) + .unwrap() + .unwrap(); + assert_eq!( + dwarf.attr_string(&read_unit1, read_name).unwrap().slice(), + name + ); + } + + assert!(read_entries.next_dfs().unwrap().is_none()); + } + + { + let read_unit2 = read_units.next().unwrap().unwrap(); + let unit2 = units.get(unit2); + assert_eq!(unit2.version(), read_unit2.version()); + assert_eq!(unit2.address_size(), read_unit2.address_size()); + assert_eq!(unit2.format(), read_unit2.format()); + + let abbrevs = dwarf.abbreviations(&read_unit2).unwrap(); + let mut read_entries = read_unit2.entries(&abbrevs); + + { + let root = unit2.get(unit2.root()); + let (depth, read_root) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 0); + assert_eq!(root.tag(), read_root.tag()); + assert!(!read_root.has_children()); + } + + assert!(read_entries.next_dfs().unwrap().is_none()); + } + + { + let read_unit3 = read_units.next().unwrap().unwrap(); + let unit3 = units.get(unit3); + assert_eq!(unit3.version(), read_unit3.version()); + assert_eq!(unit3.address_size(), read_unit3.address_size()); + assert_eq!(unit3.format(), read_unit3.format()); + + let abbrevs = dwarf.abbreviations(&read_unit3).unwrap(); + let mut read_entries = read_unit3.entries(&abbrevs); + + { + let root = unit3.get(unit3.root()); + let (depth, read_root) = read_entries.next_dfs().unwrap().unwrap(); + assert_eq!(depth, 0); + assert_eq!(root.tag(), read_root.tag()); + assert!(!read_root.has_children()); + } + + assert!(read_entries.next_dfs().unwrap().is_none()); + } + + assert!(read_units.next().unwrap().is_none()); + + let mut convert_line_strings = LineStringTable::default(); + let mut convert_strings = StringTable::default(); + let convert_units = UnitTable::from( + &dwarf, + &mut convert_line_strings, + &mut convert_strings, + &|address| Some(Address::Constant(address)), + ) + .unwrap(); + assert_eq!(convert_units.count(), units.count()); + + for i in 0..convert_units.count() { + let unit_id = units.id(i); + let unit = units.get(unit_id); + let convert_unit_id = convert_units.id(i); + let convert_unit = convert_units.get(convert_unit_id); + assert_eq!(convert_unit.version(), unit.version()); + assert_eq!(convert_unit.address_size(), unit.address_size()); + assert_eq!(convert_unit.format(), unit.format()); + assert_eq!(convert_unit.count(), unit.count()); + + let root = unit.get(unit.root()); + let convert_root = convert_unit.get(convert_unit.root()); + assert_eq!(convert_root.tag(), root.tag()); + for (convert_attr, attr) in convert_root.attrs().zip(root.attrs()) { + assert_eq!(convert_attr, attr); + } + } + } + + #[test] + fn test_attribute_value() { + // Create a string table and a string with a non-zero id/offset. + let mut strings = StringTable::default(); + strings.add("string one"); + let string_id = strings.add("string two"); + let mut debug_str = DebugStr::from(EndianVec::new(LittleEndian)); + let debug_str_offsets = strings.write(&mut debug_str).unwrap(); + let read_debug_str = read::DebugStr::new(debug_str.slice(), LittleEndian); + + let mut line_strings = LineStringTable::default(); + line_strings.add("line string one"); + let line_string_id = line_strings.add("line string two"); + let mut debug_line_str = DebugLineStr::from(EndianVec::new(LittleEndian)); + let debug_line_str_offsets = line_strings.write(&mut debug_line_str).unwrap(); + let read_debug_line_str = + read::DebugLineStr::from(read::EndianSlice::new(debug_line_str.slice(), LittleEndian)); + + let data = vec![1, 2, 3, 4]; + let read_data = read::EndianSlice::new(&[1, 2, 3, 4], LittleEndian); + + let mut expression = Expression::new(); + expression.op_constu(57); + let read_expression = read::Expression(read::EndianSlice::new( + &[constants::DW_OP_constu.0, 57], + LittleEndian, + )); + + let mut ranges = RangeListTable::default(); + let range_id = ranges.add(RangeList(vec![Range::StartEnd { + begin: Address::Constant(0x1234), + end: Address::Constant(0x2345), + }])); + + let mut locations = LocationListTable::default(); + let loc_id = locations.add(LocationList(vec![Location::StartEnd { + begin: Address::Constant(0x1234), + end: Address::Constant(0x2345), + data: expression.clone(), + }])); + + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let range_list_offsets = ranges.write(&mut sections, encoding).unwrap(); + let loc_list_offsets = locations.write(&mut sections, encoding, None).unwrap(); + + let read_debug_ranges = + read::DebugRanges::new(sections.debug_ranges.slice(), LittleEndian); + let read_debug_rnglists = + read::DebugRngLists::new(sections.debug_rnglists.slice(), LittleEndian); + + let read_debug_loc = + read::DebugLoc::new(sections.debug_loc.slice(), LittleEndian); + let read_debug_loclists = + read::DebugLocLists::new(sections.debug_loclists.slice(), LittleEndian); + + let mut units = UnitTable::default(); + let unit = units.add(Unit::new(encoding, LineProgram::none())); + let unit = units.get(unit); + let encoding = Encoding { + format, + version, + address_size, + }; + let from_unit = read::UnitHeader::new( + encoding, + 0, + read::UnitType::Compilation, + DebugAbbrevOffset(0), + DebugInfoOffset(0).into(), + read::EndianSlice::new(&[], LittleEndian), + ); + + for &(ref name, ref value, ref expect_value) in &[ + ( + constants::DW_AT_name, + AttributeValue::Address(Address::Constant(0x1234)), + read::AttributeValue::Addr(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Block(data.clone()), + read::AttributeValue::Block(read_data), + ), + ( + constants::DW_AT_name, + AttributeValue::Data1(0x12), + read::AttributeValue::Data1(0x12), + ), + ( + constants::DW_AT_name, + AttributeValue::Data2(0x1234), + read::AttributeValue::Data2(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Data4(0x1234), + read::AttributeValue::Data4(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Data8(0x1234), + read::AttributeValue::Data8(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Sdata(0x1234), + read::AttributeValue::Sdata(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Udata(0x1234), + read::AttributeValue::Udata(0x1234), + ), + ( + constants::DW_AT_name, + AttributeValue::Exprloc(expression.clone()), + read::AttributeValue::Exprloc(read_expression), + ), + ( + constants::DW_AT_name, + AttributeValue::Flag(false), + read::AttributeValue::Flag(false), + ), + /* + ( + constants::DW_AT_name, + AttributeValue::FlagPresent, + read::AttributeValue::Flag(true), + ), + */ + ( + constants::DW_AT_name, + AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x1234)), + read::AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x1234)), + ), + ( + constants::DW_AT_location, + AttributeValue::LocationListRef(loc_id), + read::AttributeValue::SecOffset(loc_list_offsets.get(loc_id).0), + ), + ( + constants::DW_AT_macro_info, + AttributeValue::DebugMacinfoRef(DebugMacinfoOffset(0x1234)), + read::AttributeValue::SecOffset(0x1234), + ), + ( + constants::DW_AT_macros, + AttributeValue::DebugMacroRef(DebugMacroOffset(0x1234)), + read::AttributeValue::SecOffset(0x1234), + ), + ( + constants::DW_AT_ranges, + AttributeValue::RangeListRef(range_id), + read::AttributeValue::SecOffset(range_list_offsets.get(range_id).0), + ), + ( + constants::DW_AT_name, + AttributeValue::DebugTypesRef(DebugTypeSignature(0x1234)), + read::AttributeValue::DebugTypesRef(DebugTypeSignature(0x1234)), + ), + ( + constants::DW_AT_name, + AttributeValue::StringRef(string_id), + read::AttributeValue::DebugStrRef(debug_str_offsets.get(string_id)), + ), + ( + constants::DW_AT_name, + AttributeValue::DebugStrRefSup(DebugStrOffset(0x1234)), + read::AttributeValue::DebugStrRefSup(DebugStrOffset(0x1234)), + ), + ( + constants::DW_AT_name, + AttributeValue::LineStringRef(line_string_id), + read::AttributeValue::DebugLineStrRef( + debug_line_str_offsets.get(line_string_id), + ), + ), + ( + constants::DW_AT_name, + AttributeValue::String(data.clone()), + read::AttributeValue::String(read_data), + ), + ( + constants::DW_AT_encoding, + AttributeValue::Encoding(constants::DwAte(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_decimal_sign, + AttributeValue::DecimalSign(constants::DwDs(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_endianity, + AttributeValue::Endianity(constants::DwEnd(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_accessibility, + AttributeValue::Accessibility(constants::DwAccess(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_visibility, + AttributeValue::Visibility(constants::DwVis(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_virtuality, + AttributeValue::Virtuality(constants::DwVirtuality(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_language, + AttributeValue::Language(constants::DwLang(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_address_class, + AttributeValue::AddressClass(constants::DwAddr(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_identifier_case, + AttributeValue::IdentifierCase(constants::DwId(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_calling_convention, + AttributeValue::CallingConvention(constants::DwCc(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_ordering, + AttributeValue::Ordering(constants::DwOrd(0x12)), + read::AttributeValue::Udata(0x12), + ), + ( + constants::DW_AT_inline, + AttributeValue::Inline(constants::DwInl(0x12)), + read::AttributeValue::Udata(0x12), + ), + ][..] + { + let form = value.form(encoding).unwrap(); + let attr = Attribute { + name: *name, + value: value.clone(), + }; + + let offsets = UnitOffsets::none(); + let line_program_offset = None; + let mut debug_info_refs = Vec::new(); + let mut unit_refs = Vec::new(); + let mut debug_info = DebugInfo::from(EndianVec::new(LittleEndian)); + attr.value + .write( + &mut debug_info, + &mut debug_info_refs, + &mut unit_refs, + &unit, + &offsets, + line_program_offset, + &debug_line_str_offsets, + &debug_str_offsets, + &range_list_offsets, + &loc_list_offsets, + ) + .unwrap(); + + let spec = read::AttributeSpecification::new(*name, form, None); + let mut r = read::EndianSlice::new(debug_info.slice(), LittleEndian); + let read_attr = read::parse_attribute(&mut r, encoding, spec).unwrap(); + let read_value = &read_attr.raw_value(); + // read::AttributeValue is invariant in the lifetime of R. + // The lifetimes here are all okay, so transmute it. + let read_value = unsafe { + mem::transmute::< + &read::AttributeValue<read::EndianSlice<LittleEndian>>, + &read::AttributeValue<read::EndianSlice<LittleEndian>>, + >(read_value) + }; + assert_eq!(read_value, expect_value); + + let dwarf = read::Dwarf { + debug_str: read_debug_str.clone(), + debug_line_str: read_debug_line_str.clone(), + ranges: read::RangeLists::new(read_debug_ranges, read_debug_rnglists), + locations: read::LocationLists::new( + read_debug_loc, + read_debug_loclists, + ), + ..Default::default() + }; + + let unit = read::Unit { + header: from_unit, + abbreviations: read::Abbreviations::default(), + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase(0), + addr_base: DebugAddrBase(0), + loclists_base: DebugLocListsBase(0), + rnglists_base: DebugRngListsBase(0), + line_program: None, + dwo_id: None, + }; + + let mut context = convert::ConvertUnitContext { + dwarf: &dwarf, + unit: &unit, + line_strings: &mut line_strings, + strings: &mut strings, + ranges: &mut ranges, + locations: &mut locations, + convert_address: &|address| Some(Address::Constant(address)), + base_address: Address::Constant(0), + line_program_offset: None, + line_program_files: Vec::new(), + entry_ids: &HashMap::new(), + }; + + let convert_attr = + Attribute::from(&mut context, &read_attr).unwrap().unwrap(); + assert_eq!(convert_attr, attr); + } + } + } + } + } + + #[test] + #[allow(clippy::cyclomatic_complexity)] + fn test_unit_ref() { + let mut units = UnitTable::default(); + let unit_id1 = units.add(Unit::new( + Encoding { + version: 4, + address_size: 8, + format: Format::Dwarf32, + }, + LineProgram::none(), + )); + assert_eq!(unit_id1, units.id(0)); + let unit_id2 = units.add(Unit::new( + Encoding { + version: 2, + address_size: 4, + format: Format::Dwarf64, + }, + LineProgram::none(), + )); + assert_eq!(unit_id2, units.id(1)); + let unit1_child1 = UnitEntryId::new(units.get(unit_id1).base_id, 1); + let unit1_child2 = UnitEntryId::new(units.get(unit_id1).base_id, 2); + let unit2_child1 = UnitEntryId::new(units.get(unit_id2).base_id, 1); + let unit2_child2 = UnitEntryId::new(units.get(unit_id2).base_id, 2); + { + let unit1 = units.get_mut(unit_id1); + let root = unit1.root(); + let child_id1 = unit1.add(root, constants::DW_TAG_subprogram); + assert_eq!(child_id1, unit1_child1); + let child_id2 = unit1.add(root, constants::DW_TAG_subprogram); + assert_eq!(child_id2, unit1_child2); + { + let child1 = unit1.get_mut(child_id1); + child1.set(constants::DW_AT_type, AttributeValue::UnitRef(child_id2)); + } + { + let child2 = unit1.get_mut(child_id2); + child2.set( + constants::DW_AT_type, + AttributeValue::DebugInfoRef(Reference::Entry(unit_id2, unit2_child1)), + ); + } + } + { + let unit2 = units.get_mut(unit_id2); + let root = unit2.root(); + let child_id1 = unit2.add(root, constants::DW_TAG_subprogram); + assert_eq!(child_id1, unit2_child1); + let child_id2 = unit2.add(root, constants::DW_TAG_subprogram); + assert_eq!(child_id2, unit2_child2); + { + let child1 = unit2.get_mut(child_id1); + child1.set(constants::DW_AT_type, AttributeValue::UnitRef(child_id2)); + } + { + let child2 = unit2.get_mut(child_id2); + child2.set( + constants::DW_AT_type, + AttributeValue::DebugInfoRef(Reference::Entry(unit_id1, unit1_child1)), + ); + } + } + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + let debug_info_offsets = units + .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets) + .unwrap(); + + println!("{:?}", sections.debug_info); + println!("{:?}", sections.debug_abbrev); + + let dwarf = read::Dwarf { + debug_abbrev: read::DebugAbbrev::new(sections.debug_abbrev.slice(), LittleEndian), + debug_info: read::DebugInfo::new(sections.debug_info.slice(), LittleEndian), + ..Default::default() + }; + + let mut read_units = dwarf.units(); + { + let read_unit1 = read_units.next().unwrap().unwrap(); + assert_eq!( + read_unit1.offset(), + debug_info_offsets.unit(unit_id1).into() + ); + + let abbrevs = dwarf.abbreviations(&read_unit1).unwrap(); + let mut read_entries = read_unit1.entries(&abbrevs); + { + let (_, _read_root) = read_entries.next_dfs().unwrap().unwrap(); + } + { + let (_, read_child1) = read_entries.next_dfs().unwrap().unwrap(); + let offset = debug_info_offsets + .entry(unit_id1, unit1_child2) + .to_unit_offset(&read_unit1) + .unwrap(); + assert_eq!( + read_child1.attr_value(constants::DW_AT_type).unwrap(), + Some(read::AttributeValue::UnitRef(offset)) + ); + } + { + let (_, read_child2) = read_entries.next_dfs().unwrap().unwrap(); + let offset = debug_info_offsets.entry(unit_id2, unit2_child1); + assert_eq!( + read_child2.attr_value(constants::DW_AT_type).unwrap(), + Some(read::AttributeValue::DebugInfoRef(offset)) + ); + } + } + { + let read_unit2 = read_units.next().unwrap().unwrap(); + assert_eq!( + read_unit2.offset(), + debug_info_offsets.unit(unit_id2).into() + ); + + let abbrevs = dwarf.abbreviations(&read_unit2).unwrap(); + let mut read_entries = read_unit2.entries(&abbrevs); + { + let (_, _read_root) = read_entries.next_dfs().unwrap().unwrap(); + } + { + let (_, read_child1) = read_entries.next_dfs().unwrap().unwrap(); + let offset = debug_info_offsets + .entry(unit_id2, unit2_child2) + .to_unit_offset(&read_unit2) + .unwrap(); + assert_eq!( + read_child1.attr_value(constants::DW_AT_type).unwrap(), + Some(read::AttributeValue::UnitRef(offset)) + ); + } + { + let (_, read_child2) = read_entries.next_dfs().unwrap().unwrap(); + let offset = debug_info_offsets.entry(unit_id1, unit1_child1); + assert_eq!( + read_child2.attr_value(constants::DW_AT_type).unwrap(), + Some(read::AttributeValue::DebugInfoRef(offset)) + ); + } + } + + let mut convert_line_strings = LineStringTable::default(); + let mut convert_strings = StringTable::default(); + let convert_units = UnitTable::from( + &dwarf, + &mut convert_line_strings, + &mut convert_strings, + &|address| Some(Address::Constant(address)), + ) + .unwrap(); + assert_eq!(convert_units.count(), units.count()); + + for i in 0..convert_units.count() { + let unit = units.get(units.id(i)); + let convert_unit = convert_units.get(convert_units.id(i)); + assert_eq!(convert_unit.version(), unit.version()); + assert_eq!(convert_unit.address_size(), unit.address_size()); + assert_eq!(convert_unit.format(), unit.format()); + assert_eq!(convert_unit.count(), unit.count()); + + let root = unit.get(unit.root()); + let convert_root = convert_unit.get(convert_unit.root()); + assert_eq!(convert_root.tag(), root.tag()); + for (convert_attr, attr) in convert_root.attrs().zip(root.attrs()) { + assert_eq!(convert_attr, attr); + } + + let child1 = unit.get(UnitEntryId::new(unit.base_id, 1)); + let convert_child1 = convert_unit.get(UnitEntryId::new(convert_unit.base_id, 1)); + assert_eq!(convert_child1.tag(), child1.tag()); + for (convert_attr, attr) in convert_child1.attrs().zip(child1.attrs()) { + assert_eq!(convert_attr.name, attr.name); + match (convert_attr.value.clone(), attr.value.clone()) { + ( + AttributeValue::DebugInfoRef(Reference::Entry(convert_unit, convert_entry)), + AttributeValue::DebugInfoRef(Reference::Entry(unit, entry)), + ) => { + assert_eq!(convert_unit.index, unit.index); + assert_eq!(convert_entry.index, entry.index); + } + (AttributeValue::UnitRef(convert_id), AttributeValue::UnitRef(id)) => { + assert_eq!(convert_id.index, id.index); + } + (convert_value, value) => assert_eq!(convert_value, value), + } + } + + let child2 = unit.get(UnitEntryId::new(unit.base_id, 2)); + let convert_child2 = convert_unit.get(UnitEntryId::new(convert_unit.base_id, 2)); + assert_eq!(convert_child2.tag(), child2.tag()); + for (convert_attr, attr) in convert_child2.attrs().zip(child2.attrs()) { + assert_eq!(convert_attr.name, attr.name); + match (convert_attr.value.clone(), attr.value.clone()) { + ( + AttributeValue::DebugInfoRef(Reference::Entry(convert_unit, convert_entry)), + AttributeValue::DebugInfoRef(Reference::Entry(unit, entry)), + ) => { + assert_eq!(convert_unit.index, unit.index); + assert_eq!(convert_entry.index, entry.index); + } + (AttributeValue::UnitRef(convert_id), AttributeValue::UnitRef(id)) => { + assert_eq!(convert_id.index, id.index); + } + (convert_value, value) => assert_eq!(convert_value, value), + } + } + } + } + + #[test] + fn test_sibling() { + fn add_child( + unit: &mut Unit, + parent: UnitEntryId, + tag: constants::DwTag, + name: &str, + ) -> UnitEntryId { + let id = unit.add(parent, tag); + let child = unit.get_mut(id); + child.set(constants::DW_AT_name, AttributeValue::String(name.into())); + child.set_sibling(true); + id + } + + fn add_children(units: &mut UnitTable, unit_id: UnitId) { + let unit = units.get_mut(unit_id); + let root = unit.root(); + let child1 = add_child(unit, root, constants::DW_TAG_subprogram, "child1"); + add_child(unit, child1, constants::DW_TAG_variable, "grandchild1"); + add_child(unit, root, constants::DW_TAG_subprogram, "child2"); + add_child(unit, root, constants::DW_TAG_subprogram, "child3"); + } + + fn next_child<R: read::Reader<Offset = usize>>( + entries: &mut read::EntriesCursor<R>, + ) -> (read::UnitOffset, Option<read::UnitOffset>) { + let (_, entry) = entries.next_dfs().unwrap().unwrap(); + let offset = entry.offset(); + let sibling = + entry + .attr_value(constants::DW_AT_sibling) + .unwrap() + .map(|attr| match attr { + read::AttributeValue::UnitRef(offset) => offset, + _ => panic!("bad sibling value"), + }); + (offset, sibling) + } + + fn check_sibling<R: read::Reader<Offset = usize>>( + unit: &read::UnitHeader<R>, + debug_abbrev: &read::DebugAbbrev<R>, + ) { + let abbrevs = unit.abbreviations(debug_abbrev).unwrap(); + let mut entries = unit.entries(&abbrevs); + // root + entries.next_dfs().unwrap().unwrap(); + // child1 + let (_, sibling1) = next_child(&mut entries); + // grandchild1 + entries.next_dfs().unwrap().unwrap(); + // child2 + let (offset2, sibling2) = next_child(&mut entries); + // child3 + let (_, _) = next_child(&mut entries); + assert_eq!(sibling1, Some(offset2)); + assert_eq!(sibling2, None); + } + + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 8, + }; + let mut units = UnitTable::default(); + let unit_id1 = units.add(Unit::new(encoding, LineProgram::none())); + add_children(&mut units, unit_id1); + let unit_id2 = units.add(Unit::new(encoding, LineProgram::none())); + add_children(&mut units, unit_id2); + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + units + .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets) + .unwrap(); + + println!("{:?}", sections.debug_info); + println!("{:?}", sections.debug_abbrev); + + let read_debug_info = read::DebugInfo::new(sections.debug_info.slice(), LittleEndian); + let read_debug_abbrev = read::DebugAbbrev::new(sections.debug_abbrev.slice(), LittleEndian); + let mut read_units = read_debug_info.units(); + check_sibling(&read_units.next().unwrap().unwrap(), &read_debug_abbrev); + check_sibling(&read_units.next().unwrap().unwrap(), &read_debug_abbrev); + } + + #[test] + fn test_line_ref() { + for &version in &[2, 3, 4, 5] { + for &address_size in &[4, 8] { + for &format in &[Format::Dwarf32, Format::Dwarf64] { + let encoding = Encoding { + format, + version, + address_size, + }; + + // The line program we'll be referencing. + let mut line_program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(b"comp_dir".to_vec()), + LineString::String(b"comp_name".to_vec()), + None, + ); + let dir = line_program.default_directory(); + let file1 = + line_program.add_file(LineString::String(b"file1".to_vec()), dir, None); + let file2 = + line_program.add_file(LineString::String(b"file2".to_vec()), dir, None); + + // Write, read, and convert the line program, so that we have the info + // required to convert the attributes. + let line_strings = DebugLineStrOffsets::none(); + let strings = DebugStrOffsets::none(); + let mut debug_line = DebugLine::from(EndianVec::new(LittleEndian)); + let line_program_offset = line_program + .write(&mut debug_line, encoding, &line_strings, &strings) + .unwrap(); + let read_debug_line = read::DebugLine::new(debug_line.slice(), LittleEndian); + let read_line_program = read_debug_line + .program( + line_program_offset, + address_size, + Some(read::EndianSlice::new(b"comp_dir", LittleEndian)), + Some(read::EndianSlice::new(b"comp_name", LittleEndian)), + ) + .unwrap(); + let dwarf = read::Dwarf::default(); + let mut convert_line_strings = LineStringTable::default(); + let mut convert_strings = StringTable::default(); + let (_, line_program_files) = LineProgram::from( + read_line_program, + &dwarf, + &mut convert_line_strings, + &mut convert_strings, + &|address| Some(Address::Constant(address)), + ) + .unwrap(); + + // Fake the unit. + let mut units = UnitTable::default(); + let unit = units.add(Unit::new(encoding, LineProgram::none())); + let unit = units.get(unit); + let from_unit = read::UnitHeader::new( + encoding, + 0, + read::UnitType::Compilation, + DebugAbbrevOffset(0), + DebugInfoOffset(0).into(), + read::EndianSlice::new(&[], LittleEndian), + ); + + for &(ref name, ref value, ref expect_value) in &[ + ( + constants::DW_AT_stmt_list, + AttributeValue::LineProgramRef, + read::AttributeValue::SecOffset(line_program_offset.0), + ), + ( + constants::DW_AT_decl_file, + AttributeValue::FileIndex(Some(file1)), + read::AttributeValue::Udata(file1.raw()), + ), + ( + constants::DW_AT_decl_file, + AttributeValue::FileIndex(Some(file2)), + read::AttributeValue::Udata(file2.raw()), + ), + ][..] + { + let mut ranges = RangeListTable::default(); + let mut locations = LocationListTable::default(); + let mut strings = StringTable::default(); + let mut line_strings = LineStringTable::default(); + + let form = value.form(encoding).unwrap(); + let attr = Attribute { + name: *name, + value: value.clone(), + }; + + let mut debug_info_refs = Vec::new(); + let mut unit_refs = Vec::new(); + let mut debug_info = DebugInfo::from(EndianVec::new(LittleEndian)); + let offsets = UnitOffsets::none(); + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + let range_list_offsets = RangeListOffsets::none(); + let loc_list_offsets = LocationListOffsets::none(); + attr.value + .write( + &mut debug_info, + &mut debug_info_refs, + &mut unit_refs, + &unit, + &offsets, + Some(line_program_offset), + &debug_line_str_offsets, + &debug_str_offsets, + &range_list_offsets, + &loc_list_offsets, + ) + .unwrap(); + + let spec = read::AttributeSpecification::new(*name, form, None); + let mut r = read::EndianSlice::new(debug_info.slice(), LittleEndian); + let read_attr = read::parse_attribute(&mut r, encoding, spec).unwrap(); + let read_value = &read_attr.raw_value(); + // read::AttributeValue is invariant in the lifetime of R. + // The lifetimes here are all okay, so transmute it. + let read_value = unsafe { + mem::transmute::< + &read::AttributeValue<read::EndianSlice<LittleEndian>>, + &read::AttributeValue<read::EndianSlice<LittleEndian>>, + >(read_value) + }; + assert_eq!(read_value, expect_value); + + let unit = read::Unit { + header: from_unit, + abbreviations: read::Abbreviations::default(), + name: None, + comp_dir: None, + low_pc: 0, + str_offsets_base: DebugStrOffsetsBase(0), + addr_base: DebugAddrBase(0), + loclists_base: DebugLocListsBase(0), + rnglists_base: DebugRngListsBase(0), + line_program: None, + dwo_id: None, + }; + + let mut context = convert::ConvertUnitContext { + dwarf: &dwarf, + unit: &unit, + line_strings: &mut line_strings, + strings: &mut strings, + ranges: &mut ranges, + locations: &mut locations, + convert_address: &|address| Some(Address::Constant(address)), + base_address: Address::Constant(0), + line_program_offset: Some(line_program_offset), + line_program_files: line_program_files.clone(), + entry_ids: &HashMap::new(), + }; + + let convert_attr = + Attribute::from(&mut context, &read_attr).unwrap().unwrap(); + assert_eq!(convert_attr, attr); + } + } + } + } + } + + #[test] + fn test_line_program_used() { + for used in vec![false, true] { + let encoding = Encoding { + format: Format::Dwarf32, + version: 5, + address_size: 8, + }; + + let line_program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::String(b"comp_dir".to_vec()), + LineString::String(b"comp_name".to_vec()), + None, + ); + + let mut unit = Unit::new(encoding, line_program); + let file_id = if used { Some(FileId::new(0)) } else { None }; + let root = unit.root(); + unit.get_mut(root).set( + constants::DW_AT_decl_file, + AttributeValue::FileIndex(file_id), + ); + + let mut units = UnitTable::default(); + units.add(unit); + + let debug_line_str_offsets = DebugLineStrOffsets::none(); + let debug_str_offsets = DebugStrOffsets::none(); + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + units + .write(&mut sections, &debug_line_str_offsets, &debug_str_offsets) + .unwrap(); + assert_eq!(!used, sections.debug_line.slice().is_empty()); + } + } + + #[test] + fn test_delete_child() { + fn set_name(unit: &mut Unit, id: UnitEntryId, name: &str) { + let entry = unit.get_mut(id); + entry.set(constants::DW_AT_name, AttributeValue::String(name.into())); + } + fn check_name<R: read::Reader>( + entry: &read::DebuggingInformationEntry<R>, + debug_str: &read::DebugStr<R>, + name: &str, + ) { + let name_attr = entry.attr(constants::DW_AT_name).unwrap().unwrap(); + let entry_name = name_attr.string_value(debug_str).unwrap(); + let entry_name_str = entry_name.to_string().unwrap(); + assert_eq!(entry_name_str, name); + } + let encoding = Encoding { + format: Format::Dwarf32, + version: 4, + address_size: 8, + }; + let mut dwarf = DwarfUnit::new(encoding); + let root = dwarf.unit.root(); + + // Add and delete entries in the root unit + let child1 = dwarf.unit.add(root, constants::DW_TAG_subprogram); + set_name(&mut dwarf.unit, child1, "child1"); + let grandchild1 = dwarf.unit.add(child1, constants::DW_TAG_variable); + set_name(&mut dwarf.unit, grandchild1, "grandchild1"); + let child2 = dwarf.unit.add(root, constants::DW_TAG_subprogram); + set_name(&mut dwarf.unit, child2, "child2"); + // This deletes both `child1` and its child `grandchild1` + dwarf.unit.get_mut(root).delete_child(child1); + let child3 = dwarf.unit.add(root, constants::DW_TAG_subprogram); + set_name(&mut dwarf.unit, child3, "child3"); + let child4 = dwarf.unit.add(root, constants::DW_TAG_subprogram); + set_name(&mut dwarf.unit, child4, "child4"); + let grandchild4 = dwarf.unit.add(child4, constants::DW_TAG_variable); + set_name(&mut dwarf.unit, grandchild4, "grandchild4"); + dwarf.unit.get_mut(child4).delete_child(grandchild4); + + let mut sections = Sections::new(EndianVec::new(LittleEndian)); + + // Write DWARF data which should only include `child2`, `child3` and `child4` + dwarf.write(&mut sections).unwrap(); + + let read_debug_info = read::DebugInfo::new(sections.debug_info.slice(), LittleEndian); + let read_debug_abbrev = read::DebugAbbrev::new(sections.debug_abbrev.slice(), LittleEndian); + let read_debug_str = read::DebugStr::new(sections.debug_str.slice(), LittleEndian); + let read_unit = read_debug_info.units().next().unwrap().unwrap(); + let abbrevs = read_unit.abbreviations(&read_debug_abbrev).unwrap(); + let mut entries = read_unit.entries(&abbrevs); + // root + entries.next_dfs().unwrap().unwrap(); + // child2 + let (_, read_child2) = entries.next_dfs().unwrap().unwrap(); + check_name(read_child2, &read_debug_str, "child2"); + // child3 + let (_, read_child3) = entries.next_dfs().unwrap().unwrap(); + check_name(read_child3, &read_debug_str, "child3"); + // child4 + let (_, read_child4) = entries.next_dfs().unwrap().unwrap(); + check_name(read_child4, &read_debug_str, "child4"); + // There should be no more entries + assert!(entries.next_dfs().unwrap().is_none()); + } +} diff --git a/vendor/gimli/src/write/writer.rs b/vendor/gimli/src/write/writer.rs new file mode 100644 index 000000000..0785d1686 --- /dev/null +++ b/vendor/gimli/src/write/writer.rs @@ -0,0 +1,497 @@ +use crate::common::{Format, SectionId}; +use crate::constants; +use crate::endianity::Endianity; +use crate::leb128; +use crate::write::{Address, Error, Result}; + +/// A trait for writing the data to a DWARF section. +/// +/// All write operations append to the section unless otherwise specified. +#[allow(clippy::len_without_is_empty)] +pub trait Writer { + /// The endianity of bytes that are written. + type Endian: Endianity; + + /// Return the endianity of bytes that are written. + fn endian(&self) -> Self::Endian; + + /// Return the current section length. + /// + /// This may be used as an offset for future `write_at` calls. + fn len(&self) -> usize; + + /// Write a slice. + fn write(&mut self, bytes: &[u8]) -> Result<()>; + + /// Write a slice at a given offset. + /// + /// The write must not extend past the current section length. + fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()>; + + /// Write an address. + /// + /// If the writer supports relocations, then it must provide its own implementation + /// of this method. + // TODO: use write_reference instead? + fn write_address(&mut self, address: Address, size: u8) -> Result<()> { + match address { + Address::Constant(val) => self.write_udata(val, size), + Address::Symbol { .. } => Err(Error::InvalidAddress), + } + } + + /// Write an address with a `.eh_frame` pointer encoding. + /// + /// The given size is only used for `DW_EH_PE_absptr` formats. + /// + /// If the writer supports relocations, then it must provide its own implementation + /// of this method. + fn write_eh_pointer( + &mut self, + address: Address, + eh_pe: constants::DwEhPe, + size: u8, + ) -> Result<()> { + match address { + Address::Constant(val) => { + // Indirect doesn't matter here. + let val = match eh_pe.application() { + constants::DW_EH_PE_absptr => val, + constants::DW_EH_PE_pcrel => { + // TODO: better handling of sign + let offset = self.len() as u64; + val.wrapping_sub(offset) + } + _ => { + return Err(Error::UnsupportedPointerEncoding(eh_pe)); + } + }; + self.write_eh_pointer_data(val, eh_pe.format(), size) + } + Address::Symbol { .. } => Err(Error::InvalidAddress), + } + } + + /// Write a value with a `.eh_frame` pointer format. + /// + /// The given size is only used for `DW_EH_PE_absptr` formats. + /// + /// This must not be used directly for values that may require relocation. + fn write_eh_pointer_data( + &mut self, + val: u64, + format: constants::DwEhPe, + size: u8, + ) -> Result<()> { + match format { + constants::DW_EH_PE_absptr => self.write_udata(val, size), + constants::DW_EH_PE_uleb128 => self.write_uleb128(val), + constants::DW_EH_PE_udata2 => self.write_udata(val, 2), + constants::DW_EH_PE_udata4 => self.write_udata(val, 4), + constants::DW_EH_PE_udata8 => self.write_udata(val, 8), + constants::DW_EH_PE_sleb128 => self.write_sleb128(val as i64), + constants::DW_EH_PE_sdata2 => self.write_sdata(val as i64, 2), + constants::DW_EH_PE_sdata4 => self.write_sdata(val as i64, 4), + constants::DW_EH_PE_sdata8 => self.write_sdata(val as i64, 8), + _ => { + return Err(Error::UnsupportedPointerEncoding(format)); + } + } + } + + /// Write an offset that is relative to the start of the given section. + /// + /// If the writer supports relocations, then it must provide its own implementation + /// of this method. + fn write_offset(&mut self, val: usize, _section: SectionId, size: u8) -> Result<()> { + self.write_udata(val as u64, size) + } + + /// Write an offset that is relative to the start of the given section. + /// + /// If the writer supports relocations, then it must provide its own implementation + /// of this method. + fn write_offset_at( + &mut self, + offset: usize, + val: usize, + _section: SectionId, + size: u8, + ) -> Result<()> { + self.write_udata_at(offset, val as u64, size) + } + + /// Write a reference to a symbol. + /// + /// If the writer supports symbols, then it must provide its own implementation + /// of this method. + fn write_reference(&mut self, _symbol: usize, _size: u8) -> Result<()> { + Err(Error::InvalidReference) + } + + /// Write a u8. + fn write_u8(&mut self, val: u8) -> Result<()> { + let bytes = [val]; + self.write(&bytes) + } + + /// Write a u16. + fn write_u16(&mut self, val: u16) -> Result<()> { + let mut bytes = [0; 2]; + self.endian().write_u16(&mut bytes, val); + self.write(&bytes) + } + + /// Write a u32. + fn write_u32(&mut self, val: u32) -> Result<()> { + let mut bytes = [0; 4]; + self.endian().write_u32(&mut bytes, val); + self.write(&bytes) + } + + /// Write a u64. + fn write_u64(&mut self, val: u64) -> Result<()> { + let mut bytes = [0; 8]; + self.endian().write_u64(&mut bytes, val); + self.write(&bytes) + } + + /// Write a u8 at the given offset. + fn write_u8_at(&mut self, offset: usize, val: u8) -> Result<()> { + let bytes = [val]; + self.write_at(offset, &bytes) + } + + /// Write a u16 at the given offset. + fn write_u16_at(&mut self, offset: usize, val: u16) -> Result<()> { + let mut bytes = [0; 2]; + self.endian().write_u16(&mut bytes, val); + self.write_at(offset, &bytes) + } + + /// Write a u32 at the given offset. + fn write_u32_at(&mut self, offset: usize, val: u32) -> Result<()> { + let mut bytes = [0; 4]; + self.endian().write_u32(&mut bytes, val); + self.write_at(offset, &bytes) + } + + /// Write a u64 at the given offset. + fn write_u64_at(&mut self, offset: usize, val: u64) -> Result<()> { + let mut bytes = [0; 8]; + self.endian().write_u64(&mut bytes, val); + self.write_at(offset, &bytes) + } + + /// Write unsigned data of the given size. + /// + /// Returns an error if the value is too large for the size. + /// This must not be used directly for values that may require relocation. + fn write_udata(&mut self, val: u64, size: u8) -> Result<()> { + match size { + 1 => { + let write_val = val as u8; + if val != u64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u8(write_val) + } + 2 => { + let write_val = val as u16; + if val != u64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u16(write_val) + } + 4 => { + let write_val = val as u32; + if val != u64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u32(write_val) + } + 8 => self.write_u64(val), + otherwise => Err(Error::UnsupportedWordSize(otherwise)), + } + } + + /// Write signed data of the given size. + /// + /// Returns an error if the value is too large for the size. + /// This must not be used directly for values that may require relocation. + fn write_sdata(&mut self, val: i64, size: u8) -> Result<()> { + match size { + 1 => { + let write_val = val as i8; + if val != i64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u8(write_val as u8) + } + 2 => { + let write_val = val as i16; + if val != i64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u16(write_val as u16) + } + 4 => { + let write_val = val as i32; + if val != i64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u32(write_val as u32) + } + 8 => self.write_u64(val as u64), + otherwise => Err(Error::UnsupportedWordSize(otherwise)), + } + } + + /// Write a word of the given size at the given offset. + /// + /// Returns an error if the value is too large for the size. + /// This must not be used directly for values that may require relocation. + fn write_udata_at(&mut self, offset: usize, val: u64, size: u8) -> Result<()> { + match size { + 1 => { + let write_val = val as u8; + if val != u64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u8_at(offset, write_val) + } + 2 => { + let write_val = val as u16; + if val != u64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u16_at(offset, write_val) + } + 4 => { + let write_val = val as u32; + if val != u64::from(write_val) { + return Err(Error::ValueTooLarge); + } + self.write_u32_at(offset, write_val) + } + 8 => self.write_u64_at(offset, val), + otherwise => Err(Error::UnsupportedWordSize(otherwise)), + } + } + + /// Write an unsigned LEB128 encoded integer. + fn write_uleb128(&mut self, val: u64) -> Result<()> { + let mut bytes = [0u8; 10]; + // bytes is long enough so this will never fail. + let len = leb128::write::unsigned(&mut { &mut bytes[..] }, val).unwrap(); + self.write(&bytes[..len]) + } + + /// Read an unsigned LEB128 encoded integer. + fn write_sleb128(&mut self, val: i64) -> Result<()> { + let mut bytes = [0u8; 10]; + // bytes is long enough so this will never fail. + let len = leb128::write::signed(&mut { &mut bytes[..] }, val).unwrap(); + self.write(&bytes[..len]) + } + + /// Write an initial length according to the given DWARF format. + /// + /// This will only write a length of zero, since the length isn't + /// known yet, and a subsequent call to `write_initial_length_at` + /// will write the actual length. + fn write_initial_length(&mut self, format: Format) -> Result<InitialLengthOffset> { + if format == Format::Dwarf64 { + self.write_u32(0xffff_ffff)?; + } + let offset = InitialLengthOffset(self.len()); + self.write_udata(0, format.word_size())?; + Ok(offset) + } + + /// Write an initial length at the given offset according to the given DWARF format. + /// + /// `write_initial_length` must have previously returned the offset. + fn write_initial_length_at( + &mut self, + offset: InitialLengthOffset, + length: u64, + format: Format, + ) -> Result<()> { + self.write_udata_at(offset.0, length, format.word_size()) + } +} + +/// The offset at which an initial length should be written. +#[derive(Debug, Clone, Copy)] +pub struct InitialLengthOffset(usize); + +#[cfg(test)] +mod tests { + use super::*; + use crate::write; + use crate::{BigEndian, LittleEndian}; + use std::{i64, u64}; + + #[test] + #[allow(clippy::cyclomatic_complexity)] + fn test_writer() { + let mut w = write::EndianVec::new(LittleEndian); + w.write_address(Address::Constant(0x1122_3344), 4).unwrap(); + assert_eq!(w.slice(), &[0x44, 0x33, 0x22, 0x11]); + assert_eq!( + w.write_address( + Address::Symbol { + symbol: 0, + addend: 0 + }, + 4 + ), + Err(Error::InvalidAddress) + ); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_offset(0x1122_3344, SectionId::DebugInfo, 4) + .unwrap(); + assert_eq!(w.slice(), &[0x44, 0x33, 0x22, 0x11]); + w.write_offset_at(1, 0x5566, SectionId::DebugInfo, 2) + .unwrap(); + assert_eq!(w.slice(), &[0x44, 0x66, 0x55, 0x11]); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_u8(0x11).unwrap(); + w.write_u16(0x2233).unwrap(); + w.write_u32(0x4455_6677).unwrap(); + w.write_u64(0x8081_8283_8485_8687).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x11, + 0x33, 0x22, + 0x77, 0x66, 0x55, 0x44, + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + ]); + w.write_u8_at(14, 0x11).unwrap(); + w.write_u16_at(12, 0x2233).unwrap(); + w.write_u32_at(8, 0x4455_6677).unwrap(); + w.write_u64_at(0, 0x8081_8283_8485_8687).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + 0x77, 0x66, 0x55, 0x44, + 0x33, 0x22, + 0x11, + ]); + + let mut w = write::EndianVec::new(BigEndian); + w.write_u8(0x11).unwrap(); + w.write_u16(0x2233).unwrap(); + w.write_u32(0x4455_6677).unwrap(); + w.write_u64(0x8081_8283_8485_8687).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x11, + 0x22, 0x33, + 0x44, 0x55, 0x66, 0x77, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + ]); + w.write_u8_at(14, 0x11).unwrap(); + w.write_u16_at(12, 0x2233).unwrap(); + w.write_u32_at(8, 0x4455_6677).unwrap(); + w.write_u64_at(0, 0x8081_8283_8485_8687).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x44, 0x55, 0x66, 0x77, + 0x22, 0x33, + 0x11, + ]); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_udata(0x11, 1).unwrap(); + w.write_udata(0x2233, 2).unwrap(); + w.write_udata(0x4455_6677, 4).unwrap(); + w.write_udata(0x8081_8283_8485_8687, 8).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x11, + 0x33, 0x22, + 0x77, 0x66, 0x55, 0x44, + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + ]); + assert_eq!(w.write_udata(0x100, 1), Err(Error::ValueTooLarge)); + assert_eq!(w.write_udata(0x1_0000, 2), Err(Error::ValueTooLarge)); + assert_eq!(w.write_udata(0x1_0000_0000, 4), Err(Error::ValueTooLarge)); + assert_eq!(w.write_udata(0x00, 3), Err(Error::UnsupportedWordSize(3))); + w.write_udata_at(14, 0x11, 1).unwrap(); + w.write_udata_at(12, 0x2233, 2).unwrap(); + w.write_udata_at(8, 0x4455_6677, 4).unwrap(); + w.write_udata_at(0, 0x8081_8283_8485_8687, 8).unwrap(); + #[rustfmt::skip] + assert_eq!(w.slice(), &[ + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + 0x77, 0x66, 0x55, 0x44, + 0x33, 0x22, + 0x11, + ]); + assert_eq!(w.write_udata_at(0, 0x100, 1), Err(Error::ValueTooLarge)); + assert_eq!(w.write_udata_at(0, 0x1_0000, 2), Err(Error::ValueTooLarge)); + assert_eq!( + w.write_udata_at(0, 0x1_0000_0000, 4), + Err(Error::ValueTooLarge) + ); + assert_eq!( + w.write_udata_at(0, 0x00, 3), + Err(Error::UnsupportedWordSize(3)) + ); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_uleb128(0).unwrap(); + assert_eq!(w.slice(), &[0]); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_uleb128(u64::MAX).unwrap(); + assert_eq!( + w.slice(), + &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1] + ); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_sleb128(0).unwrap(); + assert_eq!(w.slice(), &[0]); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_sleb128(i64::MAX).unwrap(); + assert_eq!( + w.slice(), + &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0] + ); + + let mut w = write::EndianVec::new(LittleEndian); + w.write_sleb128(i64::MIN).unwrap(); + assert_eq!( + w.slice(), + &[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f] + ); + + let mut w = write::EndianVec::new(LittleEndian); + let offset = w.write_initial_length(Format::Dwarf32).unwrap(); + assert_eq!(w.slice(), &[0, 0, 0, 0]); + w.write_initial_length_at(offset, 0x1122_3344, Format::Dwarf32) + .unwrap(); + assert_eq!(w.slice(), &[0x44, 0x33, 0x22, 0x11]); + assert_eq!( + w.write_initial_length_at(offset, 0x1_0000_0000, Format::Dwarf32), + Err(Error::ValueTooLarge) + ); + + let mut w = write::EndianVec::new(LittleEndian); + let offset = w.write_initial_length(Format::Dwarf64).unwrap(); + assert_eq!(w.slice(), &[0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0]); + w.write_initial_length_at(offset, 0x1122_3344_5566_7788, Format::Dwarf64) + .unwrap(); + assert_eq!( + w.slice(), + &[0xff, 0xff, 0xff, 0xff, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11] + ); + } +} |