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, } 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( &mut self, sections: &mut Sections, line_strings: &DebugLineStrOffsets, strings: &DebugStrOffsets, ) -> Result { 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( references: &mut Vec, 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, /// 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( &mut self, sections: &mut Sections, abbrev_offset: DebugAbbrevOffset, abbrevs: &mut AbbreviationTable, line_strings: &DebugLineStrOffsets, strings: &DebugStrOffsets, ) -> Result { 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, tag: constants::DwTag, /// Whether to emit `DW_AT_sibling`. sibling: bool, attrs: Vec, children: Vec, } 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, parent: Option, 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 { 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 { self.attrs.iter() } /// Iterate over the attributes of this entry for modification. #[inline] pub fn attrs_mut(&mut self) -> slice::IterMut { 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 { 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 { 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( &self, w: &mut DebugInfo, debug_info_refs: &mut Vec, unit_refs: &mut Vec<(DebugInfoOffset, UnitEntryId)>, unit: &Unit, offsets: &mut UnitOffsets, abbrevs: &mut AbbreviationTable, line_program: Option, 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 { 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), /// 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), /// 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), } impl AttributeValue { /// Return the form that will be used to encode this value. pub fn form(&self, encoding: Encoding) -> Result { // 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( &self, w: &mut DebugInfo, debug_info_refs: &mut Vec, unit_refs: &mut Vec<(DebugInfoOffset, UnitEntryId)>, unit: &Unit, offsets: &UnitOffsets, line_program: Option, 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, } 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, } 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> { from_unit: read::Unit, base_id: BaseId, encoding: Encoding, entries: Vec, entry_offsets: Vec, root: UnitEntryId, } pub(crate) struct ConvertUnitContext<'a, R: Reader> { pub dwarf: &'a read::Dwarf, pub unit: &'a read::Unit, 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
, pub base_address: Address, pub line_program_offset: Option, pub line_program_files: Vec, pub entry_ids: &'a HashMap, } 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>( dwarf: &read::Dwarf, line_strings: &mut write::LineStringTable, strings: &mut write::StringTable, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult { 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>( from_header: read::UnitHeader, unit_id: UnitId, entry_ids: &mut HashMap, dwarf: &read::Dwarf, ) -> ConvertResult> { 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>( unit: ConvertUnit, entry_ids: &HashMap, dwarf: &read::Dwarf, line_strings: &mut write::LineStringTable, strings: &mut write::StringTable, convert_address: &dyn Fn(u64) -> Option
, ) -> ConvertResult { 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>( from: read::EntriesTreeNode, from_unit: &read::Unit, base_id: BaseId, entries: &mut Vec, entry_offsets: &mut Vec, entry_ids: &mut HashMap, parent: Option, unit_id: UnitId, ) -> ConvertResult { 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>( &mut self, context: &mut ConvertUnitContext, 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>( context: &mut ConvertUnitContext, from: &read::Attribute, ) -> ConvertResult> { 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>( context: &mut ConvertUnitContext, from: read::AttributeValue, ) -> ConvertResult> { 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![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::AttributeValue>, >(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>( entries: &mut read::EntriesCursor, ) -> (read::UnitOffset, Option) { 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>( unit: &read::UnitHeader, debug_abbrev: &read::DebugAbbrev, ) { 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::AttributeValue>, >(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( entry: &read::DebuggingInformationEntry, debug_str: &read::DebugStr, 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()); } }