| 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()); |
| } |
| } |