diff --git a/examples/dwarfdump.rs b/examples/dwarfdump.rs index d723523a2..935349f18 100644 --- a/examples/dwarfdump.rs +++ b/examples/dwarfdump.rs @@ -544,7 +544,6 @@ where let ranges = gimli::RangeLists::new(debug_ranges, debug_rnglists); let dwarf = gimli::Dwarf { - endian, debug_abbrev, debug_addr, debug_info, @@ -869,7 +868,7 @@ fn dump_cfi_instructions( } } -fn dump_info(dwarf: &gimli::Dwarf, flags: &Flags) -> Result<()> +fn dump_info(dwarf: &gimli::Dwarf, flags: &Flags) -> Result<()> where R::Endian: Send + Sync, { @@ -879,26 +878,20 @@ where let units = dwarf.units().collect::>().unwrap(); let process_unit = |unit: CompilationUnitHeader, buf: &mut Vec| -> Result<()> { - let abbrevs = match dwarf.abbreviations(&unit) { - Ok(abbrevs) => abbrevs, + let offset = unit.offset().0; + let unit = match gimli::DwarfUnit::new(dwarf, unit.header()) { + Ok(unit) => unit, Err(err) => { writeln!( buf, - "Failed to parse abbreviations: {}", + "Failed to parse unit root entry: {}", error::Error::description(&err) )?; return Ok(()); } }; - let entries_result = dump_entries( - buf, - unit.offset().0, - unit.entries(&abbrevs), - unit.encoding(), - dwarf, - flags, - ); + let entries_result = dump_entries(buf, offset, unit, dwarf, flags); if let Err(err) = entries_result { writeln!( buf, @@ -923,28 +916,16 @@ where fn dump_types( w: &mut W, - dwarf: &gimli::Dwarf, + dwarf: &gimli::Dwarf, flags: &Flags, ) -> Result<()> { writeln!(w, "\n.debug_types")?; let mut iter = dwarf.type_units(); while let Some(unit) = iter.next()? { - let abbrevs = match dwarf.type_abbreviations(&unit) { - Ok(abbrevs) => abbrevs, - Err(err) => { - writeln!( - w, - "Failed to parse abbreviations: {}", - error::Error::description(&err) - )?; - continue; - } - }; - writeln!(w, "\nCU_HEADER:")?; write!(w, " signature = ")?; - dump_type_signature(w, unit.type_signature(), dwarf.endian)?; + dump_type_signature(w, unit.type_signature())?; writeln!(w)?; writeln!( w, @@ -953,14 +934,19 @@ fn dump_types( unit.type_offset().0 )?; - let entries_result = dump_entries( - w, - unit.offset().0, - unit.entries(&abbrevs), - unit.encoding(), - dwarf, - flags, - ); + let offset = unit.offset().0; + let unit = match gimli::DwarfUnit::new(dwarf, unit.header()) { + Ok(unit) => unit, + Err(err) => { + writeln!( + w, + "Failed to parse unit root entry: {}", + error::Error::description(&err) + )?; + return Ok(()); + } + }; + let entries_result = dump_entries(w, offset, unit, dwarf, flags); if let Err(err) = entries_result { writeln!( w, @@ -972,19 +958,6 @@ fn dump_types( Ok(()) } -// TODO: most of this should be moved to the main library. -struct Unit { - encoding: gimli::Encoding, - base_address: u64, - line_program: Option>, - comp_dir: Option>, - comp_name: Option>, - str_offsets_base: gimli::DebugStrOffsetsBase, - addr_base: gimli::DebugAddrBase, - loclists_base: gimli::DebugLocListsBase, - rnglists_base: gimli::DebugRngListsBase, -} - fn spaces(buf: &mut String, len: usize) -> &str { while buf.len() < len { buf.push(' '); @@ -992,32 +965,18 @@ fn spaces(buf: &mut String, len: usize) -> &str { &buf[..len] } -#[allow(clippy::too_many_arguments)] fn dump_entries( w: &mut W, offset: R::Offset, - mut entries: gimli::EntriesCursor, - encoding: gimli::Encoding, - dwarf: &gimli::Dwarf, + unit: gimli::DwarfUnit, + dwarf: &gimli::Dwarf, flags: &Flags, ) -> Result<()> { - let mut unit = Unit { - encoding, - base_address: 0, - line_program: None, - comp_dir: None, - comp_name: None, - // Defaults to 0 for GNU extensions - str_offsets_base: gimli::DebugStrOffsetsBase(0), - addr_base: gimli::DebugAddrBase(0), - loclists_base: gimli::DebugLocListsBase(0), - rnglists_base: gimli::DebugRngListsBase(0), - }; - let mut spaces_buf = String::new(); let mut print_local = true; let mut depth = 0; + let mut entries = unit.entries(); while let Some((delta_depth, entry)) = entries.next_dfs()? { depth += delta_depth; assert!(depth >= 0); @@ -1043,47 +1002,6 @@ fn dump_entries( entry.tag() )?; - if entry.tag() == gimli::DW_TAG_compile_unit || entry.tag() == gimli::DW_TAG_type_unit { - unit.base_address = match entry.attr_value(gimli::DW_AT_low_pc)? { - Some(gimli::AttributeValue::Addr(address)) => address, - _ => 0, - }; - unit.comp_dir = entry.attr_value(gimli::DW_AT_comp_dir)?; - unit.comp_name = entry.attr_value(gimli::DW_AT_name)?; - unit.line_program = match entry.attr_value(gimli::DW_AT_stmt_list)? { - Some(gimli::AttributeValue::DebugLineRef(offset)) => dwarf - .debug_line - .program( - offset, - unit.encoding.address_size, - unit.comp_dir.clone(), - unit.comp_name.clone(), - ) - .ok(), - _ => None, - }; - if let Some(gimli::AttributeValue::DebugStrOffsetsBase(base)) = - entry.attr_value(gimli::DW_AT_str_offsets_base)? - { - unit.str_offsets_base = base; - } - if let Some(gimli::AttributeValue::DebugAddrBase(base)) = - entry.attr_value(gimli::DW_AT_addr_base)? - { - unit.addr_base = base; - } - if let Some(gimli::AttributeValue::DebugLocListsBase(base)) = - entry.attr_value(gimli::DW_AT_loclists_base)? - { - unit.loclists_base = base; - } - if let Some(gimli::AttributeValue::DebugRngListsBase(base)) = - entry.attr_value(gimli::DW_AT_rnglists_base)? - { - unit.rnglists_base = base; - } - } - let mut attrs = entry.attrs(); while let Some(attr) = attrs.next()? { w.write_all(spaces(&mut spaces_buf, indent + 18).as_bytes())?; @@ -1113,8 +1031,8 @@ fn dump_entries( fn dump_attr_value( w: &mut W, attr: &gimli::Attribute, - unit: &Unit, - dwarf: &gimli::Dwarf, + unit: &gimli::DwarfUnit, + dwarf: &gimli::Dwarf, ) -> Result<()> { let value = attr.value(); match value { @@ -1206,10 +1124,7 @@ fn dump_attr_value( writeln!(w, "<.debug_addr+0x{:08x}>", base.0)?; } gimli::AttributeValue::DebugAddrIndex(index) => { - let address = - dwarf - .debug_addr - .get_address(unit.encoding.address_size, unit.addr_base, index)?; + let address = dwarf.address(unit, index)?; writeln!(w, "0x{:08x}", address)?; } gimli::AttributeValue::UnitRef(gimli::UnitOffset(offset)) => { @@ -1225,15 +1140,14 @@ fn dump_attr_value( writeln!(w, "0x{:08x}", offset)?; } gimli::AttributeValue::LocationListsRef(offset) => { + writeln!(w, "0x{:08x}", offset.0)?; dump_loc_list(w, offset, unit, dwarf)?; } gimli::AttributeValue::DebugLocListsBase(base) => { writeln!(w, "<.debug_loclists+0x{:08x}>", base.0)?; } gimli::AttributeValue::DebugLocListsIndex(index) => { - let offset = dwarf - .locations - .get_offset(unit.encoding, unit.loclists_base, index)?; + let offset = dwarf.locations_offset(unit, index)?; writeln!(w, "0x{:08x}", offset.0)?; dump_loc_list(w, offset, unit, dwarf)?; } @@ -1248,14 +1162,12 @@ fn dump_attr_value( writeln!(w, "<.debug_rnglists+0x{:08x}>", base.0)?; } gimli::AttributeValue::DebugRngListsIndex(index) => { - let offset = dwarf - .ranges - .get_offset(unit.encoding, unit.rnglists_base, index)?; + let offset = dwarf.ranges_offset(unit, index)?; writeln!(w, "0x{:08x}", offset.0)?; dump_range_list(w, offset, unit, dwarf)?; } gimli::AttributeValue::DebugTypesRef(signature) => { - dump_type_signature(w, signature, dwarf.endian)?; + dump_type_signature(w, signature)?; writeln!(w, " ")?; } gimli::AttributeValue::DebugStrRef(offset) => { @@ -1273,7 +1185,7 @@ fn dump_attr_value( } gimli::AttributeValue::DebugStrOffsetsIndex(index) => { let offset = dwarf.debug_str_offsets.get_str_offset( - unit.encoding.format, + unit.encoding().format, unit.str_offsets_base, index, )?; @@ -1339,26 +1251,16 @@ fn dump_attr_value( Ok(()) } -fn dump_type_signature( - w: &mut W, - signature: gimli::DebugTypeSignature, - endian: Endian, -) -> Result<()> { - // Convert back to bytes so we can match libdwarf-dwarfdump output. - let mut buf = [0; 8]; - endian.write_u64(&mut buf, signature.0); - write!(w, "0x")?; - for byte in &buf { - write!(w, "{:02x}", byte)?; - } +fn dump_type_signature(w: &mut W, signature: gimli::DebugTypeSignature) -> Result<()> { + write!(w, "0x{:016x}", signature.0)?; Ok(()) } fn dump_file_index( w: &mut W, file: u64, - unit: &Unit, - dwarf: &gimli::Dwarf, + unit: &gimli::DwarfUnit, + dwarf: &gimli::Dwarf, ) -> Result<()> { if file == 0 { return Ok(()); @@ -1376,14 +1278,16 @@ fn dump_file_index( }; write!(w, " ")?; if let Some(directory) = file.directory(header) { - let directory = dwarf.attr_string(directory)?; + let directory = dwarf.attr_string(unit, directory)?; let directory = directory.to_string_lossy()?; if !directory.starts_with('/') { if let Some(ref comp_dir) = unit.comp_dir { write!( w, "{}/", - dwarf.attr_string(comp_dir.clone())?.to_string_lossy()? + dwarf + .attr_string(unit, comp_dir.clone())? + .to_string_lossy()? )?; } } @@ -1392,7 +1296,9 @@ fn dump_file_index( write!( w, "{}", - dwarf.attr_string(file.path_name())?.to_string_lossy()? + dwarf + .attr_string(unit, file.path_name())? + .to_string_lossy()? )?; Ok(()) } @@ -1400,14 +1306,14 @@ fn dump_file_index( fn dump_exprloc( w: &mut W, data: &gimli::Expression, - unit: &Unit, + unit: &gimli::DwarfUnit, ) -> Result<()> { let mut pc = data.0.clone(); let mut space = false; while pc.len() != 0 { let mut op_pc = pc.clone(); let dwop = gimli::DwOp(op_pc.read_u8()?); - match gimli::Operation::parse(&mut pc, &data.0, unit.encoding) { + match gimli::Operation::parse(&mut pc, &data.0, unit.encoding()) { Ok(op) => { if space { write!(w, " ")?; @@ -1603,19 +1509,12 @@ fn dump_op( fn dump_loc_list( w: &mut W, offset: gimli::LocationListsOffset, - unit: &Unit, - dwarf: &gimli::Dwarf, + unit: &gimli::DwarfUnit, + dwarf: &gimli::Dwarf, ) -> Result<()> { - let raw_locations = dwarf.locations.raw_locations(offset, unit.encoding)?; + let raw_locations = dwarf.locations.raw_locations(offset, unit.encoding())?; let raw_locations: Vec<_> = raw_locations.collect()?; - let mut locations = dwarf.locations.locations( - offset, - unit.encoding, - unit.base_address, - &dwarf.debug_addr, - unit.addr_base, - )?; - + let mut locations = dwarf.locations(unit, offset)?; writeln!( w, "", @@ -1629,11 +1528,7 @@ fn dump_loc_list( writeln!(w, "", addr)?; } gimli::RawLocListEntry::BaseAddressx { addr } => { - let addr_val = dwarf.debug_addr.get_address( - unit.encoding.address_size, - unit.addr_base, - addr, - )?; + let addr_val = dwarf.address(unit, addr)?; writeln!(w, "", addr.0, addr_val)?; } gimli::RawLocListEntry::StartxEndx { @@ -1641,16 +1536,8 @@ fn dump_loc_list( end, ref data, } => { - let begin_val = dwarf.debug_addr.get_address( - unit.encoding.address_size, - unit.addr_base, - begin, - )?; - let end_val = dwarf.debug_addr.get_address( - unit.encoding.address_size, - unit.addr_base, - end, - )?; + let begin_val = dwarf.address(unit, begin)?; + let end_val = dwarf.address(unit, end)?; let location = locations.next()?.unwrap(); write!( w, @@ -1667,11 +1554,7 @@ fn dump_loc_list( length, ref data, } => { - let begin_val = dwarf.debug_addr.get_address( - unit.encoding.address_size, - unit.addr_base, - begin, - )?; + let begin_val = dwarf.address(unit, begin)?; let location = locations.next()?.unwrap(); write!( w, @@ -1744,23 +1627,17 @@ fn dump_loc_list( fn dump_range_list( w: &mut W, offset: gimli::RangeListsOffset, - unit: &Unit, - dwarf: &gimli::Dwarf, + unit: &gimli::DwarfUnit, + dwarf: &gimli::Dwarf, ) -> Result<()> { - let raw_ranges = dwarf.ranges.raw_ranges(offset, unit.encoding)?; + let raw_ranges = dwarf.ranges.raw_ranges(offset, unit.encoding())?; let raw_ranges: Vec<_> = raw_ranges.collect()?; - let mut ranges = dwarf.ranges.ranges( - offset, - unit.encoding, - unit.base_address, - &dwarf.debug_addr, - unit.addr_base, - )?; + let mut ranges = dwarf.ranges(unit, offset)?; writeln!( w, "\t\tranges: {} at {} offset {} (0x{:08x})", raw_ranges.len(), - if unit.encoding.version < 5 { + if unit.encoding().version < 5 { ".debug_ranges" } else { ".debug_rnglists" @@ -1785,24 +1662,12 @@ fn dump_range_list( writeln!(w, "", addr)?; } gimli::RawRngListEntry::BaseAddressx { addr } => { - let addr_val = dwarf.debug_addr.get_address( - unit.encoding.address_size, - unit.addr_base, - addr, - )?; + let addr_val = dwarf.address(unit, addr)?; writeln!(w, "", addr.0, addr_val)?; } gimli::RawRngListEntry::StartxEndx { begin, end } => { - let begin_val = dwarf.debug_addr.get_address( - unit.encoding.address_size, - unit.addr_base, - begin, - )?; - let end_val = dwarf.debug_addr.get_address( - unit.encoding.address_size, - unit.addr_base, - end, - )?; + let begin_val = dwarf.address(unit, begin)?; + let end_val = dwarf.address(unit, end)?; let range = if begin_val == end_val { gimli::Range { begin: begin_val, @@ -1820,11 +1685,7 @@ fn dump_range_list( )?; } gimli::RawRngListEntry::StartxLength { begin, length } => { - let begin_val = dwarf.debug_addr.get_address( - unit.encoding.address_size, - unit.addr_base, - begin, - )?; + let begin_val = dwarf.address(unit, begin)?; let range = ranges.next()?.unwrap(); writeln!( w, @@ -1873,15 +1734,26 @@ fn dump_range_list( Ok(()) } -fn dump_line(w: &mut W, dwarf: &gimli::Dwarf) -> Result<()> { +fn dump_line(w: &mut W, dwarf: &gimli::Dwarf) -> Result<()> { let mut iter = dwarf.units(); - while let Some(ref unit) = iter.next()? { + while let Some(unit) = iter.next()? { writeln!( w, "\n.debug_line: line number info for unit at .debug_info offset 0x{:08x}", unit.offset().0 )?; - match dump_line_program(w, unit, dwarf) { + let unit = match gimli::DwarfUnit::new(dwarf, unit.header()) { + Ok(unit) => unit, + Err(err) => { + writeln!( + w, + "Failed to parse unit root entry: {}", + error::Error::description(&err) + )?; + return Ok(()); + } + }; + match dump_line_program(w, &unit, dwarf) { Ok(_) => (), Err(Error::IoError) => return Err(Error::IoError), Err(err) => writeln!( @@ -1896,11 +1768,10 @@ fn dump_line(w: &mut W, dwarf: &gimli::Dwarf) fn dump_line_program( w: &mut W, - unit: &CompilationUnitHeader, - dwarf: &gimli::Dwarf, + unit: &gimli::DwarfUnit, + dwarf: &gimli::Dwarf, ) -> Result<()> { - let abbrevs = dwarf.abbreviations(unit)?; - if let Ok(Some(program)) = dwarf.line_program(unit, &abbrevs) { + if let Some(program) = unit.line_program.clone() { { let header = program.header(); writeln!(w)?; @@ -1979,7 +1850,7 @@ fn dump_line_program( w, " {} {}", base + i, - dwarf.attr_string(dir.clone())?.to_string_lossy()? + dwarf.attr_string(unit, dir.clone())?.to_string_lossy()? )?; } @@ -2009,7 +1880,9 @@ fn dump_line_program( writeln!( w, "\t{}", - dwarf.attr_string(file.path_name())?.to_string_lossy()? + dwarf + .attr_string(unit, file.path_name())? + .to_string_lossy()? )?; } @@ -2061,14 +1934,18 @@ fn dump_line_program( write!( w, " uri: \"{}/{}\"", - dwarf.attr_string(directory)?.to_string_lossy()?, - dwarf.attr_string(file.path_name())?.to_string_lossy()? + dwarf.attr_string(unit, directory)?.to_string_lossy()?, + dwarf + .attr_string(unit, file.path_name())? + .to_string_lossy()? )?; } else { write!( w, " uri: \"{}\"", - dwarf.attr_string(file.path_name())?.to_string_lossy()? + dwarf + .attr_string(unit, file.path_name())? + .to_string_lossy()? )?; } } diff --git a/src/read/dwarf.rs b/src/read/dwarf.rs index 7c7179c6a..b236bce08 100644 --- a/src/read/dwarf.rs +++ b/src/read/dwarf.rs @@ -1,23 +1,18 @@ +use common::{ + DebugAddrBase, DebugAddrIndex, DebugLocListsBase, DebugLocListsIndex, DebugRngListsBase, + DebugRngListsIndex, DebugStrOffsetsBase, Encoding, LocationListsOffset, RangeListsOffset, +}; use constants; use read::{ Abbreviations, AttributeValue, CompilationUnitHeader, CompilationUnitHeadersIter, DebugAbbrev, - DebugAddr, DebugInfo, DebugLine, DebugLineStr, DebugStr, DebugStrOffsets, DebugTypes, Error, - IncompleteLineProgram, LocationLists, RangeLists, Reader, Result, TypeUnitHeader, - TypeUnitHeadersIter, + DebugAddr, DebugInfo, DebugLine, DebugLineStr, DebugStr, DebugStrOffsets, DebugTypes, + EntriesCursor, Error, IncompleteLineProgram, LocListIter, LocationLists, RangeLists, Reader, + ReaderOffset, Result, RngListIter, TypeUnitHeader, TypeUnitHeadersIter, UnitHeader, }; -use Endianity; /// All of the commonly used DWARF sections, and other common information. -// Endian is a type parameter so that we avoid invariance problems. #[derive(Debug, Default)] -pub struct Dwarf -where - R: Reader, - Endian: Endianity, -{ - /// The endianity of bytes that are read. - pub endian: Endian, - +pub struct Dwarf { /// The `.debug_abbrev` section. pub debug_abbrev: DebugAbbrev, @@ -52,11 +47,7 @@ where pub ranges: RangeLists, } -impl Dwarf -where - R: Reader, - Endian: Endianity, -{ +impl Dwarf { /// Iterate the compilation- and partial-units in this /// `.debug_info` section. /// @@ -93,28 +84,7 @@ where unit.abbreviations(&self.debug_abbrev) } - /// Return the line number program for a unit. - pub fn line_program( - &self, - unit: &CompilationUnitHeader, - abbrevs: &Abbreviations, - ) -> Result>> { - let mut cursor = unit.entries(abbrevs); - cursor.next_dfs()?; - let root = cursor.current().ok_or(Error::MissingUnitDie)?; - let offset = match root.attr_value(constants::DW_AT_stmt_list)? { - Some(AttributeValue::DebugLineRef(offset)) => offset, - Some(_) => return Err(Error::UnsupportedAttributeForm), - None => return Ok(None), - }; - let comp_dir = root.attr_value(constants::DW_AT_comp_dir)?; - let comp_name = root.attr_value(constants::DW_AT_name)?; - self.debug_line - .program(offset, unit.address_size(), comp_dir, comp_name) - .map(Option::Some) - } - - /// Try to return an attribute value as a string slice. + /// Return an attribute value as a string slice. /// /// If the attribute value is one of: /// @@ -124,34 +94,318 @@ where /// object file /// - a `DW_FORM_line_strp` reference to an offset into the `.debug_line_str` /// section + /// - a `DW_FORM_strx` index into the `.debug_str_offsets` entries for the unit /// /// then return the attribute's string value. Returns an error if the attribute /// value does not have a string form, or if a string form has an invalid value. - // TODO: handle `DW_FORM_strx`, but that requires knowing the DebugStrOffsetsBase - pub fn attr_string(&self, attr: AttributeValue) -> Result { + pub fn attr_string( + &self, + unit: &DwarfUnit, + attr: AttributeValue, + ) -> Result { match attr { AttributeValue::String(string) => Ok(string), AttributeValue::DebugStrRef(offset) => self.debug_str.get_str(offset), AttributeValue::DebugStrRefSup(offset) => self.debug_str_sup.get_str(offset), AttributeValue::DebugLineStrRef(offset) => self.debug_line_str.get_str(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) + } _ => Err(Error::ExpectedStringAttributeValue), } } + + /// Return the address at the given index. + pub fn address(&self, unit: &DwarfUnit, index: DebugAddrIndex) -> Result { + self.debug_addr + .get_address(unit.encoding().address_size, unit.addr_base, index) + } + + /// Return the range list offset at the given index. + pub fn ranges_offset( + &self, + unit: &DwarfUnit, + index: DebugRngListsIndex, + ) -> Result> { + self.ranges + .get_offset(unit.encoding(), unit.rnglists_base, index) + } + + /// Iterate over the `RangeListEntry`s starting at the given offset. + pub fn ranges( + &self, + unit: &DwarfUnit, + offset: RangeListsOffset, + ) -> Result> { + self.ranges.ranges( + offset, + unit.encoding(), + unit.low_pc, + &self.debug_addr, + unit.addr_base, + ) + } + + /// Try to return an attribute value as a range list offset. + /// + /// If the attribute value is one of: + /// + /// - a `DW_FORM_sec_offset` reference to the `.debug_ranges` or `.debug_rnglists` sections + /// - a `DW_FORM_rnglistx` index into the `.debug_rnglists` entries for the unit + /// + /// then return the range list offset of the range list. + /// Returns `None` for other forms. + pub fn attr_ranges_offset( + &self, + unit: &DwarfUnit, + attr: AttributeValue, + ) -> Result>> { + match attr { + AttributeValue::RangeListsRef(offset) => Ok(Some(offset)), + AttributeValue::DebugRngListsIndex(index) => self.ranges_offset(unit, index).map(Some), + _ => Ok(None), + } + } + + /// Try to return an attribute value as a range list entry iterator. + /// + /// If the attribute value is one of: + /// + /// - a `DW_FORM_sec_offset` reference to the `.debug_ranges` or `.debug_rnglists` sections + /// - a `DW_FORM_rnglistx` index into the `.debug_rnglists` entries for the unit + /// + /// then return an iterator over the entries in the range list. + /// Returns `None` for other forms. + pub fn attr_ranges( + &self, + unit: &DwarfUnit, + attr: AttributeValue, + ) -> Result>> { + match self.attr_ranges_offset(unit, attr)? { + Some(offset) => Ok(Some(self.ranges(unit, offset)?)), + None => Ok(None), + } + } + + /// Return the location list offset at the given index. + pub fn locations_offset( + &self, + unit: &DwarfUnit, + index: DebugLocListsIndex, + ) -> Result> { + self.locations + .get_offset(unit.encoding(), unit.loclists_base, index) + } + + /// Iterate over the `LocationListEntry`s starting at the given offset. + pub fn locations( + &self, + unit: &DwarfUnit, + offset: LocationListsOffset, + ) -> Result> { + self.locations.locations( + offset, + unit.encoding(), + unit.low_pc, + &self.debug_addr, + unit.addr_base, + ) + } + + /// Try to return an attribute value as a location list offset. + /// + /// If the attribute value is one of: + /// + /// - a `DW_FORM_sec_offset` reference to the `.debug_loc` or `.debug_loclists` sections + /// - a `DW_FORM_loclistx` index into the `.debug_loclists` entries for the unit + /// + /// then return the location list offset of the location list. + /// Returns `None` for other forms. + pub fn attr_locations_offset( + &self, + unit: &DwarfUnit, + attr: AttributeValue, + ) -> Result>> { + match attr { + AttributeValue::LocationListsRef(offset) => Ok(Some(offset)), + AttributeValue::DebugLocListsIndex(index) => { + self.locations_offset(unit, index).map(Some) + } + _ => Ok(None), + } + } + + /// Try to return an attribute value as a location list entry iterator. + /// + /// If the attribute value is one of: + /// + /// - a `DW_FORM_sec_offset` reference to the `.debug_loc` or `.debug_loclists` sections + /// - a `DW_FORM_loclistx` index into the `.debug_loclists` entries for the unit + /// + /// then return an iterator over the entries in the location list. + /// Returns `None` for other forms. + pub fn attr_locations( + &self, + unit: &DwarfUnit, + attr: AttributeValue, + ) -> Result>> { + match self.attr_locations_offset(unit, attr)? { + Some(offset) => Ok(Some(self.locations(unit, offset)?)), + None => Ok(None), + } + } +} + +/// All of the commonly used information for a DWARF compilation unit. +#[derive(Debug)] +pub struct DwarfUnit { + /// The header of the unit. + pub header: UnitHeader, + + /// The parsed abbreviations for the unit. + pub abbreviations: Abbreviations, + + /// The `DW_AT_name` attribute of the unit. + pub name: Option>, + + /// The `DW_AT_comp_dir` attribute of the unit. + pub comp_dir: Option>, + + /// The `DW_AT_low_pc` attribute of the unit. Defaults to 0. + pub low_pc: u64, + + /// The `DW_AT_str_offsets_base` attribute of the unit. Defaults to 0. + pub str_offsets_base: DebugStrOffsetsBase, + + /// The `DW_AT_addr_base` attribute of the unit. Defaults to 0. + pub addr_base: DebugAddrBase, + + /// The `DW_AT_loclists_base` attribute of the unit. Defaults to 0. + pub loclists_base: DebugLocListsBase, + + /// The `DW_AT_rnglists_base` attribute of the unit. Defaults to 0. + pub rnglists_base: DebugRngListsBase, + + /// The line number program of the unit. + pub line_program: Option>, +} + +impl DwarfUnit { + /// Construct a new `DwarfUnit` from the given header. + pub fn new(dwarf: &Dwarf, header: UnitHeader) -> Result { + let abbreviations = header.abbreviations(&dwarf.debug_abbrev)?; + let mut name = None; + let mut comp_dir = None; + let mut low_pc = 0; + // Defaults to 0 for GNU extensions. + let mut str_offsets_base = DebugStrOffsetsBase(R::Offset::from_u8(0)); + let mut addr_base = DebugAddrBase(R::Offset::from_u8(0)); + let mut loclists_base = DebugLocListsBase(R::Offset::from_u8(0)); + let mut rnglists_base = DebugRngListsBase(R::Offset::from_u8(0)); + let mut line_program_offset = None; + + { + let mut cursor = header.entries(&abbreviations); + cursor.next_dfs()?; + let root = cursor.current().ok_or(Error::MissingUnitDie)?; + let mut attrs = root.attrs(); + while let Some(attr) = attrs.next()? { + match attr.name() { + constants::DW_AT_name => { + name = Some(attr.value()); + } + constants::DW_AT_comp_dir => { + comp_dir = Some(attr.value()); + } + constants::DW_AT_low_pc => { + if let AttributeValue::Addr(address) = attr.value() { + low_pc = address; + } + } + constants::DW_AT_stmt_list => { + if let AttributeValue::DebugLineRef(offset) = attr.value() { + line_program_offset = Some(offset); + } + } + constants::DW_AT_str_offsets_base => { + if let AttributeValue::DebugStrOffsetsBase(base) = attr.value() { + str_offsets_base = base; + } + } + constants::DW_AT_addr_base => { + if let AttributeValue::DebugAddrBase(base) = attr.value() { + addr_base = base; + } + } + constants::DW_AT_loclists_base => { + if let AttributeValue::DebugLocListsBase(base) = attr.value() { + loclists_base = base; + } + } + constants::DW_AT_rnglists_base => { + if let AttributeValue::DebugRngListsBase(base) = attr.value() { + rnglists_base = base; + } + } + _ => {} + } + } + } + + let line_program = match line_program_offset { + Some(offset) => Some(dwarf.debug_line.program( + offset, + header.address_size(), + comp_dir.clone(), + name.clone(), + )?), + None => None, + }; + + Ok(DwarfUnit { + header, + abbreviations, + name, + comp_dir, + low_pc, + str_offsets_base, + addr_base, + loclists_base, + rnglists_base, + line_program, + }) + } + + /// Return the encoding parameters for this unit. + #[inline] + pub fn encoding(&self) -> Encoding { + self.header.encoding() + } + + /// Navigate this unit's `DebuggingInformationEntry`s. + #[inline] + pub fn entries(&self) -> EntriesCursor { + self.header.entries(&self.abbreviations) + } } #[cfg(test)] mod tests { use super::*; use read::EndianSlice; + use Endianity; - /// Ensure that `Dwarf` is covariant wrt R. + /// Ensure that `Dwarf` is covariant wrt R. #[test] fn test_dwarf_variance() { /// This only needs to compile. #[allow(dead_code)] - fn f<'a: 'b, 'b, E: Endianity>( - x: Dwarf, E>, - ) -> Dwarf, E> { + fn f<'a: 'b, 'b, E: Endianity>(x: Dwarf>) -> Dwarf> { x } } diff --git a/src/read/unit.rs b/src/read/unit.rs index 8e37b7403..b99a571e1 100644 --- a/src/read/unit.rs +++ b/src/read/unit.rs @@ -232,6 +232,11 @@ where CompilationUnitHeader { header, offset } } + /// Return the `UnitHeader` containing common unit header fields. + pub fn header(self) -> UnitHeader { + self.header + } + /// Return the serialized size of the compilation unit header for the given /// DWARF format. pub fn size_of_header(format: Format) -> usize { @@ -2899,6 +2904,11 @@ where } } + /// Return the `UnitHeader` containing common unit fields. + pub fn header(self) -> UnitHeader { + self.header + } + /// Return the serialized size of the type-unit header for the given /// DWARF format. pub fn size_of_header(format: Format) -> usize { diff --git a/src/write/line.rs b/src/write/line.rs index 402a66e6c..d0a492287 100644 --- a/src/write/line.rs +++ b/src/write/line.rs @@ -982,7 +982,7 @@ mod convert { /// Return the program and a mapping from file index to `FileId`. pub fn from>( mut from_program: read::IncompleteLineProgram, - dwarf: &read::Dwarf, + dwarf: &read::Dwarf, line_strings: &mut write::LineStringTable, strings: &mut write::StringTable, convert_address: &Fn(u64) -> Option
, @@ -1126,7 +1126,7 @@ mod convert { impl LineString { fn from>( from_attr: read::AttributeValue, - dwarf: &read::Dwarf, + dwarf: &read::Dwarf, line_strings: &mut write::LineStringTable, strings: &mut write::StringTable, ) -> ConvertResult { diff --git a/src/write/unit.rs b/src/write/unit.rs index 5d6ac648c..f5a3bd439 100644 --- a/src/write/unit.rs +++ b/src/write/unit.rs @@ -1125,7 +1125,7 @@ pub(crate) mod convert { use write::{self, ConvertError, ConvertResult}; pub(crate) struct ConvertUnitContext<'a, R: Reader + 'a> { - pub dwarf: &'a read::Dwarf, + pub dwarf: &'a read::Dwarf, pub line_strings: &'a mut write::LineStringTable, pub strings: &'a mut write::StringTable, pub ranges: &'a mut write::RangeListTable, @@ -1152,7 +1152,7 @@ pub(crate) mod convert { /// responsibility to determine the symbol and addend corresponding to the address /// and return `Address::Relative { symbol, addend }`. pub fn from>( - dwarf: &read::Dwarf, + dwarf: &read::Dwarf, line_programs: &mut write::LineProgramTable, line_strings: &mut write::LineStringTable, strings: &mut write::StringTable, @@ -1213,7 +1213,7 @@ pub(crate) mod convert { from_unit: &read::CompilationUnitHeader, unit_id: UnitId, unit_entry_offsets: &mut HashMap, - dwarf: &read::Dwarf, + dwarf: &read::Dwarf, line_programs: &mut write::LineProgramTable, line_strings: &mut write::LineStringTable, strings: &mut write::StringTable, @@ -1711,8 +1711,8 @@ mod tests { assert_eq!(unit1.address_size(), read_unit1.address_size()); assert_eq!(unit1.format(), read_unit1.format()); - let abbrevs = dwarf.abbreviations(&read_unit1).unwrap(); - let mut read_entries = read_unit1.entries(&abbrevs); + let read_unit1 = read::DwarfUnit::new(&dwarf, read_unit1.header()).unwrap(); + let mut read_entries = read_unit1.entries(); let root = unit1.get(unit1.root()); { @@ -1730,7 +1730,13 @@ mod tests { .attr_value(constants::DW_AT_producer) .unwrap() .unwrap(); - assert_eq!(dwarf.attr_string(read_producer).unwrap().slice(), producer); + assert_eq!( + dwarf + .attr_string(&read_unit1, read_producer) + .unwrap() + .slice(), + producer + ); } let mut children = root.children().cloned(); @@ -1754,7 +1760,10 @@ mod tests { .attr_value(constants::DW_AT_name) .unwrap() .unwrap(); - assert_eq!(dwarf.attr_string(read_name).unwrap().slice(), name); + assert_eq!( + dwarf.attr_string(&read_unit1, read_name).unwrap().slice(), + name + ); } { @@ -1776,7 +1785,10 @@ mod tests { .attr_value(constants::DW_AT_name) .unwrap() .unwrap(); - assert_eq!(dwarf.attr_string(read_name).unwrap().slice(), name); + assert_eq!( + dwarf.attr_string(&read_unit1, read_name).unwrap().slice(), + name + ); } assert!(read_entries.next_dfs().unwrap().is_none());