summaryrefslogtreecommitdiffstats
path: root/vendor/thorin-dwp/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/thorin-dwp/src
parentInitial commit. (diff)
downloadrustc-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.rs294
-rw-r--r--vendor/thorin-dwp/src/ext.rs144
-rw-r--r--vendor/thorin-dwp/src/index.rs333
-rw-r--r--vendor/thorin-dwp/src/lib.rs310
-rw-r--r--vendor/thorin-dwp/src/package.rs728
-rw-r--r--vendor/thorin-dwp/src/relocate.rs189
-rw-r--r--vendor/thorin-dwp/src/strings.rs143
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
+ }
+}