diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /vendor/thorin-dwp/src | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/thorin-dwp/src')
-rw-r--r-- | vendor/thorin-dwp/src/error.rs | 294 | ||||
-rw-r--r-- | vendor/thorin-dwp/src/ext.rs | 144 | ||||
-rw-r--r-- | vendor/thorin-dwp/src/index.rs | 333 | ||||
-rw-r--r-- | vendor/thorin-dwp/src/lib.rs | 310 | ||||
-rw-r--r-- | vendor/thorin-dwp/src/package.rs | 728 | ||||
-rw-r--r-- | vendor/thorin-dwp/src/relocate.rs | 189 | ||||
-rw-r--r-- | vendor/thorin-dwp/src/strings.rs | 143 |
7 files changed, 2141 insertions, 0 deletions
diff --git a/vendor/thorin-dwp/src/error.rs b/vendor/thorin-dwp/src/error.rs new file mode 100644 index 000000000..e49722afa --- /dev/null +++ b/vendor/thorin-dwp/src/error.rs @@ -0,0 +1,294 @@ +use std::error::Error as StdError; +use std::fmt; + +pub(crate) type Result<T> = std::result::Result<T, Error>; + +/// Helper trait for converting an error to a `&dyn std::error::Error`. +pub trait AsDynError<'a> { + fn as_dyn_error(&self) -> &(dyn StdError + 'a); +} + +impl<'a, T: StdError + 'a> AsDynError<'a> for T { + #[inline] + fn as_dyn_error(&self) -> &(dyn StdError + 'a) { + self + } +} + +/// Diagnostics (and contexts) emitted during DWARF packaging. +#[derive(Debug)] +#[non_exhaustive] +pub enum Error { + /// Failure to read input file. + /// + /// This error occurs in the `Session::read_input` function provided by the user of `thorin`. + ReadInput(std::io::Error), + /// Failed to parse kind of input file. + /// + /// Input file kind is necessary to determine how to parse the rest of the input, and to + /// validate that the input file is of a type that `thorin` can process. + ParseFileKind(object::Error), + /// Failed to parse object file. + ParseObjectFile(object::Error), + /// Failed to parse archive file. + ParseArchiveFile(object::Error), + /// Failed to parse archive member. + ParseArchiveMember(object::Error), + /// Invalid kind of input. + /// + /// Only archive and elf files are supported input files. + InvalidInputKind, + /// Failed to decompress data. + /// + /// `thorin` uses `object` for decompression, so `object` probably didn't have support for the + /// type of compression used. + DecompressData(object::Error), + /// Section without a name. + NamelessSection(object::Error, usize), + /// Relocation has invalid symbol for a section. + RelocationWithInvalidSymbol(String, usize), + /// Multiple relocations for a section. + MultipleRelocations(String, usize), + /// Unsupported relocations for a section. + UnsupportedRelocation(String, usize), + /// Input object that has a `DwoId` (or `DebugTypeSignature`) does not have a + /// `DW_AT_GNU_dwo_name` or `DW_AT_dwo_name` attribute. + MissingDwoName(u64), + /// Input object has no compilation units. + NoCompilationUnits, + /// No top-level debugging information entry in unit. + NoDie, + /// Top-level debugging information entry is not a compilation/type unit. + TopLevelDieNotUnit, + /// Section required of input DWARF objects was missing. + MissingRequiredSection(&'static str), + /// Failed to parse unit abbreviations. + ParseUnitAbbreviations(gimli::read::Error), + /// Failed to parse unit attribute. + ParseUnitAttribute(gimli::read::Error), + /// Failed to parse unit header. + ParseUnitHeader(gimli::read::Error), + /// Failed to parse unit. + ParseUnit(gimli::read::Error), + /// Input DWARF package has a different index version than the version being output. + IncompatibleIndexVersion(String, u16, u16), + /// Failed to read string offset from `.debug_str_offsets` at index. + OffsetAtIndex(gimli::read::Error, u64), + /// Failed to read string from `.debug_str` at offset. + StrAtOffset(gimli::read::Error, usize), + /// Failed to parse index section. + /// + /// If an input file is a DWARF package, its index section needs to be read to ensure that the + /// contributions within it are preserved. + ParseIndex(gimli::read::Error, String), + /// Compilation unit in DWARF package is not its index. + UnitNotInIndex(u64), + /// Row for a compilation unit is not in the index. + RowNotInIndex(gimli::read::Error, u32), + /// Section not found in unit's row in index, i.e. a DWARF package contains a section but its + /// index doesn't record contributions to it. + SectionNotInRow, + /// Compilation unit in input DWARF object has no content. + EmptyUnit(u64), + /// Found multiple `.debug_info.dwo` sections. + MultipleDebugInfoSection, + /// Found multiple `.debug_types.dwo` sections in a DWARF package file. + MultipleDebugTypesSection, + /// Found a regular compilation unit in a DWARF object. + NotSplitUnit, + /// Found duplicate split compilation unit. + DuplicateUnit(u64), + /// Unit referenced by an executable was not found. + MissingReferencedUnit(u64), + /// No output object was created from inputs + NoOutputObjectCreated, + /// Input objects have different encodings. + MixedInputEncodings, + + /// Catch-all for `std::io::Error`. + Io(std::io::Error), + /// Catch-all for `object::Error`. + ObjectRead(object::Error), + /// Catch-all for `object::write::Error`. + ObjectWrite(object::write::Error), + /// Catch-all for `gimli::read::Error`. + GimliRead(gimli::read::Error), + /// Catch-all for `gimli::write::Error`. + GimliWrite(gimli::write::Error), +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Error::ReadInput(source) => Some(source.as_dyn_error()), + Error::ParseFileKind(source) => Some(source.as_dyn_error()), + Error::ParseObjectFile(source) => Some(source.as_dyn_error()), + Error::ParseArchiveFile(source) => Some(source.as_dyn_error()), + Error::ParseArchiveMember(source) => Some(source.as_dyn_error()), + Error::InvalidInputKind => None, + Error::DecompressData(source) => Some(source.as_dyn_error()), + Error::NamelessSection(source, _) => Some(source.as_dyn_error()), + Error::RelocationWithInvalidSymbol(_, _) => None, + Error::MultipleRelocations(_, _) => None, + Error::UnsupportedRelocation(_, _) => None, + Error::MissingDwoName(_) => None, + Error::NoCompilationUnits => None, + Error::NoDie => None, + Error::TopLevelDieNotUnit => None, + Error::MissingRequiredSection(_) => None, + Error::ParseUnitAbbreviations(source) => Some(source.as_dyn_error()), + Error::ParseUnitAttribute(source) => Some(source.as_dyn_error()), + Error::ParseUnitHeader(source) => Some(source.as_dyn_error()), + Error::ParseUnit(source) => Some(source.as_dyn_error()), + Error::IncompatibleIndexVersion(_, _, _) => None, + Error::OffsetAtIndex(source, _) => Some(source.as_dyn_error()), + Error::StrAtOffset(source, _) => Some(source.as_dyn_error()), + Error::ParseIndex(source, _) => Some(source.as_dyn_error()), + Error::UnitNotInIndex(_) => None, + Error::RowNotInIndex(source, _) => Some(source.as_dyn_error()), + Error::SectionNotInRow => None, + Error::EmptyUnit(_) => None, + Error::MultipleDebugInfoSection => None, + Error::MultipleDebugTypesSection => None, + Error::NotSplitUnit => None, + Error::DuplicateUnit(_) => None, + Error::MissingReferencedUnit(_) => None, + Error::NoOutputObjectCreated => None, + Error::MixedInputEncodings => None, + Error::Io(transparent) => StdError::source(transparent.as_dyn_error()), + Error::ObjectRead(transparent) => StdError::source(transparent.as_dyn_error()), + Error::ObjectWrite(transparent) => StdError::source(transparent.as_dyn_error()), + Error::GimliRead(transparent) => StdError::source(transparent.as_dyn_error()), + Error::GimliWrite(transparent) => StdError::source(transparent.as_dyn_error()), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::ReadInput(_) => write!(f, "Failed to read input file"), + Error::ParseFileKind(_) => write!(f, "Failed to parse input file kind"), + Error::ParseObjectFile(_) => write!(f, "Failed to parse input object file"), + Error::ParseArchiveFile(_) => write!(f, "Failed to parse input archive file"), + Error::ParseArchiveMember(_) => write!(f, "Failed to parse archive member"), + Error::InvalidInputKind => write!(f, "Input is not an archive or elf object"), + Error::DecompressData(_) => write!(f, "Failed to decompress compressed section"), + Error::NamelessSection(_, offset) => { + write!(f, "Section without name at offset 0x{:08x}", offset) + } + Error::RelocationWithInvalidSymbol(section, offset) => write!( + f, + "Relocation with invalid symbol for section `{}` at offset 0x{:08x}", + section, offset + ), + Error::MultipleRelocations(section, offset) => write!( + f, + "Multiple relocations for section `{}` at offset 0x{:08x}", + section, offset + ), + Error::UnsupportedRelocation(section, offset) => write!( + f, + "Unsupported relocation for section {} at offset 0x{:08x}", + section, offset + ), + Error::MissingDwoName(id) => { + write!(f, "Missing path attribute to DWARF object (0x{:08x})", id) + } + Error::NoCompilationUnits => { + write!(f, "Input object has no compilation units") + } + Error::NoDie => { + write!(f, "No top-level debugging information entry in compilation/type unit") + } + Error::TopLevelDieNotUnit => { + write!(f, "Top-level debugging information entry is not a compilation/type unit") + } + Error::MissingRequiredSection(section) => { + write!(f, "Input object missing required section `{}`", section) + } + Error::ParseUnitAbbreviations(_) => write!(f, "Failed to parse unit abbreviations"), + Error::ParseUnitAttribute(_) => write!(f, "Failed to parse unit attribute"), + Error::ParseUnitHeader(_) => write!(f, "Failed to parse unit header"), + Error::ParseUnit(_) => write!(f, "Failed to parse unit"), + Error::IncompatibleIndexVersion(section, format, actual) => { + write!( + f, + "Incompatible `{}` index version: found version {}, expected version {}", + section, actual, format + ) + } + Error::OffsetAtIndex(_, index) => { + write!(f, "Read offset at index {} of `.debug_str_offsets.dwo` section", index) + } + Error::StrAtOffset(_, offset) => { + write!(f, "Read string at offset 0x{:08x} of `.debug_str.dwo` section", offset) + } + Error::ParseIndex(_, section) => { + write!(f, "Failed to parse `{}` index section", section) + } + Error::UnitNotInIndex(unit) => { + write!(f, "Unit 0x{0:08x} from input package is not in its index", unit) + } + Error::RowNotInIndex(_, row) => { + write!(f, "Row {0} found in index's hash table not present in index", row) + } + Error::SectionNotInRow => write!(f, "Section not found in unit's row in index"), + Error::EmptyUnit(unit) => { + write!(f, "Unit 0x{:08x} in input DWARF object with no data", unit) + } + Error::MultipleDebugInfoSection => { + write!(f, "Multiple `.debug_info.dwo` sections") + } + Error::MultipleDebugTypesSection => { + write!(f, "Multiple `.debug_types.dwo` sections in a package") + } + Error::NotSplitUnit => { + write!(f, "Regular compilation unit in object (missing dwo identifier)") + } + Error::DuplicateUnit(unit) => { + write!(f, "Duplicate split compilation unit (0x{:08x})", unit) + } + Error::MissingReferencedUnit(unit) => { + write!(f, "Unit 0x{:08x} referenced by executable was not found", unit) + } + Error::NoOutputObjectCreated => write!(f, "No output object was created from inputs"), + Error::MixedInputEncodings => write!(f, "Input objects haved mixed encodings"), + Error::Io(e) => fmt::Display::fmt(e, f), + Error::ObjectRead(e) => fmt::Display::fmt(e, f), + Error::ObjectWrite(e) => fmt::Display::fmt(e, f), + Error::GimliRead(e) => fmt::Display::fmt(e, f), + Error::GimliWrite(e) => fmt::Display::fmt(e, f), + } + } +} + +impl From<std::io::Error> for Error { + fn from(source: std::io::Error) -> Self { + Error::Io(source) + } +} + +impl From<object::Error> for Error { + fn from(source: object::Error) -> Self { + Error::ObjectRead(source) + } +} + +impl From<object::write::Error> for Error { + fn from(source: object::write::Error) -> Self { + Error::ObjectWrite(source) + } +} + +impl From<gimli::read::Error> for Error { + fn from(source: gimli::read::Error) -> Self { + Error::GimliRead(source) + } +} + +impl From<gimli::write::Error> for Error { + fn from(source: gimli::write::Error) -> Self { + Error::GimliWrite(source) + } +} diff --git a/vendor/thorin-dwp/src/ext.rs b/vendor/thorin-dwp/src/ext.rs new file mode 100644 index 000000000..a26122ba6 --- /dev/null +++ b/vendor/thorin-dwp/src/ext.rs @@ -0,0 +1,144 @@ +use gimli::{Encoding, EndianSlice, RunTimeEndian, UnitIndex}; +use object::{Endianness, ObjectSection}; + +use crate::{relocate::RelocationMap, Session}; + +/// Helper trait to translate between `object`'s `Endianness` and `gimli`'s `RunTimeEndian`. +pub(crate) trait EndianityExt { + fn as_runtime_endian(&self) -> RunTimeEndian; +} + +impl EndianityExt for Endianness { + fn as_runtime_endian(&self) -> RunTimeEndian { + match *self { + Endianness::Little => RunTimeEndian::Little, + Endianness::Big => RunTimeEndian::Big, + } + } +} + +/// Helper trait to add `compressed_data_range` function to `ObjectSection` types. +pub(crate) trait CompressedDataRangeExt<'input, 'session: 'input>: + ObjectSection<'input> +{ + /// Return the decompressed contents of the section data in the given range. + /// + /// Decompression happens only if the data is compressed. + fn compressed_data_range( + &self, + sess: &'session impl Session<RelocationMap>, + address: u64, + size: u64, + ) -> object::Result<Option<&'input [u8]>>; +} + +impl<'input, 'session: 'input, S> CompressedDataRangeExt<'input, 'session> for S +where + S: ObjectSection<'input>, +{ + fn compressed_data_range( + &self, + sess: &'session impl Session<RelocationMap>, + address: u64, + size: u64, + ) -> object::Result<Option<&'input [u8]>> { + let data = self.compressed_data()?.decompress()?; + + /// Originally from `object::read::util`, used in `ObjectSection::data_range`, but not + /// public. + fn data_range( + data: &[u8], + data_address: u64, + range_address: u64, + size: u64, + ) -> Option<&[u8]> { + let offset = range_address.checked_sub(data_address)?; + data.get(offset.try_into().ok()?..)?.get(..size.try_into().ok()?) + } + + let data_ref = sess.alloc_owned_cow(data); + Ok(data_range(data_ref, self.address(), address, size)) + } +} + +/// Helper trait that abstracts over `gimli::DebugCuIndex` and `gimli::DebugTuIndex`. +pub(crate) trait IndexSectionExt<'input, Endian: gimli::Endianity, R: gimli::Reader>: + gimli::Section<R> +{ + fn new(section: &'input [u8], endian: Endian) -> Self; + + fn index(self) -> gimli::read::Result<UnitIndex<R>>; +} + +impl<'input, Endian: gimli::Endianity> IndexSectionExt<'input, Endian, EndianSlice<'input, Endian>> + for gimli::DebugCuIndex<EndianSlice<'input, Endian>> +{ + fn new(section: &'input [u8], endian: Endian) -> Self { + Self::new(section, endian) + } + + fn index(self) -> gimli::read::Result<UnitIndex<EndianSlice<'input, Endian>>> { + Self::index(self) + } +} + +impl<'input, Endian: gimli::Endianity> IndexSectionExt<'input, Endian, EndianSlice<'input, Endian>> + for gimli::DebugTuIndex<EndianSlice<'input, Endian>> +{ + fn new(section: &'input [u8], endian: Endian) -> Self { + Self::new(section, endian) + } + + fn index(self) -> gimli::read::Result<UnitIndex<EndianSlice<'input, Endian>>> { + Self::index(self) + } +} + +/// Helper trait to add DWARF package specific functions to the `Encoding` type. +pub(crate) trait PackageFormatExt { + /// Returns `true` if this `Encoding` would produce to a DWARF 5-standardized package file. + /// + /// See Sec 7.3.5 and Appendix F of the [DWARF specification]. + /// + /// [DWARF specification]: https://dwarfstd.org/doc/DWARF5.pdf + fn is_std_dwarf_package_format(&self) -> bool; + + /// Returns `true` if this `Encoding` would produce a GNU Extension DWARF package file + /// (preceded standardized version from DWARF 5). + /// + /// See [specification](https://gcc.gnu.org/wiki/DebugFissionDWP). + fn is_gnu_extension_dwarf_package_format(&self) -> bool; + + /// Returns index version of DWARF package for this `Encoding`. + fn dwarf_package_index_version(&self) -> u16; + + /// Returns `true` if the dwarf package index version provided is compatible with this + /// `Encoding`. + fn is_compatible_dwarf_package_index_version(&self, index_version: u16) -> bool; +} + +impl PackageFormatExt for Encoding { + fn is_gnu_extension_dwarf_package_format(&self) -> bool { + !self.is_std_dwarf_package_format() + } + + fn is_std_dwarf_package_format(&self) -> bool { + self.version >= 5 + } + + fn dwarf_package_index_version(&self) -> u16 { + if self.is_gnu_extension_dwarf_package_format() { + 2 + } else { + 5 + } + } + + fn is_compatible_dwarf_package_index_version(&self, index_version: u16) -> bool { + if self.is_gnu_extension_dwarf_package_format() { + index_version == 2 + } else { + index_version >= 5 + } + } +} diff --git a/vendor/thorin-dwp/src/index.rs b/vendor/thorin-dwp/src/index.rs new file mode 100644 index 000000000..8a65483c5 --- /dev/null +++ b/vendor/thorin-dwp/src/index.rs @@ -0,0 +1,333 @@ +use std::fmt; + +use gimli::{ + write::{EndianVec, Writer}, + Encoding, +}; +use tracing::{debug, trace}; + +use crate::{ + error::{Error, Result}, + ext::PackageFormatExt, + package::DwarfObject, +}; + +/// Helper trait for types that can be used in creating the `.debug_{cu,tu}_index` hash table. +pub(crate) trait Bucketable { + fn index(&self) -> u64; +} + +/// Returns a hash table computed for `elements`. Used in the `.debug_{cu,tu}_index` sections. +#[tracing::instrument(level = "trace", skip_all)] +fn bucket<B: Bucketable + fmt::Debug>(elements: &[B]) -> Vec<u32> { + let unit_count: u32 = elements.len().try_into().expect("unit count larger than u32"); + let num_buckets = if elements.len() < 2 { 2 } else { (3 * unit_count / 2).next_power_of_two() }; + let mask: u64 = num_buckets as u64 - 1; + trace!(?mask); + + let mut buckets = vec![0u32; num_buckets as usize]; + trace!(?buckets); + for (elem, i) in elements.iter().zip(0u32..) { + trace!(?i, ?elem); + let s = elem.index(); + let mut h = s & mask; + let hp = ((s >> 32) & mask) | 1; + trace!(?s, ?h, ?hp); + + while buckets[h as usize] > 0 { + assert!(elements[(buckets[h as usize] - 1) as usize].index() != elem.index()); + h = (h + hp) & mask; + trace!(?h); + } + + buckets[h as usize] = i + 1; + trace!(?buckets); + } + + buckets +} + +/// New-type'd offset into a section of a compilation/type unit's contribution. +#[derive(Copy, Clone, Eq, Hash, PartialEq)] +pub(crate) struct ContributionOffset(pub(crate) u64); + +impl fmt::Debug for ContributionOffset { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ContributionOffset({:#x})", self.0) + } +} + +/// Type alias for the size of a compilation/type unit's contribution. +type ContributionSize = u64; + +/// Contribution to a section - offset and size. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub(crate) struct Contribution { + /// Offset of this contribution into its containing section. + pub(crate) offset: ContributionOffset, + /// Size of this contribution in its containing section. + pub(crate) size: ContributionSize, +} + +/// Populated columns in the `.debug_cu_index` or `.debug_tu_index` section. +#[derive(Copy, Clone, Debug, Default)] +pub(crate) struct IndexColumns { + pub(crate) debug_info: bool, + pub(crate) debug_types: bool, + pub(crate) debug_abbrev: bool, + pub(crate) debug_line: bool, + pub(crate) debug_loc: bool, + pub(crate) debug_loclists: bool, + pub(crate) debug_rnglists: bool, + pub(crate) debug_str_offsets: bool, + pub(crate) debug_macinfo: bool, + pub(crate) debug_macro: bool, +} + +impl IndexColumns { + /// Return the number of columns required for all index entries. + fn number_of_columns(&self) -> u32 { + self.debug_info as u32 + + self.debug_types as u32 + + self.debug_abbrev as u32 + + self.debug_line as u32 + + self.debug_loc as u32 + + self.debug_loclists as u32 + + self.debug_rnglists as u32 + + self.debug_str_offsets as u32 + + self.debug_macinfo as u32 + + self.debug_macro as u32 + } + + /// Update the columns to include the columns required for an index entry. + fn add_entry(&mut self, entry: &IndexEntry) { + self.debug_info |= entry.debug_info.is_some(); + self.debug_types |= entry.debug_types.is_some(); + self.debug_abbrev |= entry.debug_abbrev.is_some(); + self.debug_line |= entry.debug_line.is_some(); + self.debug_loc |= entry.debug_loc.is_some(); + self.debug_loclists |= entry.debug_loclists.is_some(); + self.debug_rnglists |= entry.debug_rnglists.is_some(); + self.debug_str_offsets |= entry.debug_str_offsets.is_some(); + self.debug_macinfo |= entry.debug_macinfo.is_some(); + self.debug_macro |= entry.debug_macro.is_some(); + } + + /// Write the header row for the columns. + /// + /// There is only a single header row in any index section, its contents depend on the output + /// format and the columns from contributions so the complete index entries are required to + /// know what header to write. + fn write_header<Endian: gimli::Endianity>( + &self, + out: &mut EndianVec<Endian>, + encoding: Encoding, + ) -> Result<()> { + if encoding.is_gnu_extension_dwarf_package_format() { + if self.debug_info { + out.write_u32(gimli::DW_SECT_V2_INFO.0)?; + } + if self.debug_types { + out.write_u32(gimli::DW_SECT_V2_TYPES.0)?; + } + if self.debug_abbrev { + out.write_u32(gimli::DW_SECT_V2_ABBREV.0)?; + } + if self.debug_line { + out.write_u32(gimli::DW_SECT_V2_LINE.0)?; + } + if self.debug_loc { + out.write_u32(gimli::DW_SECT_V2_LOC.0)?; + } + if self.debug_str_offsets { + out.write_u32(gimli::DW_SECT_V2_STR_OFFSETS.0)?; + } + if self.debug_macinfo { + out.write_u32(gimli::DW_SECT_V2_MACINFO.0)?; + } + if self.debug_macro { + out.write_u32(gimli::DW_SECT_V2_MACRO.0)?; + } + } else { + if self.debug_info { + out.write_u32(gimli::DW_SECT_INFO.0)?; + } + if self.debug_abbrev { + out.write_u32(gimli::DW_SECT_ABBREV.0)?; + } + if self.debug_line { + out.write_u32(gimli::DW_SECT_LINE.0)?; + } + if self.debug_loclists { + out.write_u32(gimli::DW_SECT_LOCLISTS.0)?; + } + if self.debug_rnglists { + out.write_u32(gimli::DW_SECT_RNGLISTS.0)?; + } + if self.debug_str_offsets { + out.write_u32(gimli::DW_SECT_STR_OFFSETS.0)?; + } + if self.debug_macro { + out.write_u32(gimli::DW_SECT_MACRO.0)?; + } + } + + Ok(()) + } +} + +/// Entry into the `.debug_cu_index` or `.debug_tu_index` section. +/// +/// Contributions from `.debug_loclists.dwo` and `.debug_rnglists.dwo` from type units aren't +/// defined in the DWARF 5 specification but are tested by `llvm-dwp`'s test suite. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub(crate) struct IndexEntry { + pub(crate) encoding: Encoding, + pub(crate) id: DwarfObject, + pub(crate) debug_info: Option<Contribution>, + pub(crate) debug_types: Option<Contribution>, + pub(crate) debug_abbrev: Option<Contribution>, + pub(crate) debug_line: Option<Contribution>, + pub(crate) debug_loc: Option<Contribution>, + pub(crate) debug_loclists: Option<Contribution>, + pub(crate) debug_rnglists: Option<Contribution>, + pub(crate) debug_str_offsets: Option<Contribution>, + pub(crate) debug_macinfo: Option<Contribution>, + pub(crate) debug_macro: Option<Contribution>, +} + +impl IndexEntry { + /// Visit each contribution in an entry, calling `proj` to write a specific field. + #[tracing::instrument(level = "trace", skip(out, proj))] + fn write_contribution<Endian, Proj>( + &self, + out: &mut EndianVec<Endian>, + proj: Proj, + columns: &IndexColumns, + ) -> Result<()> + where + Endian: gimli::Endianity, + Proj: Copy + Fn(Contribution) -> u32, + { + let proj = |contrib: Option<Contribution>| contrib.map(proj).unwrap_or(0); + if columns.debug_info { + out.write_u32(proj(self.debug_info))?; + } + if columns.debug_types { + out.write_u32(proj(self.debug_types))?; + } + if columns.debug_abbrev { + out.write_u32(proj(self.debug_abbrev))?; + } + if columns.debug_line { + out.write_u32(proj(self.debug_line))?; + } + if columns.debug_loc { + out.write_u32(proj(self.debug_loc))?; + } + if columns.debug_loclists { + out.write_u32(proj(self.debug_loclists))?; + } + if columns.debug_rnglists { + out.write_u32(proj(self.debug_rnglists))?; + } + if columns.debug_str_offsets { + out.write_u32(proj(self.debug_str_offsets))?; + } + if columns.debug_macinfo { + out.write_u32(proj(self.debug_macinfo))?; + } + if columns.debug_macro { + out.write_u32(proj(self.debug_macro))?; + } + + Ok(()) + } +} + +impl Bucketable for IndexEntry { + fn index(&self) -> u64 { + self.id.index() + } +} + +/// Write a `.debug_{cu,tu}_index` section given `IndexEntry`s. +#[tracing::instrument(level = "trace")] +pub(crate) fn write_index<'output, Endian: gimli::Endianity>( + endianness: Endian, + entries: &[IndexEntry], +) -> Result<EndianVec<Endian>> { + let mut out = EndianVec::new(endianness); + + if entries.len() == 0 { + return Ok(out); + } + + let buckets = bucket(entries); + debug!(?buckets); + + let encoding = entries[0].encoding; + if !entries.iter().all(|e| e.encoding == encoding) { + return Err(Error::MixedInputEncodings); + } + debug!(?encoding); + + let mut columns = IndexColumns::default(); + for entry in entries { + columns.add_entry(entry); + } + let num_columns = columns.number_of_columns(); + debug!(?entries, ?columns, ?num_columns); + + // Write header.. + if encoding.is_gnu_extension_dwarf_package_format() { + // GNU Extension + out.write_u32(2)?; + } else { + // DWARF 5 + out.write_u16(5)?; + // Reserved padding + out.write_u16(0)?; + } + + // Columns (e.g. info, abbrev, loc, etc.) + out.write_u32(num_columns)?; + // Number of units + out.write_u32(entries.len().try_into().expect("number of units larger than u32"))?; + // Number of buckets + out.write_u32(buckets.len().try_into().expect("number of buckets larger than u32"))?; + + // Write signatures.. + for i in &buckets { + if *i > 0 { + out.write_u64(entries[(*i - 1) as usize].index())?; + } else { + out.write_u64(0)?; + } + } + + // Write indices.. + for i in &buckets { + out.write_u32(*i)?; + } + + // Write column headers.. + columns.write_header(&mut out, encoding)?; + + // Write offsets.. + let write_offset = |contrib: Contribution| { + contrib.offset.0.try_into().expect("contribution offset larger than u32") + }; + for entry in entries { + entry.write_contribution(&mut out, write_offset, &columns)?; + } + + // Write sizes.. + let write_size = + |contrib: Contribution| contrib.size.try_into().expect("contribution size larger than u32"); + for entry in entries { + entry.write_contribution(&mut out, write_size, &columns)?; + } + + Ok(out) +} diff --git a/vendor/thorin-dwp/src/lib.rs b/vendor/thorin-dwp/src/lib.rs new file mode 100644 index 000000000..b084aa311 --- /dev/null +++ b/vendor/thorin-dwp/src/lib.rs @@ -0,0 +1,310 @@ +use std::{ + borrow::Cow, + collections::HashSet, + fmt, + path::{Path, PathBuf}, +}; + +use gimli::{EndianSlice, Reader}; +use object::{write::Object as WritableObject, FileKind, Object, ObjectSection}; +use tracing::{debug, trace}; + +use crate::{ + error::Result, + ext::EndianityExt, + index::Bucketable, + package::{dwo_identifier_of_unit, DwarfObject, InProgressDwarfPackage}, + relocate::{add_relocations, Relocate, RelocationMap}, +}; + +mod error; +mod ext; +mod index; +mod package; +mod relocate; +mod strings; + +pub use crate::error::Error; + +/// `Session` is expected to be implemented by users of `thorin`, allowing users of `thorin` to +/// decide how to manage data, rather than `thorin` having arenas internally. +pub trait Session<Relocations> { + /// Returns a reference to `data`'s contents with lifetime `'session`. + fn alloc_data<'session>(&'session self, data: Vec<u8>) -> &'session [u8]; + + /// Returns a reference to `data`'s contents with lifetime `'input`. + /// + /// If `Cow` is borrowed, then return the contained reference (`'input`). If `Cow` is owned, + /// then calls `alloc_data` to return a reference of lifetime `'session`, which is guaranteed + /// to be longer than `'input`, so can be returned. + fn alloc_owned_cow<'input, 'session: 'input>( + &'session self, + data: Cow<'input, [u8]>, + ) -> &'input [u8] { + match data { + Cow::Borrowed(data) => data, + Cow::Owned(data) => self.alloc_data(data), + } + } + + /// Returns a reference to `relocation` with lifetime `'session`. + fn alloc_relocation<'session>(&'session self, data: Relocations) -> &'session Relocations; + + /// Returns a reference to contents of file at `path` with lifetime `'session`. + fn read_input<'session>(&'session self, path: &Path) -> std::io::Result<&'session [u8]>; +} + +/// Should missing DWARF objects referenced by executables be skipped or result in an error? +/// +/// Referenced objects that are still missing when the DWARF package is finished will result in +/// an error. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub enum MissingReferencedObjectBehaviour { + /// Skip missing referenced DWARF objects - useful if this is expected, i.e. the path in the + /// executable is wrong, but the referenced object will be found because it is an input. + Skip, + /// Error when encountering missing referenced DWARF objects. + Error, +} + +impl MissingReferencedObjectBehaviour { + /// Should missing referenced objects be skipped? + pub fn skip_missing(&self) -> bool { + match *self { + MissingReferencedObjectBehaviour::Skip => true, + MissingReferencedObjectBehaviour::Error => false, + } + } +} + +/// Builder for DWARF packages, add input objects/packages with `add_input_object` or input objects +/// referenced by an executable with `add_executable` before accessing the completed object with +/// `finish`. +pub struct DwarfPackage<'output, 'session: 'output, Sess: Session<RelocationMap>> { + sess: &'session Sess, + maybe_in_progress: Option<InProgressDwarfPackage<'output>>, + targets: HashSet<DwarfObject>, +} + +impl<'output, 'session: 'output, Sess> fmt::Debug for DwarfPackage<'output, 'session, Sess> +where + Sess: Session<RelocationMap>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DwarfPackage") + .field("in_progress", &self.maybe_in_progress) + .field("target_count", &self.targets.len()) + .finish() + } +} + +impl<'output, 'session: 'output, Sess> DwarfPackage<'output, 'session, Sess> +where + Sess: Session<RelocationMap>, +{ + /// Create a new `DwarfPackage` with the provided `Session` implementation. + pub fn new(sess: &'session Sess) -> Self { + Self { sess, maybe_in_progress: None, targets: HashSet::new() } + } + + /// Add an input object to the in-progress package. + #[tracing::instrument(level = "trace", skip(obj))] + fn process_input_object<'input>(&mut self, obj: &'input object::File<'input>) -> Result<()> { + if self.maybe_in_progress.is_none() { + self.maybe_in_progress = + Some(InProgressDwarfPackage::new(obj.architecture(), obj.endianness())); + } + + let encoding = if let Some(section) = obj.section_by_name(".debug_info.dwo") { + let data = section.compressed_data()?.decompress()?; + let data_ref = self.sess.alloc_owned_cow(data); + let debug_info = gimli::DebugInfo::new(data_ref, obj.endianness().as_runtime_endian()); + debug_info + .units() + .next() + .map_err(Error::ParseUnitHeader)? + .map(|root_header| root_header.encoding()) + .ok_or(Error::NoCompilationUnits)? + } else { + debug!("no `.debug_info.dwo` in input dwarf object"); + return Ok(()); + }; + + let sess = self.sess; + self.maybe_in_progress + .as_mut() + .expect("`process_input_object` is broken") + .add_input_object(sess, obj, encoding) + } + + /// Add input objects referenced by executable to the DWARF package. + #[tracing::instrument(level = "trace")] + pub fn add_executable( + &mut self, + path: &Path, + missing_behaviour: MissingReferencedObjectBehaviour, + ) -> Result<()> { + let data = self.sess.read_input(path).map_err(Error::ReadInput)?; + let obj = object::File::parse(data).map_err(Error::ParseObjectFile)?; + + let mut load_section = |id: gimli::SectionId| -> Result<_> { + let mut relocations = RelocationMap::default(); + let data = match obj.section_by_name(&id.name()) { + Some(ref section) => { + add_relocations(&mut relocations, &obj, section)?; + section.compressed_data()?.decompress()? + } + // Use a non-zero capacity so that `ReaderOffsetId`s are unique. + None => Cow::Owned(Vec::with_capacity(1)), + }; + + let data_ref = self.sess.alloc_owned_cow(data); + let reader = EndianSlice::new(data_ref, obj.endianness().as_runtime_endian()); + let section = reader; + let relocations = self.sess.alloc_relocation(relocations); + Ok(Relocate { relocations, section, reader }) + }; + + let dwarf = gimli::Dwarf::load(&mut load_section)?; + + let mut iter = dwarf.units(); + while let Some(header) = iter.next().map_err(Error::ParseUnitHeader)? { + let unit = dwarf.unit(header).map_err(Error::ParseUnit)?; + + let target = match dwo_identifier_of_unit(&dwarf.debug_abbrev, &unit.header)? { + Some(target) => target, + None => { + debug!("no target"); + continue; + } + }; + + let dwo_name = { + let mut cursor = unit.header.entries(&unit.abbreviations); + cursor.next_dfs()?; + let root = cursor.current().expect("unit w/out root debugging information entry"); + + let dwo_name = if let Some(val) = root.attr_value(gimli::DW_AT_dwo_name)? { + // DWARF 5 + val + } else if let Some(val) = root.attr_value(gimli::DW_AT_GNU_dwo_name)? { + // GNU Extension + val + } else { + return Err(Error::MissingDwoName(target.index())); + }; + + dwarf.attr_string(&unit, dwo_name)?.to_string()?.into_owned() + }; + + // Prepend the compilation directory if it exists. + let mut path = if let Some(comp_dir) = &unit.comp_dir { + PathBuf::from(comp_dir.to_string()?.into_owned()) + } else { + PathBuf::new() + }; + path.push(dwo_name); + + // Only add `DwoId`s to the targets, not `DebugTypeSignature`s. There doesn't + // appear to be a "skeleton type unit" to find the corresponding unit of (there are + // normal type units in an executable, but should we expect to find a corresponding + // split type unit for those?). + if matches!(target, DwarfObject::Compilation(_)) { + // Input objects are processed first, if a DWARF object referenced by this + // executable was already found then don't add it to the target and try to add it + // again. + if let Some(package) = &self.maybe_in_progress { + if package.contained_units().contains(&target) { + continue; + } + } + + debug!(?target, "adding target"); + self.targets.insert(target); + } + + match self.add_input_object(&path) { + Ok(()) => (), + Err(Error::ReadInput(..)) if missing_behaviour.skip_missing() => (), + Err(e) => return Err(e), + } + } + + Ok(()) + } + + /// Add an input object to the DWARF package. + /// + /// Input object must be an archive or an elf object. + #[tracing::instrument(level = "trace")] + pub fn add_input_object(&mut self, path: &Path) -> Result<()> { + let data = self.sess.read_input(&path).map_err(Error::ReadInput)?; + + let kind = FileKind::parse(data).map_err(Error::ParseFileKind)?; + trace!(?kind); + match kind { + FileKind::Archive => { + let archive = object::read::archive::ArchiveFile::parse(data) + .map_err(Error::ParseArchiveFile)?; + + for member in archive.members() { + let member = member.map_err(Error::ParseArchiveMember)?; + let data = member.data(data)?; + + let kind = if let Ok(kind) = FileKind::parse(data) { + kind + } else { + trace!("skipping non-elf archive member"); + continue; + }; + + trace!(?kind, "archive member"); + match kind { + FileKind::Elf32 | FileKind::Elf64 => { + let obj = object::File::parse(data).map_err(Error::ParseObjectFile)?; + self.process_input_object(&obj)?; + } + _ => { + trace!("skipping non-elf archive member"); + } + } + } + + Ok(()) + } + FileKind::Elf32 | FileKind::Elf64 => { + let obj = object::File::parse(data).map_err(Error::ParseObjectFile)?; + self.process_input_object(&obj) + } + _ => Err(Error::InvalidInputKind), + } + } + + /// Returns the `object::write::Object` containing the created DWARF package. + /// + /// Returns an `Error::MissingReferencedUnit` if DWARF objects referenced by executables were + /// not subsequently found. + /// Returns an `Error::NoOutputObjectCreated` if no input objects or executables were provided. + #[tracing::instrument(level = "trace")] + pub fn finish(self) -> Result<WritableObject<'output>> { + match self.maybe_in_progress { + Some(package) => { + if let Some(missing) = self.targets.difference(package.contained_units()).next() { + return Err(Error::MissingReferencedUnit(missing.index())); + } + + package.finish() + } + None if !self.targets.is_empty() => { + let first_missing_unit = self + .targets + .iter() + .next() + .copied() + .expect("non-empty map doesn't have first element"); + Err(Error::MissingReferencedUnit(first_missing_unit.index())) + } + None => Err(Error::NoOutputObjectCreated), + } + } +} diff --git a/vendor/thorin-dwp/src/package.rs b/vendor/thorin-dwp/src/package.rs new file mode 100644 index 000000000..b71ae1207 --- /dev/null +++ b/vendor/thorin-dwp/src/package.rs @@ -0,0 +1,728 @@ +use std::{collections::HashSet, fmt}; + +use gimli::{Encoding, RunTimeEndian, UnitHeader, UnitIndex, UnitSectionOffset, UnitType}; +use object::{ + write::{Object as WritableObject, SectionId}, + BinaryFormat, Object, ObjectSection, SectionKind, +}; +use tracing::debug; + +use crate::{ + error::{Error, Result}, + ext::{CompressedDataRangeExt, EndianityExt, IndexSectionExt, PackageFormatExt}, + index::{write_index, Bucketable, Contribution, ContributionOffset, IndexEntry}, + relocate::RelocationMap, + strings::PackageStringTable, + Session, +}; + +/// New-type'd index (constructed from `gimli::DwoId`) with a custom `Debug` implementation to +/// print in hexadecimal. +#[derive(Copy, Clone, Eq, Hash, PartialEq)] +pub(crate) struct DwoId(pub(crate) u64); + +impl Bucketable for DwoId { + fn index(&self) -> u64 { + self.0 + } +} + +impl fmt::Debug for DwoId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "DwoId({:#x})", self.0) + } +} + +impl From<gimli::DwoId> for DwoId { + fn from(dwo_id: gimli::DwoId) -> Self { + Self(dwo_id.0) + } +} + +/// New-type'd index (constructed from `gimli::DebugTypeSignature`) with a custom `Debug` +/// implementation to print in hexadecimal. +#[derive(Copy, Clone, Eq, Hash, PartialEq)] +pub(crate) struct DebugTypeSignature(pub(crate) u64); + +impl Bucketable for DebugTypeSignature { + fn index(&self) -> u64 { + self.0 + } +} + +impl fmt::Debug for DebugTypeSignature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "DebugTypeSignature({:#x})", self.0) + } +} + +impl From<gimli::DebugTypeSignature> for DebugTypeSignature { + fn from(signature: gimli::DebugTypeSignature) -> Self { + Self(signature.0) + } +} + +/// Identifier for a DWARF object. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub(crate) enum DwarfObject { + /// `DwoId` identifying compilation units. + Compilation(DwoId), + /// `DebugTypeSignature` identifying type units. + Type(DebugTypeSignature), +} + +impl Bucketable for DwarfObject { + fn index(&self) -> u64 { + match *self { + DwarfObject::Compilation(dwo_id) => dwo_id.index(), + DwarfObject::Type(type_signature) => type_signature.index(), + } + } +} + +/// Returns the `DwoId` or `DebugTypeSignature` of a unit. +/// +/// **DWARF 5:** +/// +/// - `DwoId` is in the unit header of a skeleton unit (identifying the split compilation unit +/// that contains the debuginfo) or split compilation unit (identifying the skeleton unit that this +/// debuginfo corresponds to). +/// - `DebugTypeSignature` is in the unit header of a split type unit. +/// +/// **Earlier DWARF versions with GNU extension:** +/// +/// - `DW_AT_GNU_dwo_id` attribute of the DIE contains the `DwoId`. +#[tracing::instrument(level = "trace", skip(debug_abbrev, header))] +pub(crate) fn dwo_identifier_of_unit<R: gimli::Reader>( + debug_abbrev: &gimli::DebugAbbrev<R>, + header: &gimli::UnitHeader<R>, +) -> Result<Option<DwarfObject>> { + match header.type_() { + // Compilation units with DWARF 5 + UnitType::Skeleton(dwo_id) | UnitType::SplitCompilation(dwo_id) => { + Ok(Some(DwarfObject::Compilation(dwo_id.into()))) + } + // Compilation units with GNU Extension + UnitType::Compilation => { + let abbreviations = + header.abbreviations(&debug_abbrev).map_err(Error::ParseUnitAbbreviations)?; + let mut cursor = header.entries(&abbreviations); + cursor.next_dfs()?; + let root = cursor.current().ok_or(Error::NoDie)?; + match root.tag() { + gimli::DW_TAG_compile_unit | gimli::DW_TAG_type_unit => (), + _ => return Err(Error::TopLevelDieNotUnit), + } + let mut attrs = root.attrs(); + while let Some(attr) = attrs.next().map_err(Error::ParseUnitAttribute)? { + match (attr.name(), attr.value()) { + (gimli::constants::DW_AT_GNU_dwo_id, gimli::AttributeValue::DwoId(dwo_id)) => { + return Ok(Some(DwarfObject::Compilation(dwo_id.into()))) + } + _ => (), + } + } + + Ok(None) + } + // Type units with DWARF 5 + UnitType::SplitType { type_signature, .. } => { + Ok(Some(DwarfObject::Type(type_signature.into()))) + } + // Type units with GNU extension + UnitType::Type { type_signature, .. } => Ok(Some(DwarfObject::Type(type_signature.into()))), + // Wrong compilation unit type. + _ => Ok(None), + } +} + +/// Wrapper around `.debug_info.dwo` and `debug_types.dwo` unit iterators for uniform handling. +enum UnitHeaderIterator<R: gimli::Reader> { + DebugInfo(gimli::read::DebugInfoUnitHeadersIter<R>), + DebugTypes(gimli::read::DebugTypesUnitHeadersIter<R>), +} + +impl<R: gimli::Reader> UnitHeaderIterator<R> { + fn next(&mut self) -> gimli::read::Result<Option<UnitHeader<R>>> { + match self { + UnitHeaderIterator::DebugInfo(iter) => iter.next(), + UnitHeaderIterator::DebugTypes(iter) => iter.next(), + } + } +} + +/// Returns the parsed unit index from a `.debug_{cu,tu}_index` section. +pub(crate) fn maybe_load_index_section<'input, 'session: 'input, Endian, Index, R, Sess>( + sess: &'session Sess, + encoding: Encoding, + endian: Endian, + input: &object::File<'input>, +) -> Result<Option<UnitIndex<R>>> +where + Endian: gimli::Endianity, + Index: IndexSectionExt<'input, Endian, R>, + R: gimli::Reader, + Sess: Session<RelocationMap>, +{ + let index_name = Index::id().dwo_name().expect("index id w/out known value"); + if let Some(index_section) = input.section_by_name(index_name) { + let index_data = index_section + .compressed_data() + .and_then(|d| d.decompress()) + .map_err(Error::DecompressData)?; + let index_data_ref = sess.alloc_owned_cow(index_data); + let unit_index = Index::new(index_data_ref, endian) + .index() + .map_err(|e| Error::ParseIndex(e, index_name.to_string()))?; + + if !encoding.is_compatible_dwarf_package_index_version(unit_index.version()) { + return Err(Error::IncompatibleIndexVersion( + index_name.to_string(), + encoding.dwarf_package_index_version(), + unit_index.version(), + )); + } + + Ok(Some(unit_index)) + } else { + Ok(None) + } +} + +/// Returns a closure which takes an identifier and a `Option<Contribution>`, and returns an +/// adjusted contribution if the input file is a DWARF package (and the contribution was +/// present). +/// +/// For example, consider the `.debug_str_offsets` section: DWARF packages have a single +/// `.debug_str_offsets` section which contains the string offsets of all of its compilation/type +/// units, the contributions of each unit into that section are tracked in its +/// `.debug_{cu,tu}_index` section. +/// +/// When a DWARF package is the input, the contributions of the units which constituted that +/// package should not be lost when its `.debug_str_offsets` section is merged with the new +/// DWARF package currently being created. +/// +/// Given a parsed index section, use the size of its contribution to `.debug_str_offsets` as the +/// size of its contribution in the new unit (without this, it would be the size of the entire +/// `.debug_str_offsets` section from the input, rather than the part that the compilation unit +/// originally contributed to that). For subsequent units from the input, the offset in the +/// contribution will need to be adjusted to based on the size of the previous units. +/// +/// This function returns a "contribution adjustor" closure, which adjusts the contribution's +/// offset and size according to its contribution in the input's index and with an offset +/// accumulated over all calls to the closure. +pub(crate) fn create_contribution_adjustor<'input, R: 'input>( + cu_index: Option<&'input UnitIndex<R>>, + tu_index: Option<&'input UnitIndex<R>>, + target_section_id: gimli::SectionId, +) -> impl FnMut(DwarfObject, Option<Contribution>) -> Result<Option<Contribution>> + 'input +where + R: gimli::Reader, +{ + let mut cu_adjustment = 0; + let mut tu_adjustment = 0; + + move |identifier: DwarfObject, + contribution: Option<Contribution>| + -> Result<Option<Contribution>> { + let (adjustment, index) = match identifier { + DwarfObject::Compilation(_) => (&mut cu_adjustment, &cu_index), + DwarfObject::Type(_) => (&mut tu_adjustment, &tu_index), + }; + match (index, contribution) { + // dwp input with section + (Some(index), Some(contribution)) => { + let idx = identifier.index(); + let row_id = index.find(idx).ok_or(Error::UnitNotInIndex(idx))?; + let section = index + .sections(row_id) + .map_err(|e| Error::RowNotInIndex(e, row_id))? + .find(|index_section| index_section.section == target_section_id) + .ok_or(Error::SectionNotInRow)?; + let adjusted_offset: u64 = contribution.offset.0 + *adjustment; + *adjustment += section.size as u64; + + Ok(Some(Contribution { + offset: ContributionOffset(adjusted_offset), + size: section.size as u64, + })) + } + // dwp input without section + (Some(_) | None, None) => Ok(contribution), + // dwo input with section, but we aren't adjusting this particular index + (None, Some(_)) => Ok(contribution), + } + } +} + +/// Wrapper around `object::write::Object` that keeps track of the section indexes relevant to +/// DWARF packaging. +struct DwarfPackageObject<'file> { + /// Object file being created. + obj: WritableObject<'file>, + + /// Identifier for output `.debug_cu_index.dwo` section. + debug_cu_index: Option<SectionId>, + /// `.debug_tu_index.dwo` + debug_tu_index: Option<SectionId>, + /// `.debug_info.dwo` + debug_info: Option<SectionId>, + /// `.debug_abbrev.dwo` + debug_abbrev: Option<SectionId>, + /// `.debug_str.dwo` + debug_str: Option<SectionId>, + /// `.debug_types.dwo` + debug_types: Option<SectionId>, + /// `.debug_line.dwo` + debug_line: Option<SectionId>, + /// `.debug_loc.dwo` + debug_loc: Option<SectionId>, + /// `.debug_loclists.dwo` + debug_loclists: Option<SectionId>, + /// `.debug_rnglists.dwo` + debug_rnglists: Option<SectionId>, + /// `.debug_str_offsets.dwo` + debug_str_offsets: Option<SectionId>, + /// `.debug_macinfo.dwo` + debug_macinfo: Option<SectionId>, + /// `.debug_macro.dwo` + debug_macro: Option<SectionId>, +} + +/// Macro for generating helper functions which appending non-empty data to specific sections. +macro_rules! generate_append_for { + ( $( $fn_name:ident => ($name:ident, $section_name:expr) ),+ ) => { + $( + fn $fn_name(&mut self, data: &[u8]) -> Option<Contribution> { + if data.is_empty() { + return None; + } + + let id = *self.$name.get_or_insert_with(|| self.obj.add_section( + Vec::new(), + Vec::from($section_name), + SectionKind::Debug, + )); + + // FIXME: correct alignment + let offset = self.obj.append_section_data(id, data, 1); + debug!(?offset, ?data); + Some(Contribution { + offset: ContributionOffset(offset), + size: data.len().try_into().expect("data size larger than u64"), + }) + } + )+ + }; +} + +impl<'file> DwarfPackageObject<'file> { + /// Create a new `DwarfPackageObject` from an architecture and endianness. + #[tracing::instrument(level = "trace")] + pub(crate) fn new( + architecture: object::Architecture, + endianness: object::Endianness, + ) -> DwarfPackageObject<'file> { + let obj = WritableObject::new(BinaryFormat::Elf, architecture, endianness); + Self { + obj, + debug_cu_index: Default::default(), + debug_tu_index: Default::default(), + debug_info: Default::default(), + debug_abbrev: Default::default(), + debug_str: Default::default(), + debug_types: Default::default(), + debug_line: Default::default(), + debug_loc: Default::default(), + debug_loclists: Default::default(), + debug_rnglists: Default::default(), + debug_str_offsets: Default::default(), + debug_macinfo: Default::default(), + debug_macro: Default::default(), + } + } + + generate_append_for! { + append_to_debug_abbrev => (debug_abbrev, ".debug_abbrev.dwo"), + append_to_debug_cu_index => (debug_cu_index, ".debug_cu_index"), + append_to_debug_info => (debug_info, ".debug_info.dwo"), + append_to_debug_line => (debug_line, ".debug_line.dwo"), + append_to_debug_loc => (debug_loc, ".debug_loc.dwo"), + append_to_debug_loclists => (debug_loclists, ".debug_loclists.dwo"), + append_to_debug_macinfo => (debug_macinfo, ".debug_macinfo.dwo"), + append_to_debug_macro => (debug_macro, ".debug_macro.dwo"), + append_to_debug_rnglists => (debug_rnglists, ".debug_rnglists.dwo"), + append_to_debug_str => (debug_str, ".debug_str.dwo"), + append_to_debug_str_offsets => (debug_str_offsets, ".debug_str_offsets.dwo"), + append_to_debug_tu_index => (debug_tu_index, ".debug_tu_index"), + append_to_debug_types => (debug_types, ".debug_types.dwo") + } + + /// Return the DWARF package object file. + pub(crate) fn finish(self) -> WritableObject<'file> { + self.obj + } +} + +/// In-progress DWARF package being produced. +pub(crate) struct InProgressDwarfPackage<'file> { + /// Endianness of the DWARF package being created. + endian: RunTimeEndian, + + /// Object file being created. + obj: DwarfPackageObject<'file>, + /// In-progress string table being accumulated. + /// + /// Used to write final `.debug_str.dwo` and `.debug_str_offsets.dwo`. + string_table: PackageStringTable, + + /// Compilation unit index entries (offsets + sizes) being accumulated. + cu_index_entries: Vec<IndexEntry>, + /// Type unit index entries (offsets + sizes) being accumulated. + tu_index_entries: Vec<IndexEntry>, + + /// `DebugTypeSignature`s of type units and `DwoId`s of compilation units that have already + /// been added to the output package. + /// + /// Used when adding new TU index entries to de-duplicate type units (as required by the + /// specification). Also used to check that all dwarf objects referenced by executables + /// have been found. + contained_units: HashSet<DwarfObject>, +} + +impl<'file> InProgressDwarfPackage<'file> { + /// Create an object file with empty sections that will be later populated from DWARF object + /// files. + #[tracing::instrument(level = "trace")] + pub(crate) fn new( + architecture: object::Architecture, + endianness: object::Endianness, + ) -> InProgressDwarfPackage<'file> { + let endian = endianness.as_runtime_endian(); + Self { + endian, + obj: DwarfPackageObject::new(architecture, endianness), + string_table: PackageStringTable::new(), + cu_index_entries: Default::default(), + tu_index_entries: Default::default(), + contained_units: Default::default(), + } + } + + /// Returns the units contained within the DWARF package. + pub(crate) fn contained_units(&self) -> &HashSet<DwarfObject> { + &self.contained_units + } + + /// Process an input DWARF object. + /// + /// Copies relevant debug sections, compilation/type units and strings from the `input` DWARF + /// object into this DWARF package. + #[tracing::instrument(level = "trace", skip(sess, input,))] + pub(crate) fn add_input_object<'input, 'session: 'input>( + &mut self, + sess: &'session impl Session<RelocationMap>, + input: &object::File<'input>, + encoding: Encoding, + ) -> Result<()> { + // Load index sections (if they exist). + let cu_index = maybe_load_index_section::<_, gimli::DebugCuIndex<_>, _, _>( + sess, + encoding, + self.endian, + input, + )?; + let tu_index = maybe_load_index_section::<_, gimli::DebugTuIndex<_>, _, _>( + sess, + encoding, + self.endian, + input, + )?; + + let mut debug_abbrev = None; + let mut debug_line = None; + let mut debug_loc = None; + let mut debug_loclists = None; + let mut debug_macinfo = None; + let mut debug_macro = None; + let mut debug_rnglists = None; + let mut debug_str_offsets = None; + + macro_rules! update { + ($target:ident += $source:expr) => { + if let Some(other) = $source { + let contribution = $target.get_or_insert(Contribution { size: 0, ..other }); + contribution.size += other.size; + } + debug!(?$target); + }; + } + + // Iterate over sections rather than using `section_by_name` because sections can be + // repeated. + for section in input.sections() { + match section.name() { + Ok(".debug_abbrev.dwo" | ".zdebug_abbrev.dwo") => { + let data = section.compressed_data()?.decompress()?; + update!(debug_abbrev += self.obj.append_to_debug_abbrev(&data)); + } + Ok(".debug_line.dwo" | ".zdebug_line.dwo") => { + let data = section.compressed_data()?.decompress()?; + update!(debug_line += self.obj.append_to_debug_line(&data)); + } + Ok(".debug_loc.dwo" | ".zdebug_loc.dwo") => { + let data = section.compressed_data()?.decompress()?; + update!(debug_loc += self.obj.append_to_debug_loc(&data)); + } + Ok(".debug_loclists.dwo" | ".zdebug_loclists.dwo") => { + let data = section.compressed_data()?.decompress()?; + update!(debug_loclists += self.obj.append_to_debug_loclists(&data)); + } + Ok(".debug_macinfo.dwo" | ".zdebug_macinfo.dwo") => { + let data = section.compressed_data()?.decompress()?; + update!(debug_macinfo += self.obj.append_to_debug_macinfo(&data)); + } + Ok(".debug_macro.dwo" | ".zdebug_macro.dwo") => { + let data = section.compressed_data()?.decompress()?; + update!(debug_macro += self.obj.append_to_debug_macro(&data)); + } + Ok(".debug_rnglists.dwo" | ".zdebug_rnglists.dwo") => { + let data = section.compressed_data()?.decompress()?; + update!(debug_rnglists += self.obj.append_to_debug_rnglists(&data)); + } + Ok(".debug_str_offsets.dwo" | ".zdebug_str_offsets.dwo") => { + let debug_str_offsets_section = { + let data = section.compressed_data()?.decompress()?; + let data_ref = sess.alloc_owned_cow(data); + gimli::DebugStrOffsets::from(gimli::EndianSlice::new(data_ref, self.endian)) + }; + + let debug_str_section = + if let Some(section) = input.section_by_name(".debug_str.dwo") { + let data = section.compressed_data()?.decompress()?; + let data_ref = sess.alloc_owned_cow(data); + gimli::DebugStr::new(data_ref, self.endian) + } else { + return Err(Error::MissingRequiredSection(".debug_str.dwo")); + }; + + let data = self.string_table.remap_str_offsets_section( + debug_str_section, + debug_str_offsets_section, + section.size(), + self.endian, + encoding, + )?; + update!( + debug_str_offsets += self.obj.append_to_debug_str_offsets(data.slice()) + ); + } + _ => (), + } + } + + // `.debug_abbrev.dwo`'s contribution will already have been processed, but getting the + // `DwoId` of a GNU Extension compilation unit requires access to it. + let debug_abbrev_section = if let Some(section) = input.section_by_name(".debug_abbrev.dwo") + { + let data = section.compressed_data()?.decompress()?; + let data_ref = sess.alloc_owned_cow(data); + gimli::DebugAbbrev::new(data_ref, self.endian) + } else { + return Err(Error::MissingRequiredSection(".debug_abbrev.dwo")); + }; + + // Create offset adjustor functions, see comment on `create_contribution_adjustor` for + // explanation. + let mut abbrev_adjustor = create_contribution_adjustor( + cu_index.as_ref(), + tu_index.as_ref(), + gimli::SectionId::DebugAbbrev, + ); + let mut line_adjustor = create_contribution_adjustor( + cu_index.as_ref(), + tu_index.as_ref(), + gimli::SectionId::DebugLine, + ); + let mut loc_adjustor = create_contribution_adjustor( + cu_index.as_ref(), + tu_index.as_ref(), + gimli::SectionId::DebugLoc, + ); + let mut loclists_adjustor = create_contribution_adjustor( + cu_index.as_ref(), + tu_index.as_ref(), + gimli::SectionId::DebugLocLists, + ); + let mut rnglists_adjustor = create_contribution_adjustor( + cu_index.as_ref(), + tu_index.as_ref(), + gimli::SectionId::DebugRngLists, + ); + let mut str_offsets_adjustor = create_contribution_adjustor( + cu_index.as_ref(), + tu_index.as_ref(), + gimli::SectionId::DebugStrOffsets, + ); + let mut macinfo_adjustor = create_contribution_adjustor( + cu_index.as_ref(), + tu_index.as_ref(), + gimli::SectionId::DebugMacinfo, + ); + let mut macro_adjustor = create_contribution_adjustor( + cu_index.as_ref(), + tu_index.as_ref(), + gimli::SectionId::DebugMacro, + ); + + let mut seen_debug_info = false; + let mut seen_debug_types = false; + + for section in input.sections() { + let data; + let mut iter = match section.name() { + Ok(".debug_info.dwo" | ".zdebug_info.dwo") + // Report an error if a input DWARF package has multiple `.debug_info` + // sections. + if seen_debug_info && cu_index.is_some() => + { + return Err(Error::MultipleDebugInfoSection); + } + Ok(".debug_info.dwo" | ".zdebug_info.dwo") => { + data = section.compressed_data()?.decompress()?; + seen_debug_info = true; + UnitHeaderIterator::DebugInfo( + gimli::DebugInfo::new(&data, self.endian).units(), + ) + } + Ok(".debug_types.dwo" | ".zdebug_types.dwo") + // Report an error if a input DWARF package has multiple `.debug_types` + // sections. + if seen_debug_types && tu_index.is_some() => + { + return Err(Error::MultipleDebugTypesSection); + } + Ok(".debug_types.dwo" | ".zdebug_types.dwo") => { + data = section.compressed_data()?.decompress()?; + seen_debug_types = true; + UnitHeaderIterator::DebugTypes( + gimli::DebugTypes::new(&data, self.endian).units(), + ) + } + _ => continue, + }; + + while let Some(header) = iter.next().map_err(Error::ParseUnitHeader)? { + let id = match dwo_identifier_of_unit(&debug_abbrev_section, &header)? { + // Report an error if the unit doesn't have a `DwoId` or `DebugTypeSignature`. + None => { + return Err(Error::NotSplitUnit); + } + // Report an error when a duplicate compilation unit is found. + Some(id @ DwarfObject::Compilation(dwo_id)) + if self.contained_units.contains(&id) => + { + return Err(Error::DuplicateUnit(dwo_id.0)); + } + // Skip duplicate type units, these happen during proper operation of `thorin`. + Some(id @ DwarfObject::Type(type_sig)) + if self.contained_units.contains(&id) => + { + debug!(?type_sig, "skipping duplicate type unit, already seen"); + continue; + } + Some(id) => id, + }; + + let size: u64 = header + .length_including_self() + .try_into() + .expect("unit header length larger than u64"); + let offset = match header.offset() { + UnitSectionOffset::DebugInfoOffset(offset) => offset.0, + UnitSectionOffset::DebugTypesOffset(offset) => offset.0, + }; + + let data = section + .compressed_data_range( + sess, + offset.try_into().expect("offset larger than u64"), + size, + ) + .map_err(Error::DecompressData)? + .ok_or(Error::EmptyUnit(id.index()))?; + + let (debug_info, debug_types) = match (&iter, id) { + (UnitHeaderIterator::DebugTypes(_), DwarfObject::Type(_)) => { + (None, self.obj.append_to_debug_types(data)) + } + (_, DwarfObject::Compilation(_) | DwarfObject::Type(_)) => { + (self.obj.append_to_debug_info(data), None) + } + }; + + let debug_abbrev = abbrev_adjustor(id, debug_abbrev)?; + let debug_line = line_adjustor(id, debug_line)?; + let debug_loc = loc_adjustor(id, debug_loc)?; + let debug_loclists = loclists_adjustor(id, debug_loclists)?; + let debug_rnglists = rnglists_adjustor(id, debug_rnglists)?; + let debug_str_offsets = str_offsets_adjustor(id, debug_str_offsets)?; + let debug_macinfo = macinfo_adjustor(id, debug_macinfo)?; + let debug_macro = macro_adjustor(id, debug_macro)?; + + let entry = IndexEntry { + encoding, + id, + debug_info, + debug_types, + debug_abbrev, + debug_line, + debug_loc, + debug_loclists, + debug_rnglists, + debug_str_offsets, + debug_macinfo, + debug_macro, + }; + debug!(?entry); + + match id { + DwarfObject::Compilation(_) => self.cu_index_entries.push(entry), + DwarfObject::Type(_) => self.tu_index_entries.push(entry), + } + self.contained_units.insert(id); + } + } + + if !seen_debug_info { + // Report an error if no `.debug_info` section was found. + return Err(Error::MissingRequiredSection(".debug_info.dwo")); + } + + Ok(()) + } + + /// Return the DWARF package object being created, writing any final sections. + pub(crate) fn finish(self) -> Result<WritableObject<'file>> { + let Self { mut obj, string_table, cu_index_entries, tu_index_entries, .. } = self; + + // Write `.debug_str` to the object. + let _ = obj.append_to_debug_str(&string_table.finish()); + + // Write `.debug_{cu,tu}_index` sections to the object. + debug!("writing cu index"); + let cu_index_data = write_index(self.endian, &cu_index_entries)?; + let _ = obj.append_to_debug_cu_index(cu_index_data.slice()); + debug!("writing tu index"); + let tu_index_data = write_index(self.endian, &tu_index_entries)?; + let _ = obj.append_to_debug_tu_index(tu_index_data.slice()); + + Ok(obj.finish()) + } +} + +impl<'file> fmt::Debug for InProgressDwarfPackage<'file> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "InProgressDwarfPackage") + } +} diff --git a/vendor/thorin-dwp/src/relocate.rs b/vendor/thorin-dwp/src/relocate.rs new file mode 100644 index 000000000..7b1a21fb4 --- /dev/null +++ b/vendor/thorin-dwp/src/relocate.rs @@ -0,0 +1,189 @@ +//! Apply relocations to addresses and offsets during parsing, instead of requiring the data +//! to be fully relocated prior to parsing. Necessary to load object files that reference dwarf +//! objects (not just executables). Implementation derived from Gimli's `dwarfdump` example. + +use std::borrow::Cow; + +use gimli; +use hashbrown::HashMap; +use object::{Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget}; + +use crate::{Error, Result}; + +pub(crate) type RelocationMap = HashMap<usize, object::Relocation>; + +#[derive(Debug, Clone)] +pub(crate) struct Relocate<'a, R: gimli::Reader<Offset = usize>> { + pub(crate) relocations: &'a RelocationMap, + pub(crate) section: R, + pub(crate) reader: R, +} + +impl<'a, R: gimli::Reader<Offset = usize>> Relocate<'a, R> { + fn relocate(&self, offset: usize, value: u64) -> u64 { + if let Some(relocation) = self.relocations.get(&offset) { + if matches!(relocation.kind(), RelocationKind::Absolute) { + if relocation.has_implicit_addend() { + // Use the explicit addend too, because it may have the symbol value. + return value.wrapping_add(relocation.addend() as u64); + } else { + return relocation.addend() as u64; + } + } + }; + + value + } +} + +impl<'a, R: gimli::Reader<Offset = usize>> gimli::Reader for Relocate<'a, R> { + type Endian = R::Endian; + type Offset = R::Offset; + + fn read_address(&mut self, address_size: u8) -> gimli::Result<u64> { + let offset = self.reader.offset_from(&self.section); + let value = self.reader.read_address(address_size)?; + Ok(self.relocate(offset, value)) + } + + fn read_length(&mut self, format: gimli::Format) -> gimli::Result<usize> { + let offset = self.reader.offset_from(&self.section); + let value = self.reader.read_length(format)?; + <usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64)) + } + + fn read_offset(&mut self, format: gimli::Format) -> gimli::Result<usize> { + let offset = self.reader.offset_from(&self.section); + let value = self.reader.read_offset(format)?; + <usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64)) + } + + fn read_sized_offset(&mut self, size: u8) -> gimli::Result<usize> { + let offset = self.reader.offset_from(&self.section); + let value = self.reader.read_sized_offset(size)?; + <usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64)) + } + + #[inline] + fn split(&mut self, len: Self::Offset) -> gimli::Result<Self> { + let mut other = self.clone(); + other.reader.truncate(len)?; + self.reader.skip(len)?; + Ok(other) + } + + // All remaining methods simply delegate to `self.reader`. + + #[inline] + fn endian(&self) -> Self::Endian { + self.reader.endian() + } + + #[inline] + fn len(&self) -> Self::Offset { + self.reader.len() + } + + #[inline] + fn empty(&mut self) { + self.reader.empty() + } + + #[inline] + fn truncate(&mut self, len: Self::Offset) -> gimli::Result<()> { + self.reader.truncate(len) + } + + #[inline] + fn offset_from(&self, base: &Self) -> Self::Offset { + self.reader.offset_from(&base.reader) + } + + #[inline] + fn offset_id(&self) -> gimli::ReaderOffsetId { + self.reader.offset_id() + } + + #[inline] + fn lookup_offset_id(&self, id: gimli::ReaderOffsetId) -> Option<Self::Offset> { + self.reader.lookup_offset_id(id) + } + + #[inline] + fn find(&self, byte: u8) -> gimli::Result<Self::Offset> { + self.reader.find(byte) + } + + #[inline] + fn skip(&mut self, len: Self::Offset) -> gimli::Result<()> { + self.reader.skip(len) + } + + #[inline] + fn to_slice(&self) -> gimli::Result<Cow<'_, [u8]>> { + self.reader.to_slice() + } + + #[inline] + fn to_string(&self) -> gimli::Result<Cow<'_, str>> { + self.reader.to_string() + } + + #[inline] + fn to_string_lossy(&self) -> gimli::Result<Cow<'_, str>> { + self.reader.to_string_lossy() + } + + #[inline] + fn read_slice(&mut self, buf: &mut [u8]) -> gimli::Result<()> { + self.reader.read_slice(buf) + } +} + +pub(crate) fn add_relocations( + relocations: &mut RelocationMap, + file: &object::File<'_>, + section: &object::Section<'_, '_>, +) -> Result<()> { + for (offset64, mut relocation) in section.relocations() { + let offset = offset64 as usize; + if offset as u64 != offset64 { + continue; + } + + let offset = offset as usize; + if matches!(relocation.kind(), RelocationKind::Absolute) { + if let RelocationTarget::Symbol(symbol_idx) = relocation.target() { + match file.symbol_by_index(symbol_idx) { + Ok(symbol) => { + let addend = symbol.address().wrapping_add(relocation.addend() as u64); + relocation.set_addend(addend as i64); + } + Err(_) => { + return Err(Error::RelocationWithInvalidSymbol( + section + .name() + .map_err(|e| Error::NamelessSection(e, offset))? + .to_string(), + offset, + )); + } + } + } + + if relocations.insert(offset, relocation).is_some() { + return Err(Error::MultipleRelocations( + section.name().map_err(|e| Error::NamelessSection(e, offset))?.to_string(), + offset, + )); + } + } else { + return Err(Error::UnsupportedRelocation( + section.name().map_err(|e| Error::NamelessSection(e, offset))?.to_string(), + offset, + )); + } + } + + Ok(()) +} diff --git a/vendor/thorin-dwp/src/strings.rs b/vendor/thorin-dwp/src/strings.rs new file mode 100644 index 000000000..4fa2dfbdd --- /dev/null +++ b/vendor/thorin-dwp/src/strings.rs @@ -0,0 +1,143 @@ +use gimli::{ + write::{EndianVec, Writer}, + DebugStrOffsetsBase, DebugStrOffsetsIndex, DwarfFileType, Encoding, EndianSlice, Format, + Section, +}; +use hashbrown::HashMap; +use tracing::debug; + +use crate::{ + error::{Error, Result}, + ext::PackageFormatExt, +}; + +/// New-type'd offset into `.debug_str` section. +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub(crate) struct PackageStringOffset(usize); + +/// DWARF packages need to merge the `.debug_str` sections of input DWARF objects. +/// `.debug_str_offsets` sections then need to be rebuilt with offsets into the new merged +/// `.debug_str` section and then concatenated (indices into each dwarf object's offset list will +/// therefore still refer to the same string). +/// +/// Gimli's `StringTable` produces a `.debug_str` section with a single `.debug_str_offsets` +/// section, but `PackageStringTable` accumulates a single `.debug_str` section and can be used to +/// produce multiple `.debug_str_offsets` sections (which will be concatenated) which all offset +/// into the same `.debug_str`. +pub(crate) struct PackageStringTable { + data: Vec<u8>, + strings: HashMap<Vec<u8>, PackageStringOffset>, +} + +impl PackageStringTable { + /// Create a new `PackageStringTable` with a given endianity. + pub(crate) fn new() -> Self { + Self { data: Vec::new(), strings: HashMap::new() } + } + + /// Insert a string into the string table and return its offset in the table. If the string is + /// already in the table, returns its offset. + pub(crate) fn get_or_insert(&mut self, bytes: &[u8]) -> PackageStringOffset { + debug_assert!(!bytes.contains(&0)); + if let Some(offset) = self.strings.get(bytes) { + return *offset; + } + + // Keep track of the offset for this string, it might be referenced by the next compilation + // unit too. + let offset = PackageStringOffset(self.data.len()); + self.strings.insert(bytes.into(), offset); + + // Insert into the string table. + self.data.extend_from_slice(bytes); + self.data.push(0); + + offset + } + + /// Adds strings from input `.debug_str_offsets` and `.debug_str` into the string table, returns + /// data for a equivalent `.debug_str_offsets` section with offsets pointing into the new + /// `.debug_str` section. + pub(crate) fn remap_str_offsets_section<E: gimli::Endianity>( + &mut self, + debug_str: gimli::DebugStr<EndianSlice<E>>, + debug_str_offsets: gimli::DebugStrOffsets<EndianSlice<E>>, + section_size: u64, + endian: E, + encoding: Encoding, + ) -> Result<EndianVec<E>> { + let entry_size = match encoding.format { + Format::Dwarf32 => 4, + Format::Dwarf64 => 8, + }; + + // Reduce the number of allocations needed. + self.data.reserve(debug_str.reader().len()); + + let mut data = EndianVec::new(endian); + + // `DebugStrOffsetsBase` knows to skip past the header with DWARF 5. + let base: gimli::DebugStrOffsetsBase<usize> = + DebugStrOffsetsBase::default_for_encoding_and_file(encoding, DwarfFileType::Dwo); + + if encoding.is_std_dwarf_package_format() { + match encoding.format { + Format::Dwarf32 => { + // Unit length (4 bytes): size of the offsets section without this + // header (8 bytes total). + data.write_u32( + (section_size - 8) + .try_into() + .expect("section size w/out header larger than u32"), + )?; + } + Format::Dwarf64 => { + // Unit length (4 bytes then 8 bytes): size of the offsets section without + // this header (16 bytes total). + data.write_u32(u32::MAX)?; + data.write_u64(section_size - 16)?; + } + }; + // Version (2 bytes): DWARF 5 + data.write_u16(5)?; + // Reserved padding (2 bytes) + data.write_u16(0)?; + } + debug!(?base); + + let base_offset: u64 = base.0.try_into().expect("base offset larger than u64"); + let num_elements = (section_size - base_offset) / entry_size; + debug!(?section_size, ?base_offset, ?num_elements); + + for i in 0..num_elements { + let dwo_index = DebugStrOffsetsIndex(i as usize); + let dwo_offset = debug_str_offsets + .get_str_offset(encoding.format, base, dwo_index) + .map_err(|e| Error::OffsetAtIndex(e, i))?; + let dwo_str = + debug_str.get_str(dwo_offset).map_err(|e| Error::StrAtOffset(e, dwo_offset.0))?; + + let dwp_offset = self.get_or_insert(&dwo_str); + + match encoding.format { + Format::Dwarf32 => { + let dwp_offset = + dwp_offset.0.try_into().expect("string offset larger than u32"); + data.write_u32(dwp_offset)?; + } + Format::Dwarf64 => { + let dwp_offset = + dwp_offset.0.try_into().expect("string offset larger than u64"); + data.write_u64(dwp_offset)?; + } + } + } + + Ok(data) + } + + /// Returns the accumulated `.debug_str` section data + pub(crate) fn finish(self) -> Vec<u8> { + self.data + } +} |