Skip to main content

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}