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)] pubstruct 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] pubfn add(&mutself, unit: Unit) -> UnitId { let id = UnitId::new(self.base_id, self.units.len()); self.units.push(unit);
id
}
/// Return the number of units. #[inline] pubfn count(&self) -> usize { self.units.len()
}
/// Return the id of a unit. /// /// # Panics /// /// Panics if `index >= self.count()`. #[inline] pubfn 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] pubfn 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] pubfn get_mut(&mutself, id: UnitId) -> &mut Unit {
debug_assert_eq!(self.base_id, id.base_id);
&mutself.units[id.index]
}
/// Write the units to the given sections. /// /// `strings` must contain the `.debug_str` offsets of the corresponding /// `StringTable`. pubfn write<W: Writer>(
&mutself,
sections: &mut Sections<W>,
line_strings: &DebugLineStrOffsets,
strings: &DebugStrOffsets,
) -> Result<DebugInfoOffsets> { letmut offsets = DebugInfoOffsets {
base_id: self.base_id,
units: Vec::new(),
}; for unit in &mutself.units { // TODO: maybe share abbreviation tables let abbrev_offset = sections.debug_abbrev.offset(); letmut abbrevs = AbbreviationTable::default();
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)] pubstruct 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`. pubfn new(encoding: Encoding, line_program: LineProgram) -> Self { let base_id = BaseId::default(); let ranges = RangeListTable::default(); let locations = LocationListTable::default(); letmut 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] pubfn encoding(&self) -> Encoding { self.encoding
}
/// Return the DWARF version for this unit. #[inline] pubfn version(&self) -> u16 { self.encoding.version
}
/// Return the address size in bytes for this unit. #[inline] pubfn address_size(&self) -> u8 { self.encoding.address_size
}
/// Return the DWARF format for this unit. #[inline] pubfn 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] pubfn count(&self) -> usize { self.entries.len()
}
/// Return the id of the root entry. #[inline] pubfn 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] pubfn add(&mutself, parent: UnitEntryId, tag: constants::DwTag) -> UnitEntryId {
debug_assert_eq!(self.base_id, parent.base_id);
DebuggingInformationEntry::new(self.base_id, &mutself.entries, Some(parent), tag)
}
/// Get a reference to an entry. /// /// # Panics /// /// Panics if `id` is invalid. #[inline] pubfn 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] pubfn get_mut(&mutself, id: UnitEntryId) -> &mut DebuggingInformationEntry {
debug_assert_eq!(self.base_id, id.base_id);
&mutself.entries[id.index]
}
/// Return true if `self.line_program` is used by a DIE. fn line_program_in_use(&self) -> bool { ifself.line_program.is_none() { returnfalse;
} if !self.line_program.is_empty() { returntrue;
}
for entry in &self.entries { for attr in &entry.attrs { iflet AttributeValue::FileIndex(Some(_)) = attr.value { returntrue;
}
}
}
false
}
/// Write the unit to the given sections. pub(crate) fn write<W: Writer>(
&mutself,
sections: &mut Sections<W>,
abbrev_offset: DebugAbbrevOffset,
abbrevs: &mut AbbreviationTable,
line_strings: &DebugLineStrOffsets,
strings: &DebugStrOffsets,
) -> Result<UnitOffsets> { let line_program = ifself.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;
letmut 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();
// 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(); letmut 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 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(&mutself) { let root = &self.entries[self.root.index]; letmut root_children = Vec::with_capacity(root.children.len()); for entry in &root.children { ifself.entries[entry.index].tag == constants::DW_TAG_base_type {
root_children.push(*entry);
}
} for entry in &root.children { ifself.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)] pubstruct 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(),
}); iflet 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] pubfn id(&self) -> UnitEntryId { self.id
}
/// Return the parent of this entry. #[inline] pubfn parent(&self) -> Option<UnitEntryId> { self.parent
}
/// Return the tag of this entry. #[inline] pubfn tag(&self) -> constants::DwTag { self.tag
}
/// Return `true` if a `DW_AT_sibling` attribute will be emitted. #[inline] pubfn 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] pubfn set_sibling(&mutself, sibling: bool) { self.sibling = sibling;
}
/// Iterate over the attributes of this entry. #[inline] pubfn attrs(&self) -> slice::Iter<'_, Attribute> { self.attrs.iter()
}
/// Iterate over the attributes of this entry for modification. #[inline] pubfn attrs_mut(&mutself) -> slice::IterMut<'_, Attribute> { self.attrs.iter_mut()
}
/// Get an attribute. pubfn get(&self, name: constants::DwAt) -> Option<&AttributeValue> { self.attrs
.iter()
.find(|attr| attr.name == name)
.map(|attr| &attr.value)
}
/// Get an attribute for modification. pubfn get_mut(&mutself, name: constants::DwAt) -> Option<&le='color:red'>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. pubfn set(&mutself, name: constants::DwAt, value: AttributeValue) {
assert_ne!(name, constants::DW_AT_sibling); iflet 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. pubfn delete(&mutself, 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] pubfn children(&self) -> slice::Iter<'_, UnitEntryId> { self.children.iter()
}
/// Delete a child entry and all of its children. pubfn delete_child(&mutself, id: UnitEntryId) { self.children.retain(|&child| child != id);
}
/// Return the type abbreviation for this DIE. fn abbreviation(&self, encoding: Encoding) -> Result<Abbreviation> { letmut attrs = Vec::new();
ifself.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)?);
}
let sibling_offset = ifself.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)?;
}
iflet 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)] pubstruct Attribute {
name: constants::DwAt,
value: AttributeValue,
}
impl Attribute { /// Get the name of this attribute. #[inline] pubfn name(&self) -> constants::DwAt { self.name
}
/// Get the value of this attribute. #[inline] pubfn get(&self) -> &AttributeValue {
&self.value
}
/// Set the value of this attribute. #[inline] pubfn set(&mutself, 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)] pubenum 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. pubfn 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)
}
define_section!(
DebugInfo,
DebugInfoOffset, "A writable `.debug_info` section."
);
/// The section offsets of all elements within a `.debug_info` section. #[derive(Debug, Default)] pubstruct DebugInfoOffsets {
base_id: BaseId,
units: Vec<UnitOffsets>,
}
/// Get the `.debug_info` section offset for the given unit. #[inline] pubfn 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] pubfn 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>,
}
/// 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
}
}
/// 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 { usesuper::*; usecrate::common::{DwoId, UnitSectionOffset}; usecrate::read::{self, Reader}; usecrate::write::{self, ConvertError, ConvertResult, LocationList, RangeList}; use std::collections::HashMap;
impl UnitTable { /// Create a unit table by reading the data in the given sections. /// /// This also updates the given tables with the values that are referenced from /// attributes in this section. /// /// `convert_address` is a function to convert read addresses into the `Address` /// type. For non-relocatable addresses, this function may simply return /// `Address::Constant(address)`. For relocatable addresses, it is the caller's /// responsibility to determine the symbol and addend corresponding to the address /// and return `Address::Symbol { symbol, addend }`. pubfn from<R: Reader<Offset = usize>>(
dwarf: &read::Dwarf<R>,
line_strings: &mut write::LineStringTable,
strings: &mut write::StringTable,
convert_address: &dynFn(u64) -> Option<Address>,
) -> ConvertResult<UnitTable> { let base_id = BaseId::default(); letmut unit_entries = Vec::new(); letmut entry_ids = HashMap::new();
// Attributes must be converted in a separate pass so that we can handle // references to other compilation units. letmut 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();
/// Create an entry's attributes by reading the data in the input sections. fn convert_attributes<R: Reader<Offset = usize>>(
&mutself,
context: &mut ConvertUnitContext<'_, R>,
entry_offsets: &[read::UnitOffset],
) -> ConvertResult<()> { let offset = entry_offsets[self.id.index]; let from = context.unit.entry(offset)?; letmut from_attrs = from.attrs(); whilelet 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);
} elseiflet 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))
}
}
}
// Test attrs letmut 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 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
);
}
for i in0..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. letmut strings = StringTable::default();
strings.add("string one"); let string_id = strings.add("string two"); letmut 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);
letmut line_strings = LineStringTable::default();
line_strings.add("line string one"); let line_string_id = line_strings.add("line string two"); letmut 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);
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,
};
letmut 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);
letmut 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),
);
let spec = read::AttributeSpecification::new(*name, form, None); letmut 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);
for i in0..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 read_debug_info = read::DebugInfo::new(sections.debug_info.slice(), LittleEndian); let read_debug_abbrev = read::DebugAbbrev::new(sections.debug_abbrev.slice(), LittleEndian); letmut 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. letmut 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(); letmut 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(); letmut convert_line_strings = LineStringTable::default(); letmut 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. letmut 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),
);
let form = value.form(encoding).unwrap(); let attr = Attribute {
name: *name,
value: value.clone(),
};
letmut debug_info_refs = Vec::new(); letmut unit_refs = Vec::new(); letmut 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); letmut 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);
#[test] fn test_line_program_used() { for used in [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,
);
letmut 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),
);
letmut units = UnitTable::default();
units.add(unit);
let debug_line_str_offsets = DebugLineStrOffsets::none(); let debug_str_offsets = DebugStrOffsets::none(); letmut 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,
}; letmut 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);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.