diff --git a/crates/examples/src/bin/dwarfdump.rs b/crates/examples/src/bin/dwarfdump.rs index c9969996..fbc9800c 100644 --- a/crates/examples/src/bin/dwarfdump.rs +++ b/crates/examples/src/bin/dwarfdump.rs @@ -1100,7 +1100,8 @@ fn dump_unit( } } - let entries_result = dump_entries(w, unit, dwarf, flags); + let unit_ref = unit.unit_ref(dwarf); + let entries_result = dump_entries(w, unit_ref, flags); if let Err(err) = entries_result { writeln_error(w, dwarf, err, "Failed to dump entries")?; } @@ -1137,8 +1138,7 @@ fn write_offset( fn dump_entries( w: &mut W, - unit: gimli::Unit, - dwarf: &gimli::Dwarf, + unit: gimli::UnitRef, flags: &Flags, ) -> Result<()> { let mut spaces_buf = String::new(); @@ -1180,9 +1180,11 @@ fn dump_entries( if flags.raw { writeln!(w, "{:?}", attr.raw_value())?; } else { - match dump_attr_value(w, &attr, &unit, dwarf) { + match dump_attr_value(w, &attr, unit) { Ok(_) => (), - Err(err) => writeln_error(w, dwarf, err, "Failed to dump attribute value")?, + Err(err) => { + writeln_error(w, unit.dwarf, err, "Failed to dump attribute value")? + } }; } } @@ -1193,8 +1195,7 @@ fn dump_entries( fn dump_attr_value( w: &mut W, attr: &gimli::Attribute, - unit: &gimli::Unit, - dwarf: &gimli::Dwarf, + unit: gimli::UnitRef, ) -> Result<()> { let value = attr.value(); match value { @@ -1286,12 +1287,12 @@ fn dump_attr_value( } gimli::AttributeValue::DebugAddrIndex(index) => { write!(w, "(indirect address, index {:#x}): ", index.0)?; - let address = dwarf.address(unit, index)?; + let address = unit.address(index)?; writeln!(w, "0x{:08x}", address)?; } gimli::AttributeValue::UnitRef(offset) => { write!(w, "0x{:08x}", offset.0)?; - match offset.to_unit_section_offset(unit) { + match offset.to_unit_section_offset(&unit) { UnitSectionOffset::DebugInfoOffset(goff) => { write!(w, "<.debug_info+0x{:08x}>", goff.0)?; } @@ -1311,15 +1312,15 @@ fn dump_attr_value( writeln!(w, "<.debug_line+0x{:08x}>", offset.0)?; } gimli::AttributeValue::LocationListsRef(offset) => { - dump_loc_list(w, offset, unit, dwarf)?; + dump_loc_list(w, offset, unit)?; } gimli::AttributeValue::DebugLocListsBase(base) => { writeln!(w, "<.debug_loclists+0x{:08x}>", base.0)?; } gimli::AttributeValue::DebugLocListsIndex(index) => { write!(w, "(indirect location list, index {:#x}): ", index.0)?; - let offset = dwarf.locations_offset(unit, index)?; - dump_loc_list(w, offset, unit, dwarf)?; + let offset = unit.locations_offset(index)?; + dump_loc_list(w, offset, unit)?; } gimli::AttributeValue::DebugMacinfoRef(offset) => { writeln!(w, "<.debug_macinfo+0x{:08x}>", offset.0)?; @@ -1328,33 +1329,30 @@ fn dump_attr_value( writeln!(w, "<.debug_macro+0x{:08x}>", offset.0)?; } gimli::AttributeValue::RangeListsRef(offset) => { - let offset = dwarf.ranges_offset_from_raw(unit, offset); - dump_range_list(w, offset, unit, dwarf)?; + let offset = unit.ranges_offset_from_raw(offset); + dump_range_list(w, offset, unit)?; } gimli::AttributeValue::DebugRngListsBase(base) => { writeln!(w, "<.debug_rnglists+0x{:08x}>", base.0)?; } gimli::AttributeValue::DebugRngListsIndex(index) => { write!(w, "(indirect range list, index {:#x}): ", index.0)?; - let offset = dwarf.ranges_offset(unit, index)?; - dump_range_list(w, offset, unit, dwarf)?; + let offset = unit.ranges_offset(index)?; + dump_range_list(w, offset, unit)?; } gimli::AttributeValue::DebugTypesRef(signature) => { dump_type_signature(w, signature)?; writeln!(w, " ")?; } gimli::AttributeValue::DebugStrRef(offset) => { - if let Ok(s) = dwarf.debug_str.get_str(offset) { + if let Ok(s) = unit.string(offset) { writeln!(w, "{}", s.to_string_lossy()?)?; } else { writeln!(w, "<.debug_str+0x{:08x}>", offset.0)?; } } gimli::AttributeValue::DebugStrRefSup(offset) => { - if let Some(s) = dwarf - .sup() - .and_then(|sup| sup.debug_str.get_str(offset).ok()) - { + if let Ok(s) = unit.sup_string(offset) { writeln!(w, "{}", s.to_string_lossy()?)?; } else { writeln!(w, "<.debug_str(sup)+0x{:08x}>", offset.0)?; @@ -1365,19 +1363,15 @@ fn dump_attr_value( } gimli::AttributeValue::DebugStrOffsetsIndex(index) => { write!(w, "(indirect string, index {:#x}): ", index.0)?; - let offset = dwarf.debug_str_offsets.get_str_offset( - unit.encoding().format, - unit.str_offsets_base, - index, - )?; - if let Ok(s) = dwarf.debug_str.get_str(offset) { + let offset = unit.string_offset(index)?; + if let Ok(s) = unit.string(offset) { writeln!(w, "{}", s.to_string_lossy()?)?; } else { writeln!(w, "<.debug_str+0x{:08x}>", offset.0)?; } } gimli::AttributeValue::DebugLineStrRef(offset) => { - if let Ok(s) = dwarf.debug_line_str.get_str(offset) { + if let Ok(s) = unit.line_string(offset) { writeln!(w, "{}", s.to_string_lossy()?)?; } else { writeln!(w, "<.debug_line_str=0x{:08x}>", offset.0)?; @@ -1424,7 +1418,7 @@ fn dump_attr_value( } gimli::AttributeValue::FileIndex(value) => { write!(w, "0x{:08x}", value)?; - dump_file_index(w, value, unit, dwarf)?; + dump_file_index(w, value, unit)?; writeln!(w)?; } gimli::AttributeValue::DwoId(value) => { @@ -1443,8 +1437,7 @@ fn dump_type_signature(w: &mut W, signature: gimli::DebugTypeSignature fn dump_file_index( w: &mut W, file_index: u64, - unit: &gimli::Unit, - dwarf: &gimli::Dwarf, + unit: gimli::UnitRef, ) -> Result<()> { if file_index == 0 && unit.header.version() <= 4 { return Ok(()); @@ -1462,7 +1455,7 @@ fn dump_file_index( }; write!(w, " ")?; if let Some(directory) = file.directory(header) { - let directory = dwarf.attr_string(unit, directory)?; + let directory = unit.attr_string(directory)?; let directory = directory.to_string_lossy()?; if file.directory_index() != 0 && !directory.starts_with('/') { if let Some(ref comp_dir) = unit.comp_dir { @@ -1474,9 +1467,7 @@ fn dump_file_index( write!( w, "{}", - dwarf - .attr_string(unit, file.path_name())? - .to_string_lossy()? + unit.attr_string(file.path_name())?.to_string_lossy()? )?; Ok(()) } @@ -1709,10 +1700,9 @@ fn dump_range(w: &mut W, range: Option) -> Result<()> { fn dump_loc_list( w: &mut W, offset: gimli::LocationListsOffset, - unit: &gimli::Unit, - dwarf: &gimli::Dwarf, + unit: gimli::UnitRef, ) -> Result<()> { - let mut locations = dwarf.locations(unit, offset)?; + let mut locations = unit.locations(offset)?; writeln!( w, "", @@ -1735,7 +1725,7 @@ fn dump_loc_list( writeln!(w, "", addr)?; } gimli::RawLocListEntry::BaseAddressx { addr } => { - let addr_val = dwarf.address(unit, addr)?; + let addr_val = unit.address(addr)?; writeln!(w, "", addr.0, addr_val)?; } gimli::RawLocListEntry::StartxEndx { @@ -1743,8 +1733,8 @@ fn dump_loc_list( end, ref data, } => { - let begin_val = dwarf.address(unit, begin)?; - let end_val = dwarf.address(unit, end)?; + let begin_val = unit.address(begin)?; + let end_val = unit.address(end)?; write!( w, "", @@ -1759,7 +1749,7 @@ fn dump_loc_list( length, ref data, } => { - let begin_val = dwarf.address(unit, begin)?; + let begin_val = unit.address(begin)?; write!( w, "", @@ -1817,10 +1807,9 @@ fn dump_loc_list( fn dump_range_list( w: &mut W, offset: gimli::RangeListsOffset, - unit: &gimli::Unit, - dwarf: &gimli::Dwarf, + unit: gimli::UnitRef, ) -> Result<()> { - let mut ranges = dwarf.ranges(unit, offset)?; + let mut ranges = unit.ranges(offset)?; writeln!( w, "", @@ -1841,12 +1830,12 @@ fn dump_range_list( writeln!(w, "", addr)?; } gimli::RawRngListEntry::BaseAddressx { addr } => { - let addr_val = dwarf.address(unit, addr)?; + let addr_val = unit.address(addr)?; writeln!(w, "", addr.0, addr_val)?; } gimli::RawRngListEntry::StartxEndx { begin, end } => { - let begin_val = dwarf.address(unit, begin)?; - let end_val = dwarf.address(unit, end)?; + let begin_val = unit.address(begin)?; + let end_val = unit.address(end)?; write!( w, "", @@ -1856,7 +1845,7 @@ fn dump_range_list( writeln!(w)?; } gimli::RawRngListEntry::StartxLength { begin, length } => { - let begin_val = dwarf.address(unit, begin)?; + let begin_val = unit.address(begin)?; write!( w, "", @@ -1906,7 +1895,8 @@ fn dump_line(w: &mut W, dwarf: &gimli::Dwarf) -> Result< continue; } }; - match dump_line_program(w, &unit, dwarf) { + let unit_ref = unit.unit_ref(dwarf); + match dump_line_program(w, unit_ref) { Ok(_) => (), Err(Error::IoError) => return Err(Error::IoError), Err(err) => writeln_error(w, dwarf, err, "Failed to dump line program")?, @@ -1915,11 +1905,7 @@ fn dump_line(w: &mut W, dwarf: &gimli::Dwarf) -> Result< Ok(()) } -fn dump_line_program( - w: &mut W, - unit: &gimli::Unit, - dwarf: &gimli::Dwarf, -) -> Result<()> { +fn dump_line_program(w: &mut W, unit: gimli::UnitRef) -> Result<()> { if let Some(program) = unit.line_program.clone() { { let header = program.header(); @@ -1999,7 +1985,7 @@ fn dump_line_program( w, " {} {}", base + i, - dwarf.attr_string(unit, dir.clone())?.to_string_lossy()? + unit.attr_string(dir.clone())?.to_string_lossy()? )?; } @@ -2029,9 +2015,7 @@ fn dump_line_program( writeln!( w, "\t{}", - dwarf - .attr_string(unit, file.path_name())? - .to_string_lossy()? + unit.attr_string(file.path_name())?.to_string_lossy()? )?; } @@ -2086,18 +2070,14 @@ fn dump_line_program( write!( w, " uri: \"{}/{}\"", - dwarf.attr_string(unit, directory)?.to_string_lossy()?, - dwarf - .attr_string(unit, file.path_name())? - .to_string_lossy()? + unit.attr_string(directory)?.to_string_lossy()?, + unit.attr_string(file.path_name())?.to_string_lossy()? )?; } else { write!( w, " uri: \"{}\"", - dwarf - .attr_string(unit, file.path_name())? - .to_string_lossy()? + unit.attr_string(file.path_name())?.to_string_lossy()? )?; } } diff --git a/crates/examples/src/bin/simple.rs b/crates/examples/src/bin/simple.rs index c55f7e67..18d9a2b9 100644 --- a/crates/examples/src/bin/simple.rs +++ b/crates/examples/src/bin/simple.rs @@ -128,7 +128,8 @@ fn dump_file( header.offset().as_debug_info_offset().unwrap().0 ); let unit = dwarf.unit(header)?; - dump_unit(&dwarf, &unit)?; + let unit_ref = unit.unit_ref(&dwarf); + dump_unit(unit_ref)?; // Check for a DWO unit. let Some(dwp) = &dwp else { continue }; @@ -141,13 +142,14 @@ fn dump_file( continue; }; let unit = dwo.unit(header)?; - dump_unit(&dwo, &unit)?; + let unit_ref = unit.unit_ref(&dwo); + dump_unit(unit_ref)?; } Ok(()) } -fn dump_unit(dwarf: &gimli::Dwarf, unit: &gimli::Unit) -> Result<(), gimli::Error> { +fn dump_unit(unit: gimli::UnitRef) -> Result<(), gimli::Error> { // Iterate over the Debugging Information Entries (DIEs) in the unit. let mut depth = 0; let mut entries = unit.entries(); @@ -159,7 +161,7 @@ fn dump_unit(dwarf: &gimli::Dwarf, unit: &gimli::Unit) -> Result let mut attrs = entry.attrs(); while let Some(attr) = attrs.next()? { print!(" {}: {:?}", attr.name(), attr.value()); - if let Ok(s) = dwarf.attr_string(unit, attr.value()) { + if let Ok(s) = unit.attr_string(attr.value()) { print!(" '{}'", s.to_string_lossy()?); } println!(); diff --git a/crates/examples/src/bin/simple_line.rs b/crates/examples/src/bin/simple_line.rs index 8b0165ed..a180c6a6 100644 --- a/crates/examples/src/bin/simple_line.rs +++ b/crates/examples/src/bin/simple_line.rs @@ -46,6 +46,7 @@ fn dump_file( header.offset().as_debug_info_offset().unwrap().0 ); let unit = dwarf.unit(header)?; + let unit = unit.unit_ref(&dwarf); // Get the line program for the compilation unit. if let Some(program) = unit.line_program.clone() { @@ -70,15 +71,12 @@ fn dump_file( // The directory index 0 is defined to correspond to the compilation unit directory. if file.directory_index() != 0 { if let Some(dir) = file.directory(header) { - path.push( - dwarf.attr_string(&unit, dir)?.to_string_lossy().as_ref(), - ); + path.push(unit.attr_string(dir)?.to_string_lossy().as_ref()); } } path.push( - dwarf - .attr_string(&unit, file.path_name())? + unit.attr_string(file.path_name())? .to_string_lossy() .as_ref(), ); diff --git a/src/read/dwarf.rs b/src/read/dwarf.rs index ec51feec..16799d3f 100644 --- a/src/read/dwarf.rs +++ b/src/read/dwarf.rs @@ -380,6 +380,17 @@ impl Dwarf { self.debug_line_str.get_str(offset) } + /// Return the string at the given offset in the `.debug_str` + /// in the supplementary object file. + #[inline] + pub fn sup_string(&self, offset: DebugStrOffset) -> Result { + if let Some(sup) = self.sup() { + sup.debug_str.get_str(offset) + } else { + Err(Error::ExpectedStringAttributeValue) + } + } + /// Return an attribute value as a string slice. /// /// If the attribute value is one of: @@ -397,22 +408,12 @@ impl Dwarf { pub fn attr_string(&self, unit: &Unit, attr: AttributeValue) -> Result { match attr { AttributeValue::String(string) => Ok(string), - AttributeValue::DebugStrRef(offset) => self.debug_str.get_str(offset), - AttributeValue::DebugStrRefSup(offset) => { - if let Some(sup) = self.sup() { - sup.debug_str.get_str(offset) - } else { - Err(Error::ExpectedStringAttributeValue) - } - } - AttributeValue::DebugLineStrRef(offset) => self.debug_line_str.get_str(offset), + AttributeValue::DebugStrRef(offset) => self.string(offset), + AttributeValue::DebugStrRefSup(offset) => self.sup_string(offset), + AttributeValue::DebugLineStrRef(offset) => self.line_string(offset), AttributeValue::DebugStrOffsetsIndex(index) => { - let offset = self.debug_str_offsets.get_str_offset( - unit.header.format(), - unit.str_offsets_base, - index, - )?; - self.debug_str.get_str(offset) + let offset = self.string_offset(unit, index)?; + self.string(offset) } _ => Err(Error::ExpectedStringAttributeValue), } @@ -1263,6 +1264,11 @@ impl Unit { Ok(unit) } + /// Return a reference to this unit and its associated `Dwarf`. + pub fn unit_ref<'a>(&'a self, dwarf: &'a Dwarf) -> UnitRef<'a, R> { + UnitRef::new(dwarf, self) + } + /// Return the encoding parameters for this unit. #[inline] pub fn encoding(&self) -> Encoding { @@ -1328,9 +1334,193 @@ impl Unit { } } +/// A reference to a `Unit` and its associated `Dwarf`. +/// +/// These often need to be passed around together, so this struct makes that easier. +/// +/// It implements `Deref` to `Unit`, so you can use it as if it were a `Unit`. +/// It also implements methods that correspond to methods on `Dwarf` that take a `Unit`. +#[derive(Debug)] +pub struct UnitRef<'a, R: Reader> { + /// The `Dwarf` that contains the unit. + pub dwarf: &'a Dwarf, + + /// The `Unit` being referenced. + pub unit: &'a Unit, +} + +impl<'a, R: Reader> Clone for UnitRef<'a, R> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, R: Reader> Copy for UnitRef<'a, R> {} + +impl<'a, R: Reader> core::ops::Deref for UnitRef<'a, R> { + type Target = Unit; + + fn deref(&self) -> &Self::Target { + self.unit + } +} + +impl<'a, R: Reader> UnitRef<'a, R> { + /// Construct a new `UnitRef` from a `Dwarf` and a `Unit`. + pub fn new(dwarf: &'a Dwarf, unit: &'a Unit) -> Self { + UnitRef { dwarf, unit } + } + + /// Return the string offset at the given index. + #[inline] + pub fn string_offset( + &self, + index: DebugStrOffsetsIndex, + ) -> Result> { + self.dwarf.string_offset(self.unit, index) + } + + /// Return the string at the given offset in `.debug_str`. + #[inline] + pub fn string(&self, offset: DebugStrOffset) -> Result { + self.dwarf.string(offset) + } + + /// Return the string at the given offset in `.debug_line_str`. + #[inline] + pub fn line_string(&self, offset: DebugLineStrOffset) -> Result { + self.dwarf.line_string(offset) + } + + /// Return the string at the given offset in the `.debug_str` + /// in the supplementary object file. + #[inline] + pub fn sup_string(&self, offset: DebugStrOffset) -> Result { + self.dwarf.sup_string(offset) + } + + /// Return an attribute value as a string slice. + /// + /// See [`Dwarf::attr_string`] for more information. + pub fn attr_string(&self, attr: AttributeValue) -> Result { + self.dwarf.attr_string(self.unit, attr) + } + + /// Return the address at the given index. + pub fn address(&self, index: DebugAddrIndex) -> Result { + self.dwarf.address(self.unit, index) + } + + /// Try to return an attribute value as an address. + /// + /// See [`Dwarf::attr_address`] for more information. + pub fn attr_address(&self, attr: AttributeValue) -> Result> { + self.dwarf.attr_address(self.unit, attr) + } + + /// Return the range list offset for the given raw offset. + /// + /// This handles adding `DW_AT_GNU_ranges_base` if required. + pub fn ranges_offset_from_raw( + &self, + offset: RawRangeListsOffset, + ) -> RangeListsOffset { + self.dwarf.ranges_offset_from_raw(self.unit, offset) + } + + /// Return the range list offset at the given index. + pub fn ranges_offset( + &self, + index: DebugRngListsIndex, + ) -> Result> { + self.dwarf.ranges_offset(self.unit, index) + } + + /// Iterate over the `RangeListEntry`s starting at the given offset. + pub fn ranges(&self, offset: RangeListsOffset) -> Result> { + self.dwarf.ranges(self.unit, offset) + } + + /// Iterate over the `RawRngListEntry`ies starting at the given offset. + pub fn raw_ranges(&self, offset: RangeListsOffset) -> Result> { + self.dwarf.raw_ranges(self.unit, offset) + } + + /// Try to return an attribute value as a range list offset. + /// + /// See [`Dwarf::attr_ranges_offset`] for more information. + pub fn attr_ranges_offset( + &self, + attr: AttributeValue, + ) -> Result>> { + self.dwarf.attr_ranges_offset(self.unit, attr) + } + + /// Try to return an attribute value as a range list entry iterator. + /// + /// See [`Dwarf::attr_ranges`] for more information. + pub fn attr_ranges(&self, attr: AttributeValue) -> Result>> { + self.dwarf.attr_ranges(self.unit, attr) + } + + /// Return an iterator for the address ranges of a `DebuggingInformationEntry`. + /// + /// This uses `DW_AT_low_pc`, `DW_AT_high_pc` and `DW_AT_ranges`. + pub fn die_ranges(&self, entry: &DebuggingInformationEntry) -> Result> { + self.dwarf.die_ranges(self.unit, entry) + } + + /// Return an iterator for the address ranges of the `Unit`. + /// + /// This uses `DW_AT_low_pc`, `DW_AT_high_pc` and `DW_AT_ranges` of the + /// root `DebuggingInformationEntry`. + pub fn unit_ranges(&self) -> Result> { + self.dwarf.unit_ranges(self.unit) + } + + /// Return the location list offset at the given index. + pub fn locations_offset( + &self, + index: DebugLocListsIndex, + ) -> Result> { + self.dwarf.locations_offset(self.unit, index) + } + + /// Iterate over the `LocationListEntry`s starting at the given offset. + pub fn locations(&self, offset: LocationListsOffset) -> Result> { + self.dwarf.locations(self.unit, offset) + } + + /// Iterate over the raw `LocationListEntry`s starting at the given offset. + pub fn raw_locations( + &self, + offset: LocationListsOffset, + ) -> Result> { + self.dwarf.raw_locations(self.unit, offset) + } + + /// Try to return an attribute value as a location list offset. + /// + /// See [`Dwarf::attr_locations_offset`] for more information. + pub fn attr_locations_offset( + &self, + attr: AttributeValue, + ) -> Result>> { + self.dwarf.attr_locations_offset(self.unit, attr) + } + + /// Try to return an attribute value as a location list entry iterator. + /// + /// See [`Dwarf::attr_locations`] for more information. + pub fn attr_locations(&self, attr: AttributeValue) -> Result>> { + self.dwarf.attr_locations(self.unit, attr) + } +} + impl UnitSectionOffset { /// Convert an offset to be relative to the start of the given unit, /// instead of relative to the start of the section. + /// /// Returns `None` if the offset is not within the unit entries. pub fn to_unit_offset(&self, unit: &Unit) -> Option> where