1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Fatal error type.
//!
//! Only errors that stop the link are returned as [`Error`]. Anything the link
//! can continue past is a [`crate::diag::Diagnostic`] instead.
//!
//! Parsing code must never panic on malformed input: bounds-check every
//! offset, use checked arithmetic, and return [`Error::Malformed`].
use std::fmt;
use std::path::PathBuf;
/// Result alias used throughout qld.
pub type Result<T, E = Error> = std::result::Result<T, E>;
/// A fatal linker error.
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
/// An input file could not be parsed.
Malformed {
/// The file the problem was found in.
file: PathBuf,
/// Archive member name, when the file is an archive.
member: Option<String>,
/// Byte offset of the problem within the file.
offset: u64,
/// What was wrong, phrased as a noun ("section header table").
what: String,
},
/// A linker script could not be parsed or evaluated. Renders as
/// `file:line:column: message`, the form GNU tools use.
Script(Box<crate::script::ScriptError>),
/// An I/O operation failed.
Io {
/// The path being operated on, if any.
path: Option<PathBuf>,
/// The underlying error.
source: std::io::Error,
},
/// A command-line option was not recognized, or its value was invalid.
Option(String),
/// A library or linker script named on the command line was not found in
/// any search directory. The message is GNU-style: `cannot find -lfoo`.
NotFound(String),
/// A hard limit was exceeded (more than 2^32 input files, symbols, …).
Limit(String),
/// An LTO plugin could not be used, or an IR input has no plugin to
/// compile it.
Plugin(String),
/// An internal invariant was violated: a bug in qld, such as a backend
/// handing a pass an inconsistent layout. Never caused by input files.
Internal(String),
/// The link failed because of errors already reported to the diagnostic
/// sink (for example undefined symbols).
Reported {
/// How many errors were reported.
errors: usize,
},
/// A feature that qld intends to support is not implemented yet.
///
/// Every use of this must name the roadmap milestone that will remove it.
Unimplemented(String),
/// The link was cancelled through
/// [`LinkOptions::cancel`](crate::args::LinkOptions::cancel).
Cancelled,
}
impl Error {
/// Creates an [`Error::Io`] that names the path involved.
pub fn io(path: impl Into<PathBuf>, source: std::io::Error) -> Self {
Self::Io {
path: Some(path.into()),
source,
}
}
/// Creates an [`Error::Malformed`] for a file with no archive member.
pub fn malformed(path: impl Into<PathBuf>, offset: u64, what: impl Into<String>) -> Self {
Self::Malformed {
file: path.into(),
member: None,
offset,
what: what.into(),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Malformed {
file,
member,
offset,
what,
} => {
write!(f, "{}", file.display())?;
if let Some(member) = member {
write!(f, "({member})")?;
}
write!(f, ": malformed {what} at offset {offset:#x}")
}
Self::Io {
path: Some(path),
source,
} => write!(f, "{}: {source}", path.display()),
Self::Io { path: None, source } => write!(f, "{source}"),
Self::Script(error) => write!(f, "{error}"),
Self::Option(message) | Self::NotFound(message) => write!(f, "{message}"),
Self::Limit(message) => write!(f, "limit exceeded: {message}"),
Self::Plugin(message) => write!(f, "{message}"),
Self::Internal(message) => write!(f, "internal error: {message}"),
Self::Reported { errors } if *errors == 1 => write!(f, "1 error"),
Self::Reported { errors } => write!(f, "{errors} errors"),
Self::Unimplemented(what) => write!(f, "not implemented yet: {what}"),
Self::Cancelled => f.write_str("link cancelled"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io { source, .. } => Some(source),
Self::Script(error) => Some(error.as_ref()),
_ => None,
}
}
}
impl From<std::io::Error> for Error {
fn from(source: std::io::Error) -> Self {
Self::Io { path: None, source }
}
}