object_lang/error.rs
1//! Errors: what is wrong with an object model, why a file could not be written, and why
2//! bytes could not be read.
3
4use core::fmt;
5
6use crate::model::{SectionId, SymbolId};
7
8/// What is wrong with one section. Reported inside [`ModelError::Section`].
9///
10/// # Examples
11///
12/// ```
13/// use object_lang::SectionProblem;
14///
15/// assert_eq!(SectionProblem::BadAlignment.to_string(), "alignment is not a power of two");
16/// ```
17#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
18#[non_exhaustive]
19pub enum SectionProblem {
20 /// The name contains a NUL byte, which no format can store.
21 NameContainsNul,
22 /// The alignment is zero or not a power of two.
23 BadAlignment,
24 /// The flags are not allowed for the section's kind (see
25 /// [`SectionKind`](crate::SectionKind)).
26 FlagsDoNotMatchKind,
27 /// [`MERGE`](crate::SectionFlags::MERGE) is set but the entry size is zero.
28 MergeWithoutEntrySize,
29 /// A [`Bss`](crate::SectionKind::Bss) section has bytes, or another kind has a BSS
30 /// size.
31 ContentsDoNotMatchKind,
32 /// The address is not a multiple of the alignment.
33 MisalignedAddress,
34 /// The address plus the size overflows the 64-bit address space.
35 AddressOverflow,
36}
37
38impl fmt::Display for SectionProblem {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 f.write_str(match self {
41 SectionProblem::NameContainsNul => "name contains a NUL byte",
42 SectionProblem::BadAlignment => "alignment is not a power of two",
43 SectionProblem::FlagsDoNotMatchKind => "flags are not allowed for the section's kind",
44 SectionProblem::MergeWithoutEntrySize => "mergeable section has an entry size of zero",
45 SectionProblem::ContentsDoNotMatchKind => {
46 "contents do not match the kind (bytes in a BSS section, or a BSS size on another kind)"
47 }
48 SectionProblem::MisalignedAddress => "address is not a multiple of the alignment",
49 SectionProblem::AddressOverflow => "address plus size overflows the address space",
50 })
51 }
52}
53
54/// What is wrong with one symbol. Reported inside [`ModelError::Symbol`].
55///
56/// # Examples
57///
58/// ```
59/// use object_lang::SymbolProblem;
60///
61/// assert_eq!(SymbolProblem::LocalUndefined.to_string(), "a local symbol must be defined");
62/// ```
63#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
64#[non_exhaustive]
65pub enum SymbolProblem {
66 /// The name contains a NUL byte.
67 NameContainsNul,
68 /// The symbol is defined in a section the object does not have.
69 UnknownSection,
70 /// The symbol (its value, or value plus size) lies outside its section.
71 OutOfSection,
72 /// A local symbol is undefined or common: nothing outside the object could define it.
73 LocalUndefined,
74 /// A [`File`](crate::SymbolKind::File) symbol is not local and absolute with value
75 /// and size zero.
76 BadFileSymbol,
77 /// A [`Common`](crate::SymbolSection::Common) symbol is not global, or its alignment
78 /// (its value) is not a power of two.
79 BadCommonSymbol,
80 /// A [`Common`](crate::SymbolSection::Common) symbol in an executable: an
81 /// executable's storage is already allocated.
82 CommonInExecutable,
83 /// A non-weak undefined symbol in an executable: nothing is left to define it.
84 UndefinedInExecutable,
85 /// A [`Tls`](crate::SymbolKind::Tls) symbol defined in a section without the
86 /// [`TLS`](crate::SectionFlags::TLS) flag.
87 TlsOutsideTlsSection,
88}
89
90impl fmt::Display for SymbolProblem {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 f.write_str(match self {
93 SymbolProblem::NameContainsNul => "name contains a NUL byte",
94 SymbolProblem::UnknownSection => "defined in a section the object does not have",
95 SymbolProblem::OutOfSection => "lies outside its section",
96 SymbolProblem::LocalUndefined => "a local symbol must be defined",
97 SymbolProblem::BadFileSymbol => {
98 "a file symbol must be local and absolute, with value and size zero"
99 }
100 SymbolProblem::BadCommonSymbol => {
101 "a common symbol must be global, with a power-of-two alignment as its value"
102 }
103 SymbolProblem::CommonInExecutable => "an executable cannot hold a common symbol",
104 SymbolProblem::UndefinedInExecutable => {
105 "an executable cannot hold a non-weak undefined symbol"
106 }
107 SymbolProblem::TlsOutsideTlsSection => {
108 "a thread-local symbol is defined outside a thread-local section"
109 }
110 })
111 }
112}
113
114/// What is wrong with one relocation. Reported inside [`ModelError::Relocation`].
115///
116/// # Examples
117///
118/// ```
119/// use object_lang::RelocationProblem;
120///
121/// assert_eq!(RelocationProblem::OutOfSection.to_string(), "patches bytes outside its section");
122/// ```
123#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
124#[non_exhaustive]
125pub enum RelocationProblem {
126 /// The relocation kind belongs to another architecture.
127 WrongArchitecture,
128 /// The patched bytes run past the end of the section.
129 OutOfSection,
130 /// An instruction relocation whose offset is not a multiple of 4.
131 Misaligned,
132 /// The target symbol or section does not exist.
133 UnknownTarget,
134 /// The section is [`Bss`](crate::SectionKind::Bss): there are no bytes to patch.
135 InUninitializedSection,
136 /// The object is an executable, which is fully linked and carries no relocations.
137 InExecutable,
138}
139
140impl fmt::Display for RelocationProblem {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 f.write_str(match self {
143 RelocationProblem::WrongArchitecture => "kind belongs to another architecture",
144 RelocationProblem::OutOfSection => "patches bytes outside its section",
145 RelocationProblem::Misaligned => "instruction relocation is not 4-byte aligned",
146 RelocationProblem::UnknownTarget => "target symbol or section does not exist",
147 RelocationProblem::InUninitializedSection => {
148 "patches a BSS section, which has no bytes"
149 }
150 RelocationProblem::InExecutable => "executables carry no relocations",
151 })
152 }
153}
154
155/// A rule of the object model that an [`Object`](crate::Object) breaks, from
156/// [`Object::validate`](crate::Object::validate), from the builders on
157/// [`Section`](crate::Section), or from a reader whose input decodes to an invalid model.
158///
159/// The enum is `#[non_exhaustive]`: match it with a wildcard arm.
160///
161/// # Examples
162///
163/// ```
164/// use object_lang::{Architecture, ModelError, Object, Symbol, SymbolProblem, Binding};
165///
166/// let mut obj = Object::relocatable(Architecture::X86_64);
167/// let sym = obj.add_symbol(Symbol::undefined("x").with_binding(Binding::Local));
168/// assert_eq!(
169/// obj.validate(),
170/// Err(ModelError::Symbol { symbol: sym, problem: SymbolProblem::LocalUndefined }),
171/// );
172/// ```
173#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
174#[non_exhaustive]
175pub enum ModelError {
176 /// A section breaks a rule.
177 Section {
178 /// The section.
179 section: SectionId,
180 /// The rule it breaks.
181 problem: SectionProblem,
182 },
183 /// A symbol breaks a rule.
184 Symbol {
185 /// The symbol.
186 symbol: SymbolId,
187 /// The rule it breaks.
188 problem: SymbolProblem,
189 },
190 /// A relocation breaks a rule.
191 Relocation {
192 /// The section the relocation patches.
193 section: SectionId,
194 /// The relocation's position in that section's
195 /// [`relocations`](crate::Section::relocations).
196 index: usize,
197 /// The rule it breaks.
198 problem: RelocationProblem,
199 },
200 /// Two loadable sections of an executable overlap in memory.
201 SectionsOverlap {
202 /// The section at the lower address.
203 first: SectionId,
204 /// The section that starts inside it.
205 second: SectionId,
206 },
207 /// An executable's entry point is not inside an executable section.
208 EntryNotExecutable {
209 /// The entry address.
210 entry: u64,
211 },
212 /// A section id that the object does not have, passed to
213 /// [`Object::add_relocation`](crate::Object::add_relocation).
214 UnknownSection {
215 /// The id.
216 section: SectionId,
217 },
218 /// An alignment that is zero or not a power of two, passed to
219 /// [`Section::append`](crate::Section::append) or
220 /// [`Section::reserve`](crate::Section::reserve).
221 InvalidAlignment {
222 /// The alignment.
223 align: u64,
224 },
225 /// [`Section::append`](crate::Section::append) on a
226 /// [`Bss`](crate::SectionKind::Bss) section, which holds no bytes; use
227 /// [`reserve`](crate::Section::reserve).
228 AppendToBss,
229 /// A section would grow past the 64-bit size range or past available memory.
230 SizeOverflow,
231 /// The object holds more sections or symbols than ids can number (2³¹ − 1).
232 TooManyItems,
233}
234
235impl fmt::Display for ModelError {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 match self {
238 ModelError::Section { section, problem } => {
239 write!(f, "section #{}: {problem}", section.index())
240 }
241 ModelError::Symbol { symbol, problem } => {
242 write!(f, "symbol #{}: {problem}", symbol.index())
243 }
244 ModelError::Relocation {
245 section,
246 index,
247 problem,
248 } => write!(
249 f,
250 "relocation #{index} of section #{}: {problem}",
251 section.index()
252 ),
253 ModelError::SectionsOverlap { first, second } => write!(
254 f,
255 "sections #{} and #{} overlap in memory",
256 first.index(),
257 second.index()
258 ),
259 ModelError::EntryNotExecutable { entry } => {
260 write!(
261 f,
262 "entry point {entry:#x} is not inside an executable section"
263 )
264 }
265 ModelError::UnknownSection { section } => {
266 write!(f, "the object has no section #{}", section.index())
267 }
268 ModelError::InvalidAlignment { align } => {
269 write!(f, "alignment {align} is not a power of two")
270 }
271 ModelError::AppendToBss => {
272 f.write_str("cannot append bytes to a BSS section; reserve space instead")
273 }
274 ModelError::SizeOverflow => f.write_str("section size overflows"),
275 ModelError::TooManyItems => f.write_str("too many sections or symbols to number"),
276 }
277 }
278}
279
280impl core::error::Error for ModelError {}
281
282/// Why an object could not be written.
283///
284/// The enum is `#[non_exhaustive]`: match it with a wildcard arm.
285///
286/// # Examples
287///
288/// ```
289/// use object_lang::{Architecture, Object, Section, SectionKind, WriteError};
290///
291/// let mut obj = Object::relocatable(Architecture::X86_64);
292/// let note = obj.add_section(Section::new(".note.GNU-stack", SectionKind::Other));
293/// assert_eq!(
294/// object_lang::elf::write(&obj),
295/// Err(WriteError::ReservedSectionName { section: note }),
296/// );
297/// ```
298#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
299#[non_exhaustive]
300pub enum WriteError {
301 /// The object breaks a rule of the model.
302 Invalid(ModelError),
303 /// A section uses a name the writer generates itself (ELF: `.note.GNU-stack` in a
304 /// relocatable object, which records [`executable_stack`](crate::Object::executable_stack)).
305 ReservedSectionName {
306 /// The section.
307 section: SectionId,
308 },
309 /// Two loadable sections of an executable with different permissions, or a BSS
310 /// section followed by initialized data, share a page. Each permission change must
311 /// start on a fresh [page](crate::Architecture::page_size).
312 SegmentsSharePage {
313 /// The last section of the earlier segment.
314 first: SectionId,
315 /// The first section of the later one.
316 second: SectionId,
317 },
318 /// An executable's lowest section is too close to address zero to map the file
319 /// headers on the pages below it.
320 NoRoomForHeaders,
321 /// The object uses something this writer does not support yet; the message says
322 /// what.
323 Unsupported {
324 /// What is unsupported.
325 what: &'static str,
326 },
327 /// The file would exceed the format's size limits or the address space.
328 TooLarge,
329}
330
331impl fmt::Display for WriteError {
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333 match self {
334 WriteError::Invalid(err) => write!(f, "invalid object: {err}"),
335 WriteError::ReservedSectionName { section } => write!(
336 f,
337 "section #{} uses a name the writer reserves for itself",
338 section.index()
339 ),
340 WriteError::SegmentsSharePage { first, second } => write!(
341 f,
342 "sections #{} and #{} need different page permissions but share a page",
343 first.index(),
344 second.index()
345 ),
346 WriteError::NoRoomForHeaders => f.write_str(
347 "the lowest section is too close to address zero to map the headers below it",
348 ),
349 WriteError::Unsupported { what } => write!(f, "unsupported: {what}"),
350 WriteError::TooLarge => f.write_str("the file would exceed the format's size limits"),
351 }
352 }
353}
354
355impl core::error::Error for WriteError {
356 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
357 match self {
358 WriteError::Invalid(err) => Some(err),
359 _ => None,
360 }
361 }
362}
363
364impl From<ModelError> for WriteError {
365 fn from(err: ModelError) -> Self {
366 WriteError::Invalid(err)
367 }
368}
369
370/// Why bytes could not be read as an object.
371///
372/// Readers are strict: anything the model cannot represent exactly, or that a careful
373/// writer would never produce, is refused rather than guessed at. The enum is
374/// `#[non_exhaustive]`: match it with a wildcard arm.
375///
376/// # Examples
377///
378/// ```
379/// use object_lang::ReadError;
380///
381/// assert_eq!(object_lang::elf::read(b"MZ\x90\x00"), Err(ReadError::Truncated { what: "file header" }));
382/// assert_eq!(object_lang::elf::read(&[0u8; 64]), Err(ReadError::BadMagic));
383/// ```
384#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
385#[non_exhaustive]
386pub enum ReadError {
387 /// The bytes do not start with the format's magic number.
388 BadMagic,
389 /// A structure runs past the end of the input.
390 Truncated {
391 /// The structure.
392 what: &'static str,
393 },
394 /// A field holds a value the format does not allow, or structures contradict each
395 /// other.
396 Malformed {
397 /// What is wrong.
398 what: &'static str,
399 },
400 /// The file is valid but uses something this reader does not support.
401 Unsupported {
402 /// What is unsupported.
403 what: &'static str,
404 /// The raw value of the unsupported field (a type, flag set, or class).
405 value: u64,
406 },
407 /// A [`Limits`](crate::Limits) budget was exceeded.
408 LimitExceeded {
409 /// Which limit.
410 limit: &'static str,
411 },
412 /// The file decodes to an object that breaks a rule of the model.
413 Invalid(ModelError),
414}
415
416impl fmt::Display for ReadError {
417 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418 match self {
419 ReadError::BadMagic => {
420 f.write_str("not an object file of this format (bad magic number)")
421 }
422 ReadError::Truncated { what } => {
423 write!(f, "truncated: {what} runs past the end of the input")
424 }
425 ReadError::Malformed { what } => write!(f, "malformed: {what}"),
426 ReadError::Unsupported { what, value } => write!(f, "unsupported {what} ({value:#x})"),
427 ReadError::LimitExceeded { limit } => write!(f, "limit exceeded: {limit}"),
428 ReadError::Invalid(err) => write!(f, "invalid object: {err}"),
429 }
430 }
431}
432
433impl core::error::Error for ReadError {
434 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
435 match self {
436 ReadError::Invalid(err) => Some(err),
437 _ => None,
438 }
439 }
440}
441
442impl From<ModelError> for ReadError {
443 fn from(err: ModelError) -> Self {
444 ReadError::Invalid(err)
445 }
446}