object_lang/model.rs
1//! The format-neutral object model: an [`Object`] is a target, a file kind, sections,
2//! and symbols. Every writer takes one and every reader returns one.
3
4use alloc::vec::Vec;
5
6use crate::error::ModelError;
7use crate::reloc::Relocation;
8use crate::section::Section;
9use crate::symbol::Symbol;
10
11/// The instruction set an object is built for.
12///
13/// The architecture decides which [relocation kinds](crate::RelocationKind) an object may
14/// carry and the page size an executable is laid out with.
15///
16/// # Examples
17///
18/// ```
19/// use object_lang::Architecture;
20///
21/// assert_eq!(Architecture::X86_64.page_size(), 0x1000);
22/// assert_eq!(Architecture::Aarch64.page_size(), 0x1_0000);
23/// ```
24#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
25#[non_exhaustive]
26pub enum Architecture {
27 /// 64-bit x86 (AMD64, Intel 64).
28 X86_64,
29 /// 64-bit Arm (AArch64, ARMv8-A and later).
30 Aarch64,
31}
32
33impl Architecture {
34 /// The page size an executable for this architecture is laid out with: the boundary
35 /// at which loadable segments start, and the alignment of each segment.
36 ///
37 /// AArch64 kernels run with 4 KiB, 16 KiB, or 64 KiB pages, so AArch64 uses 64 KiB,
38 /// which is correct on all three (and is what GNU ld and LLVM lld default to). x86-64
39 /// uses 4 KiB. A linker that places sections for an executable must start each
40 /// change of permissions on a fresh page of this size.
41 ///
42 /// # Examples
43 ///
44 /// ```
45 /// use object_lang::Architecture;
46 ///
47 /// let page = Architecture::X86_64.page_size();
48 /// assert!(page.is_power_of_two());
49 /// ```
50 #[must_use]
51 pub const fn page_size(self) -> u64 {
52 match self {
53 Architecture::X86_64 => 0x1000,
54 Architecture::Aarch64 => 0x1_0000,
55 }
56 }
57}
58
59/// What a file is for: input to a linker, or a program the OS can run.
60///
61/// # Examples
62///
63/// ```
64/// use object_lang::{Architecture, FileKind, Object};
65///
66/// let obj = Object::new(FileKind::Relocatable, Architecture::X86_64);
67/// assert_eq!(obj.kind(), FileKind::Relocatable);
68/// ```
69#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
70#[non_exhaustive]
71pub enum FileKind {
72 /// A relocatable object (`.o`, `.obj`): sections at no fixed address, symbol values
73 /// as offsets within their section, and relocations still to be applied by a linker.
74 Relocatable,
75 /// A statically linked executable: every loadable section at its final address,
76 /// symbol values as addresses, no relocations, and an entry point.
77 Executable,
78}
79
80/// The identity of a section within its [`Object`]: its position in
81/// [`Object::sections`].
82///
83/// Ids are dense, starting at zero in the order sections were added, so they index
84/// straight into the section list. An id is only meaningful for the object that issued it.
85///
86/// # Examples
87///
88/// ```
89/// use object_lang::{Architecture, Object, Section, SectionKind};
90///
91/// let mut obj = Object::relocatable(Architecture::X86_64);
92/// let text = obj.add_section(Section::new(".text", SectionKind::Text));
93/// let data = obj.add_section(Section::new(".data", SectionKind::Data));
94/// assert_eq!((text.index(), data.index()), (0, 1));
95/// ```
96#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
97pub struct SectionId(pub(crate) u32);
98
99impl SectionId {
100 /// The id of the section at position `index` in [`Object::sections`].
101 ///
102 /// An id is only an index: one that names no section of the object it is used with
103 /// is caught by [`Object::validate`] (or refused by [`Object::section`]), never
104 /// trusted.
105 ///
106 /// # Examples
107 ///
108 /// ```
109 /// use object_lang::{Architecture, Object, Section, SectionId, SectionKind};
110 ///
111 /// let mut obj = Object::relocatable(Architecture::X86_64);
112 /// let text = obj.add_section(Section::new(".text", SectionKind::Text));
113 /// assert_eq!(SectionId::new(0), text);
114 /// assert!(obj.section(SectionId::new(1)).is_none());
115 /// ```
116 #[must_use]
117 pub const fn new(index: u32) -> Self {
118 SectionId(index)
119 }
120
121 /// The position of the section in [`Object::sections`].
122 ///
123 /// # Examples
124 ///
125 /// ```
126 /// use object_lang::{Architecture, Object, Section, SectionKind};
127 ///
128 /// let mut obj = Object::relocatable(Architecture::Aarch64);
129 /// let id = obj.add_section(Section::new(".text", SectionKind::Text));
130 /// assert_eq!(obj.sections()[id.index()].name(), ".text");
131 /// ```
132 #[must_use]
133 pub const fn index(self) -> usize {
134 self.0 as usize
135 }
136}
137
138/// The identity of a symbol within its [`Object`]: its position in [`Object::symbols`].
139///
140/// Ids are dense, starting at zero in the order symbols were added.
141///
142/// # Examples
143///
144/// ```
145/// use object_lang::{Architecture, Object, Symbol};
146///
147/// let mut obj = Object::relocatable(Architecture::X86_64);
148/// let puts = obj.add_symbol(Symbol::undefined("puts"));
149/// assert_eq!(obj.symbols()[puts.index()].name, "puts");
150/// ```
151#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
152pub struct SymbolId(pub(crate) u32);
153
154impl SymbolId {
155 /// The id of the symbol at position `index` in [`Object::symbols`]. As with
156 /// [`SectionId::new`], an id that names no symbol is caught by [`Object::validate`].
157 ///
158 /// # Examples
159 ///
160 /// ```
161 /// use object_lang::{Architecture, Object, Symbol, SymbolId};
162 ///
163 /// let mut obj = Object::relocatable(Architecture::X86_64);
164 /// let exit = obj.add_symbol(Symbol::undefined("exit"));
165 /// assert_eq!(SymbolId::new(0), exit);
166 /// ```
167 #[must_use]
168 pub const fn new(index: u32) -> Self {
169 SymbolId(index)
170 }
171
172 /// The position of the symbol in [`Object::symbols`].
173 ///
174 /// # Examples
175 ///
176 /// ```
177 /// use object_lang::{Architecture, Object, Symbol};
178 ///
179 /// let mut obj = Object::relocatable(Architecture::X86_64);
180 /// let first = obj.add_symbol(Symbol::undefined("a"));
181 /// assert_eq!(first.index(), 0);
182 /// ```
183 #[must_use]
184 pub const fn index(self) -> usize {
185 self.0 as usize
186 }
187}
188
189/// The largest number of sections, or of symbols, one object may hold.
190///
191/// Ids are `u32`, and every format needs a few indices of its own on top of the
192/// object's, so the model stops well short of `u32::MAX`.
193pub(crate) const MAX_ITEMS: usize = 0x7fff_ffff;
194
195/// One object file or executable, independent of the format it is written in.
196///
197/// An object is a [target](Architecture), a [kind](FileKind), a list of [`Section`]s
198/// (each carrying its own [`Relocation`]s), and a list of [`Symbol`]s. Executables also
199/// have an entry point. The same object can be written in any format the crate supports;
200/// reading a file returns an object of the same shape.
201///
202/// Nothing is checked while an object is built. [`validate`](Object::validate) checks
203/// every rule the model has, and every writer runs it first, so a malformed object is an
204/// error value, never a malformed file.
205///
206/// # Examples
207///
208/// A relocatable object with a function that calls an external one:
209///
210/// ```
211/// use object_lang::{
212/// Architecture, Object, Relocation, RelocationTarget, Section, SectionKind, Symbol,
213/// X86_64Reloc,
214/// };
215///
216/// let mut obj = Object::relocatable(Architecture::X86_64);
217/// // call puts; ret
218/// let text = obj.add_section(
219/// Section::new(".text", SectionKind::Text).with_data(vec![0xe8, 0, 0, 0, 0, 0xc3]),
220/// );
221/// obj.add_symbol(Symbol::function("main", text, 0, 6));
222/// let puts = obj.add_symbol(Symbol::undefined("puts"));
223/// obj.add_relocation(
224/// text,
225/// Relocation::new(1, X86_64Reloc::Plt32, RelocationTarget::Symbol(puts), -4),
226/// )?;
227///
228/// let bytes = object_lang::elf::write(&obj)?;
229/// let back = object_lang::elf::read(&bytes)?;
230/// assert_eq!(back.symbols().len(), 2);
231/// assert_eq!(back.sections()[0].relocations().len(), 1);
232/// # Ok::<(), Box<dyn std::error::Error>>(())
233/// ```
234#[derive(Clone, PartialEq, Eq, Debug)]
235pub struct Object {
236 kind: FileKind,
237 arch: Architecture,
238 entry: u64,
239 executable_stack: bool,
240 sections: Vec<Section>,
241 symbols: Vec<Symbol>,
242}
243
244impl Object {
245 /// Creates an empty object of the given kind for the given architecture.
246 ///
247 /// The entry point starts at zero and the stack is not executable.
248 ///
249 /// # Examples
250 ///
251 /// ```
252 /// use object_lang::{Architecture, FileKind, Object};
253 ///
254 /// let exe = Object::new(FileKind::Executable, Architecture::Aarch64);
255 /// assert_eq!(exe.architecture(), Architecture::Aarch64);
256 /// assert!(exe.sections().is_empty());
257 /// ```
258 #[must_use]
259 pub const fn new(kind: FileKind, arch: Architecture) -> Self {
260 Object {
261 kind,
262 arch,
263 entry: 0,
264 executable_stack: false,
265 sections: Vec::new(),
266 symbols: Vec::new(),
267 }
268 }
269
270 /// Creates an empty relocatable object: `Object::new(FileKind::Relocatable, arch)`.
271 ///
272 /// # Examples
273 ///
274 /// ```
275 /// use object_lang::{Architecture, FileKind, Object};
276 ///
277 /// assert_eq!(Object::relocatable(Architecture::X86_64).kind(), FileKind::Relocatable);
278 /// ```
279 #[must_use]
280 pub const fn relocatable(arch: Architecture) -> Self {
281 Object::new(FileKind::Relocatable, arch)
282 }
283
284 /// Creates an empty executable: `Object::new(FileKind::Executable, arch)`.
285 ///
286 /// # Examples
287 ///
288 /// ```
289 /// use object_lang::{Architecture, FileKind, Object};
290 ///
291 /// assert_eq!(Object::executable(Architecture::X86_64).kind(), FileKind::Executable);
292 /// ```
293 #[must_use]
294 pub const fn executable(arch: Architecture) -> Self {
295 Object::new(FileKind::Executable, arch)
296 }
297
298 /// What the file is for.
299 ///
300 /// # Examples
301 ///
302 /// ```
303 /// use object_lang::{Architecture, FileKind, Object};
304 ///
305 /// assert_eq!(Object::executable(Architecture::X86_64).kind(), FileKind::Executable);
306 /// ```
307 #[must_use]
308 pub const fn kind(&self) -> FileKind {
309 self.kind
310 }
311
312 /// The instruction set the object is built for.
313 ///
314 /// # Examples
315 ///
316 /// ```
317 /// use object_lang::{Architecture, Object};
318 ///
319 /// assert_eq!(Object::relocatable(Architecture::Aarch64).architecture(), Architecture::Aarch64);
320 /// ```
321 #[must_use]
322 pub const fn architecture(&self) -> Architecture {
323 self.arch
324 }
325
326 /// The address execution starts at. Meaningful for executables, where it must lie
327 /// inside an executable section; relocatable objects normally leave it at zero.
328 ///
329 /// # Examples
330 ///
331 /// ```
332 /// use object_lang::{Architecture, Object};
333 ///
334 /// let mut exe = Object::executable(Architecture::X86_64);
335 /// exe.set_entry(0x40_1000);
336 /// assert_eq!(exe.entry(), 0x40_1000);
337 /// ```
338 #[must_use]
339 pub const fn entry(&self) -> u64 {
340 self.entry
341 }
342
343 /// Sets the entry point address. See [`entry`](Object::entry).
344 ///
345 /// # Examples
346 ///
347 /// ```
348 /// use object_lang::{Architecture, Object};
349 ///
350 /// let mut exe = Object::executable(Architecture::Aarch64);
351 /// exe.set_entry(0x41_0000);
352 /// assert_eq!(exe.entry(), 0x41_0000);
353 /// ```
354 pub fn set_entry(&mut self, address: u64) {
355 self.entry = address;
356 }
357
358 /// Whether the program asks for an executable stack.
359 ///
360 /// Almost nothing should: it is off by default. ELF records the request as a
361 /// `.note.GNU-stack` section in relocatable objects and a `PT_GNU_STACK` program
362 /// header in executables; when a file read in carries neither, it is reported as
363 /// `true`, because that is how the GNU toolchain and the Linux kernel treat a file
364 /// that does not say.
365 ///
366 /// # Examples
367 ///
368 /// ```
369 /// use object_lang::{Architecture, Object};
370 ///
371 /// let obj = Object::relocatable(Architecture::X86_64);
372 /// assert!(!obj.executable_stack());
373 /// ```
374 #[must_use]
375 pub const fn executable_stack(&self) -> bool {
376 self.executable_stack
377 }
378
379 /// Sets whether the program asks for an executable stack. See
380 /// [`executable_stack`](Object::executable_stack).
381 ///
382 /// # Examples
383 ///
384 /// ```
385 /// use object_lang::{Architecture, Object};
386 ///
387 /// let mut obj = Object::relocatable(Architecture::X86_64);
388 /// obj.set_executable_stack(true);
389 /// assert!(obj.executable_stack());
390 /// ```
391 pub fn set_executable_stack(&mut self, executable: bool) {
392 self.executable_stack = executable;
393 }
394
395 /// Adds a section and returns its id. Sections keep the order they were added in.
396 ///
397 /// # Examples
398 ///
399 /// ```
400 /// use object_lang::{Architecture, Object, Section, SectionKind};
401 ///
402 /// let mut obj = Object::relocatable(Architecture::X86_64);
403 /// let bss = obj.add_section(Section::new(".bss", SectionKind::Bss).with_bss_size(64));
404 /// assert_eq!(obj.section(bss).map(|s| s.size()), Some(64));
405 /// ```
406 pub fn add_section(&mut self, section: Section) -> SectionId {
407 // Ids beyond MAX_ITEMS saturate here and are refused by `validate`, which
408 // checks the count; adding is infallible so building reads as a plain sequence.
409 let id = SectionId(u32::try_from(self.sections.len()).unwrap_or(u32::MAX));
410 self.sections.push(section);
411 id
412 }
413
414 /// Every section, in the order added; a [`SectionId`] indexes this slice.
415 ///
416 /// # Examples
417 ///
418 /// ```
419 /// use object_lang::{Architecture, Object, Section, SectionKind};
420 ///
421 /// let mut obj = Object::relocatable(Architecture::X86_64);
422 /// obj.add_section(Section::new(".text", SectionKind::Text));
423 /// assert_eq!(obj.sections()[0].name(), ".text");
424 /// ```
425 #[must_use]
426 pub fn sections(&self) -> &[Section] {
427 &self.sections
428 }
429
430 /// The section with this id, or `None` if the object has no such section.
431 ///
432 /// # Examples
433 ///
434 /// ```
435 /// use object_lang::{Architecture, Object, Section, SectionKind};
436 ///
437 /// let mut obj = Object::relocatable(Architecture::X86_64);
438 /// let id = obj.add_section(Section::new(".rodata", SectionKind::ReadOnlyData));
439 /// assert_eq!(obj.section(id).map(Section::name), Some(".rodata"));
440 /// ```
441 #[must_use]
442 pub fn section(&self, id: SectionId) -> Option<&Section> {
443 self.sections.get(id.index())
444 }
445
446 /// The section with this id, mutably, or `None` if the object has no such section.
447 ///
448 /// # Examples
449 ///
450 /// ```
451 /// use object_lang::{Architecture, Object, Section, SectionKind};
452 ///
453 /// let mut obj = Object::relocatable(Architecture::X86_64);
454 /// let text = obj.add_section(Section::new(".text", SectionKind::Text));
455 /// let offset = obj.section_mut(text).map(|s| s.append(&[0xc3], 16));
456 /// assert_eq!(offset, Some(Ok(0)));
457 /// ```
458 #[must_use]
459 pub fn section_mut(&mut self, id: SectionId) -> Option<&mut Section> {
460 self.sections.get_mut(id.index())
461 }
462
463 /// The first section with this name, or `None`. A linear search: formats allow
464 /// several sections to share a name, and this returns the earliest.
465 ///
466 /// # Examples
467 ///
468 /// ```
469 /// use object_lang::{Architecture, Object, Section, SectionKind};
470 ///
471 /// let mut obj = Object::relocatable(Architecture::X86_64);
472 /// let data = obj.add_section(Section::new(".data", SectionKind::Data));
473 /// assert_eq!(obj.section_by_name(".data"), Some(data));
474 /// assert_eq!(obj.section_by_name(".bss"), None);
475 /// ```
476 #[must_use]
477 pub fn section_by_name(&self, name: &str) -> Option<SectionId> {
478 self.sections
479 .iter()
480 .position(|s| s.name() == name)
481 .and_then(|i| u32::try_from(i).ok())
482 .map(SectionId)
483 }
484
485 /// Adds a symbol and returns its id. Symbols keep the order they were added in;
486 /// formats that need a different order (ELF puts local symbols first) reorder on
487 /// write, so a file read back lists them in the format's order.
488 ///
489 /// # Examples
490 ///
491 /// ```
492 /// use object_lang::{Architecture, Object, Section, SectionKind, Symbol};
493 ///
494 /// let mut obj = Object::relocatable(Architecture::X86_64);
495 /// let text = obj.add_section(Section::new(".text", SectionKind::Text).with_data(vec![0xc3]));
496 /// let f = obj.add_symbol(Symbol::function("f", text, 0, 1));
497 /// assert_eq!(obj.symbol(f).map(|s| s.name.as_str()), Some("f"));
498 /// ```
499 pub fn add_symbol(&mut self, symbol: Symbol) -> SymbolId {
500 let id = SymbolId(u32::try_from(self.symbols.len()).unwrap_or(u32::MAX));
501 self.symbols.push(symbol);
502 id
503 }
504
505 /// Every symbol, in the order added; a [`SymbolId`] indexes this slice.
506 ///
507 /// # Examples
508 ///
509 /// ```
510 /// use object_lang::{Architecture, Object, Symbol};
511 ///
512 /// let mut obj = Object::relocatable(Architecture::X86_64);
513 /// obj.add_symbol(Symbol::undefined("exit"));
514 /// assert_eq!(obj.symbols()[0].name, "exit");
515 /// ```
516 #[must_use]
517 pub fn symbols(&self) -> &[Symbol] {
518 &self.symbols
519 }
520
521 /// The symbol with this id, or `None` if the object has no such symbol.
522 ///
523 /// # Examples
524 ///
525 /// ```
526 /// use object_lang::{Architecture, Object, Symbol};
527 ///
528 /// let mut obj = Object::relocatable(Architecture::X86_64);
529 /// let id = obj.add_symbol(Symbol::undefined("exit"));
530 /// assert!(obj.symbol(id).is_some());
531 /// ```
532 #[must_use]
533 pub fn symbol(&self, id: SymbolId) -> Option<&Symbol> {
534 self.symbols.get(id.index())
535 }
536
537 /// The symbol with this id, mutably, or `None` if the object has no such symbol.
538 ///
539 /// # Examples
540 ///
541 /// ```
542 /// use object_lang::{Architecture, Binding, Object, Symbol};
543 ///
544 /// let mut obj = Object::relocatable(Architecture::X86_64);
545 /// let id = obj.add_symbol(Symbol::undefined("hook"));
546 /// if let Some(sym) = obj.symbol_mut(id) {
547 /// sym.binding = Binding::Weak;
548 /// }
549 /// assert_eq!(obj.symbols()[0].binding, Binding::Weak);
550 /// ```
551 #[must_use]
552 pub fn symbol_mut(&mut self, id: SymbolId) -> Option<&mut Symbol> {
553 self.symbols.get_mut(id.index())
554 }
555
556 /// The first symbol with this name, or `None`. A linear search.
557 ///
558 /// # Examples
559 ///
560 /// ```
561 /// use object_lang::{Architecture, Object, Symbol};
562 ///
563 /// let mut obj = Object::relocatable(Architecture::X86_64);
564 /// let id = obj.add_symbol(Symbol::undefined("memcpy"));
565 /// assert_eq!(obj.symbol_by_name("memcpy"), Some(id));
566 /// ```
567 #[must_use]
568 pub fn symbol_by_name(&self, name: &str) -> Option<SymbolId> {
569 self.symbols
570 .iter()
571 .position(|s| s.name == name)
572 .and_then(|i| u32::try_from(i).ok())
573 .map(SymbolId)
574 }
575
576 /// Adds a relocation to a section: shorthand for
577 /// `object.section_mut(section)?.add_relocation(relocation)`.
578 ///
579 /// The relocation itself is checked by [`validate`](Object::validate), like
580 /// everything else.
581 ///
582 /// # Errors
583 ///
584 /// [`ModelError::UnknownSection`] if the object has no section `section`.
585 ///
586 /// # Examples
587 ///
588 /// ```
589 /// use object_lang::{
590 /// Aarch64Reloc, Architecture, Object, Relocation, RelocationTarget, Section,
591 /// SectionKind, Symbol,
592 /// };
593 ///
594 /// let mut obj = Object::relocatable(Architecture::Aarch64);
595 /// // bl <callee>
596 /// let text = obj.add_section(
597 /// Section::new(".text", SectionKind::Text).with_data(vec![0x00, 0x00, 0x00, 0x94]),
598 /// );
599 /// let callee = obj.add_symbol(Symbol::undefined("callee"));
600 /// obj.add_relocation(
601 /// text,
602 /// Relocation::new(0, Aarch64Reloc::Call26, RelocationTarget::Symbol(callee), 0),
603 /// )?;
604 /// assert_eq!(obj.sections()[0].relocations().len(), 1);
605 /// # Ok::<(), object_lang::ModelError>(())
606 /// ```
607 pub fn add_relocation(
608 &mut self,
609 section: SectionId,
610 relocation: Relocation,
611 ) -> Result<(), ModelError> {
612 match self.sections.get_mut(section.index()) {
613 Some(s) => {
614 s.add_relocation(relocation);
615 Ok(())
616 }
617 None => Err(ModelError::UnknownSection { section }),
618 }
619 }
620
621 /// Checks every rule of the model, returning the first violation.
622 ///
623 /// Writers call this before writing anything, and readers call it on what they read,
624 /// so an object that passes is one every writer can represent (barring
625 /// format-specific limits, reported as [`WriteError`](crate::WriteError)). The rules
626 /// are listed under [`ModelError`], [`SectionProblem`](crate::SectionProblem),
627 /// [`SymbolProblem`](crate::SymbolProblem), and
628 /// [`RelocationProblem`](crate::RelocationProblem).
629 ///
630 /// # Errors
631 ///
632 /// The first rule the object breaks, as a [`ModelError`].
633 ///
634 /// # Examples
635 ///
636 /// ```
637 /// use object_lang::{
638 /// Architecture, ModelError, Object, Section, SectionKind, SectionProblem,
639 /// };
640 ///
641 /// let mut obj = Object::relocatable(Architecture::X86_64);
642 /// let text = obj.add_section(Section::new(".text", SectionKind::Text).with_align(3));
643 /// assert_eq!(
644 /// obj.validate(),
645 /// Err(ModelError::Section { section: text, problem: SectionProblem::BadAlignment }),
646 /// );
647 /// ```
648 pub fn validate(&self) -> Result<(), ModelError> {
649 crate::validate::validate(self)
650 }
651
652 /// Builds an object from parts a reader has already checked structurally.
653 pub(crate) fn from_parts(
654 kind: FileKind,
655 arch: Architecture,
656 entry: u64,
657 executable_stack: bool,
658 sections: Vec<Section>,
659 symbols: Vec<Symbol>,
660 ) -> Self {
661 Object {
662 kind,
663 arch,
664 entry,
665 executable_stack,
666 sections,
667 symbols,
668 }
669 }
670}