thorin-dwp 0.11.0

Library for building DWARF packages from input DWARF objects and packages
Documentation
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 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,
    ContributionOutOfBounds(crate::index::Contribution, usize),
    /// 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,
    /// A DW_LLE value was unrecognized.
    UnsupportedLocListsEntry(u8),
    /// `.debug_info.dwo` compilation unit is malformed or truncated during GC.
    MalformedDebugInfo,
    /// An abbreviation form encountered during GC byte-level rewriting is not supported.
    UnsupportedForm(u16),
    /// A section-absolute reference was found where not expected.
    UnexpectedSectionAbsoluteReference,
    /// GC input object or executable added without prior `preprocess_gc_executable` call.
    GcNotInitialized,
    /// A `.dwo` is referenced by multiple executables that supply DWARF4 `.debug_ranges`.
    ///
    /// DWARF4 range lists hold raw addresses that live in the linked executable.
    /// When several executables reference the same `.dwo`, each has its own (independently
    /// tombstoned) `.debug_ranges`, but the GC currently only supports consulting one
    /// of them. Proceeding could prune a range list still live in a different executable.
    GcSharedDwarf4Ranges(crate::package::DwoId),

    /// 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::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::ContributionOutOfBounds(..) => 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::UnsupportedLocListsEntry(_) => None,
            Error::MalformedDebugInfo => None,
            Error::UnsupportedForm(_) => None,
            Error::UnexpectedSectionAbsoluteReference => None,
            Error::GcNotInitialized => None,
            Error::GcSharedDwarf4Ranges(_) => 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::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::ContributionOutOfBounds(contribution, section_len) => {
                write!(
                    f,
                    "Index contribution at offset 0x{:08x} with size 0x{:x} extends beyond section \
                     of length 0x{:x}",
                    contribution.offset.0, contribution.size, section_len
                )
            }
            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::UnsupportedLocListsEntry(s) => {
                write!(f, "Unsupported DW_LLE value: {}", s)
            }
            Error::MalformedDebugInfo => write!(f, "Malformed `.debug_info.dwo` compilation unit"),
            Error::UnsupportedForm(form) => {
                write!(f, "Unsupported DWARF form 0x{:02x} during GC rewrite", form)
            }
            Error::UnexpectedSectionAbsoluteReference => {
                write!(f, "A section-absolute reference was found in a .dwo file")
            }
            Error::GcNotInitialized => {
                write!(f, "GC was requested but no executables were preprocessed")
            }
            Error::GcSharedDwarf4Ranges(dwo_id) => {
                write!(
                    f,
                    "DWARF4 .debug_ranges for {dwo_id:?} is supplied by multiple executables; \
                     cannot safely GC"
                )
            }
            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)
    }
}