use alloc::string::String;
use core::fmt;
use crate::object::Width;
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum LinkError {
DuplicateSymbol {
name: String,
},
UndefinedSymbol {
name: String,
object: String,
},
UndefinedEntry {
name: String,
},
InvalidSection {
object: String,
section: String,
},
RelocationOutOfRange {
object: String,
section: String,
offset: u64,
},
RelocationOverflow {
target: String,
width: Width,
},
LayoutOverflow,
}
impl fmt::Display for LinkError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LinkError::DuplicateSymbol { name } => {
write!(f, "symbol `{name}` is defined by more than one object")
}
LinkError::UndefinedSymbol { name, object } => {
write!(
f,
"object `{object}` references symbol `{name}`, which no object defines"
)
}
LinkError::UndefinedEntry { name } => {
write!(f, "entry point `{name}` is not defined by any object")
}
LinkError::InvalidSection { object, section } => {
write!(
f,
"object `{object}` names section `{section}`, which it never created"
)
}
LinkError::RelocationOutOfRange {
object,
section,
offset,
} => {
write!(
f,
"relocation at offset {offset} in section `{section}` of object \
`{object}` runs past the end of the section"
)
}
LinkError::RelocationOverflow { target, width } => {
write!(
f,
"the resolved address of `{target}` does not fit in {} bytes",
width.bytes()
)
}
LinkError::LayoutOverflow => {
f.write_str("the laid-out image would exceed the 64-bit address space")
}
}
}
}
impl core::error::Error for LinkError {}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
reason = "tests assert on specific outcomes; a wrong outcome should fail the test loudly"
)]
mod tests {
use super::LinkError;
use crate::object::Width;
use alloc::string::ToString;
#[test]
fn test_each_variant_renders_a_distinct_message() {
let messages = [
LinkError::DuplicateSymbol {
name: "main".into(),
}
.to_string(),
LinkError::UndefinedSymbol {
name: "puts".into(),
object: "a".into(),
}
.to_string(),
LinkError::UndefinedEntry {
name: "_start".into(),
}
.to_string(),
LinkError::InvalidSection {
object: "a".into(),
section: ".txet".into(),
}
.to_string(),
LinkError::RelocationOutOfRange {
object: "a".into(),
section: ".data".into(),
offset: 12,
}
.to_string(),
LinkError::RelocationOverflow {
target: "big".into(),
width: Width::U32,
}
.to_string(),
LinkError::LayoutOverflow.to_string(),
];
for (i, a) in messages.iter().enumerate() {
for b in &messages[i + 1..] {
assert_ne!(a, b);
}
}
assert!(messages[0].contains("main"));
assert!(messages[5].contains("4 bytes"));
}
#[test]
fn test_error_is_a_std_error() {
fn assert_error<E: core::error::Error>(_: &E) {}
assert_error(&LinkError::LayoutOverflow);
}
}