aboutsummaryrefslogtreecommitdiff
path: root/vendor/gimli/src/write/unit.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gimli/src/write/unit.rs')
-rw-r--r--vendor/gimli/src/write/unit.rs3152
1 files changed, 3152 insertions, 0 deletions
diff --git a/vendor/gimli/src/write/unit.rs b/vendor/gimli/src/write/unit.rs
new file mode 100644
index 0000000..dd8ba67
--- /dev/null
+++ b/vendor/gimli/src/write/unit.rs
@@ -0,0 +1,3152 @@
+use alloc::vec::Vec;
+use std::ops::{Deref, DerefMut};
+use std::{slice, usize};
+
+use crate::common::{
+ DebugAbbrevOffset, DebugInfoOffset, DebugLineOffset, DebugMacinfoOffset, DebugMacroOffset,
+ DebugStrOffset, DebugTypeSignature, 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 implementation:
+ // - 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,
+ 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.
+ 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,
+ 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,
+ 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.
+ 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)]
+ #[cfg(feature = "read")]
+ 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)]
+ #[cfg(feature = "read")]
+ 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::{DwoId, 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.
+ 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;
+ use std::sync::Arc;
+
+ #[test]
+ 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: Arc::new(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]
+ 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: Arc::new(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());
+ }
+}