use thiserror::Error;
#[cfg(feature = "convert")]
#[derive(Error)]
#[error("{0}")]
pub struct ParserError(#[source] ParserErrorKind);
#[cfg(feature = "convert")]
impl std::fmt::Debug for ParserError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "ParserError({})", self.0)
}
}
#[cfg(feature = "convert")]
#[derive(Error)]
enum ParserErrorKind {
#[error("{0}")]
Object(#[source] object::Error),
#[error("{0}")]
Dwarf(#[source] gimli::Error),
}
#[cfg(feature = "convert")]
impl std::fmt::Debug for ParserErrorKind {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "ParserSource({self})")
}
}
#[cfg(feature = "convert")]
impl ParserError {
pub(crate) const fn object(source: object::Error) -> Self {
Self(ParserErrorKind::Object(source))
}
pub(crate) const fn dwarf(source: gimli::Error) -> Self {
Self(ParserErrorKind::Dwarf(source))
}
}
#[cfg(feature = "convert")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ElfInputKind {
Image,
Debug,
Symbols,
Supplementary,
Dwp,
Dwo,
EmbeddedDebugData,
}
#[cfg(feature = "convert")]
impl std::fmt::Display for ElfInputKind {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(match self {
Self::Image => "ELF image",
Self::Debug => "ELF debug file",
Self::Symbols => "ELF symbol file",
Self::Supplementary => "supplementary ELF debug file",
Self::Dwp => "DWP file",
Self::Dwo => "DWO file",
Self::EmbeddedDebugData => ".gnu_debugdata ELF",
})
}
}
#[cfg(feature = "convert")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum CompanionMismatch {
Architecture,
BuildId,
}
#[cfg(feature = "convert")]
impl std::fmt::Display for CompanionMismatch {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str(match self {
Self::Architecture => "architecture",
Self::BuildId => "build ID",
})
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum Error {
#[error("unexpected end of input at offset {offset}: need {needed} bytes, have {remaining}")]
UnexpectedEof {
offset: usize,
needed: usize,
remaining: usize,
},
#[error("integer overflow while computing {0}")]
Overflow(&'static str),
#[error("invalid {field} value {value}; maximum is {max}")]
OutOfRange {
field: &'static str,
value: u64,
max: u64,
},
#[error("function index {index} is out of bounds for {count} functions")]
FunctionIndexOutOfBounds {
index: usize,
count: usize,
},
#[error("invalid GSYM magic 0x{0:08x}")]
InvalidMagic(u32),
#[error("unsupported GSYM version {0}")]
UnsupportedVersion(u16),
#[error("invalid address offset size {size} for GSYM v{version}")]
InvalidAddressOffsetSize {
version: u16,
size: u8,
},
#[error("unsupported string table encoding {0}")]
UnsupportedStringTableEncoding(u8),
#[error("invalid UUID size {0}; GSYM v1 supports at most 20 bytes")]
InvalidUuidSize(usize),
#[error("GSYM v1 build identifier is {size} bytes; maximum is 20 bytes")]
V1BuildIdTooLong {
size: usize,
},
#[error("missing required GSYM section type {0}")]
MissingSection(u32),
#[error("duplicate GSYM section type {0}")]
DuplicateSection(u32),
#[error("GSYM section type {section_type} has zero size")]
ZeroSizedSection {
section_type: u32,
},
#[error(
"GSYM section type {section_type} is outside the input: offset={offset}, size={size}, input={input_len}"
)]
SectionOutOfBounds {
section_type: u32,
offset: u64,
size: u64,
input_len: usize,
},
#[error("invalid offset {offset} for input of {input_len} bytes")]
InvalidOffset {
offset: u64,
input_len: usize,
},
#[error("invalid alignment {0}")]
InvalidAlignment(usize),
#[error("malformed unsigned LEB128 at offset {offset}: {reason}")]
MalformedUleb {
offset: usize,
reason: &'static str,
},
#[error("malformed signed LEB128 at offset {offset}: {reason}")]
MalformedSleb {
offset: usize,
reason: &'static str,
},
#[error("unsupported FunctionInfo type {0}")]
UnsupportedInfoType(u32),
#[error("FunctionInfo name offset must not be zero")]
ZeroNameOffset,
#[error("invalid GSYM data: {0}")]
InvalidFormat(&'static str),
#[error("invalid GSYM model: {0}")]
InvalidModel(&'static str),
#[error("{context} value {value} exceeds the supported limit of {limit}")]
Limit {
context: &'static str,
value: u64,
limit: u64,
},
#[error("malformed {context}: {detail}")]
Malformed {
context: &'static str,
detail: Box<str>,
},
#[cfg(feature = "convert")]
#[error("failed to parse {input}: {source}")]
ElfParse {
input: ElfInputKind,
#[source]
source: ParserError,
},
#[cfg(feature = "convert")]
#[error("malformed DWARF: {source}")]
Dwarf {
#[source]
source: ParserError,
},
#[cfg(feature = "convert")]
#[error("{input} is not an ELF file")]
NotElf {
input: ElfInputKind,
},
#[cfg(feature = "convert")]
#[error("{input} {mismatch} does not match the linked image")]
CompanionMismatch {
input: ElfInputKind,
mismatch: CompanionMismatch,
},
#[error("failed to {operation} {}", path.display())]
IoAtPath {
operation: &'static str,
path: std::path::PathBuf,
#[source]
source: std::io::Error,
},
#[error("GSYM v1 limit exceeded for {field}: {value}; write version 2 explicitly")]
V1LimitExceeded {
field: &'static str,
value: u64,
},
#[error(transparent)]
Io(#[from] std::io::Error),
}
pub type Result<T> = std::result::Result<T, Error>;
impl Error {
pub(crate) fn malformed(context: &'static str, detail: impl Into<Box<str>>) -> Self {
Self::Malformed {
context,
detail: detail.into(),
}
}
}
#[cfg(feature = "convert")]
impl From<gimli::Error> for Error {
fn from(error: gimli::Error) -> Self {
Self::Dwarf {
source: ParserError::dwarf(error),
}
}
}