object_lang/elf/mod.rs
1//! ELF64, little-endian, for x86-64 and AArch64: relocatable objects (`ET_REL`) and
2//! static executables (`ET_EXEC`).
3//!
4//! - [`write()`] and [`write_into`] turn an [`Object`] into ELF bytes.
5//! - [`read()`] and [`read_with_limits`] turn ELF bytes back into an [`Object`].
6//! - [`executable`] is the one-call path from machine code to a runnable program.
7//!
8//! ## What the writer produces
9//!
10//! A **relocatable object** holds the object's sections in order (section header `i + 1`
11//! is the model's section `i`), then one `.rela<name>` section per section with
12//! relocations, an empty `.note.GNU-stack` marking whether the stack must be executable,
13//! `.symtab` (with `.symtab_shndx` when a section index exceeds 16 bits), `.strtab`, and
14//! `.shstrtab`. The symbol table starts with the null symbol, then one `STT_SECTION`
15//! symbol for each section a relocation targets directly, then the object's local
16//! symbols, then the rest, each group in model order; `sh_info` is the index of the
17//! first non-local symbol. String tables share the bytes of names that are suffixes of
18//! other names. Section counts and indices past 0xff00 use ELF's extended numbering.
19//!
20//! An **executable** maps its file headers read-only on the page(s) just below its
21//! lowest section, then one `PT_LOAD` per run of sections with the same permissions,
22//! each aligned to the [page size](crate::Architecture::page_size) with its file offset
23//! congruent to its address. A `PT_GNU_STACK` header marks the stack non-executable
24//! unless [`executable_stack`](Object::executable_stack) is set. There is no
25//! interpreter and no dynamic section: the program is fully static. Section headers and
26//! a symbol table are written too, so tools can name what they disassemble.
27//!
28//! The output depends only on the object: the same object always gives the same bytes.
29//!
30//! ## What the reader accepts
31//!
32//! Exactly the files the model can represent: ELF64 little-endian `ET_REL` or `ET_EXEC`
33//! for x86-64 or AArch64, with `SHT_RELA` relocations of the kinds in
34//! [`X86_64Reloc`](crate::X86_64Reloc) and [`Aarch64Reloc`](crate::Aarch64Reloc). It
35//! drops what carries no meaning in the model (`STT_SECTION` symbols, which become
36//! [`RelocationTarget::Section`](crate::RelocationTarget::Section); LLVM's
37//! `.llvm_addrsig` hint) and refuses everything else it cannot represent exactly
38//! (COMDAT groups, `SHT_REL`, dynamic sections, unknown section, symbol, or
39//! relocation types) with a [`ReadError`].
40
41mod consts;
42mod read;
43mod strtab;
44mod write;
45
46use alloc::vec::Vec;
47
48use crate::error::{ReadError, WriteError};
49use crate::limits::Limits;
50use crate::model::{Architecture, Object};
51use crate::section::{Section, SectionKind};
52use crate::symbol::Symbol;
53
54/// Writes an object as an ELF file and returns the bytes.
55///
56/// The object is [validated](Object::validate) first. The output is deterministic.
57///
58/// # Errors
59///
60/// - [`WriteError::Invalid`] if the object breaks a rule of the model.
61/// - [`WriteError::ReservedSectionName`] for a relocatable object with a section named
62/// `.note.GNU-stack`, which the writer emits itself.
63/// - For executables: [`WriteError::SegmentsSharePage`],
64/// [`WriteError::NoRoomForHeaders`], and [`WriteError::Unsupported`] for
65/// thread-local sections.
66/// - [`WriteError::TooLarge`] if a table outgrows ELF's 32-bit fields.
67///
68/// # Examples
69///
70/// ```
71/// use object_lang::{Architecture, Object, Section, SectionKind, Symbol};
72///
73/// let mut obj = Object::relocatable(Architecture::X86_64);
74/// let text = obj.add_section(Section::new(".text", SectionKind::Text).with_data(vec![0xc3]));
75/// obj.add_symbol(Symbol::function("ret", text, 0, 1));
76///
77/// let bytes = object_lang::elf::write(&obj)?;
78/// assert_eq!(&bytes[..4], b"\x7fELF");
79/// assert_eq!(object_lang::elf::read(&bytes)?, obj);
80/// # Ok::<(), Box<dyn std::error::Error>>(())
81/// ```
82pub fn write(object: &Object) -> Result<Vec<u8>, WriteError> {
83 let mut out = Vec::new();
84 write::write_into(object, &mut out)?;
85 Ok(out)
86}
87
88/// Writes an object as an ELF file, appending the bytes to `out`, so one buffer can be
89/// reused across many objects. On error, `out` is left as it was.
90///
91/// # Errors
92///
93/// As [`write()`].
94///
95/// # Examples
96///
97/// ```
98/// use object_lang::{Architecture, Object};
99///
100/// let mut buffer = Vec::new();
101/// for arch in [Architecture::X86_64, Architecture::Aarch64] {
102/// buffer.clear();
103/// object_lang::elf::write_into(&Object::relocatable(arch), &mut buffer)?;
104/// assert_eq!(object_lang::elf::read(&buffer)?.architecture(), arch);
105/// }
106/// # Ok::<(), Box<dyn std::error::Error>>(())
107/// ```
108pub fn write_into(object: &Object, out: &mut Vec<u8>) -> Result<(), WriteError> {
109 write::write_into(object, out)
110}
111
112/// Reads an ELF file into an object, with the default [`Limits`].
113///
114/// # Errors
115///
116/// A [`ReadError`] saying what is wrong: [`BadMagic`](ReadError::BadMagic) for bytes
117/// that are not ELF, [`Truncated`](ReadError::Truncated) when a structure runs past the
118/// input, [`Malformed`](ReadError::Malformed) when fields contradict the format or each
119/// other, [`Unsupported`](ReadError::Unsupported) for valid ELF the model cannot
120/// represent, [`LimitExceeded`](ReadError::LimitExceeded) when a budget is exceeded,
121/// and [`Invalid`](ReadError::Invalid) when the decoded object breaks a model rule. It
122/// never panics, whatever the bytes.
123///
124/// # Examples
125///
126/// ```
127/// use object_lang::{Architecture, FileKind};
128///
129/// let exe = object_lang::elf::executable(Architecture::X86_64, &[0xeb, 0xfe])?; // jmp .
130/// let obj = object_lang::elf::read(&exe)?;
131/// assert_eq!(obj.kind(), FileKind::Executable);
132/// assert_eq!(obj.sections()[0].data(), &[0xeb, 0xfe]);
133/// # Ok::<(), Box<dyn std::error::Error>>(())
134/// ```
135pub fn read(bytes: &[u8]) -> Result<Object, ReadError> {
136 read::read(bytes, &Limits::DEFAULT)
137}
138
139/// Reads an ELF file into an object, with the given [`Limits`].
140///
141/// # Errors
142///
143/// As [`read()`], with [`ReadError::LimitExceeded`] naming the first budget exceeded.
144///
145/// # Examples
146///
147/// ```
148/// use object_lang::{Architecture, Limits, Object, ReadError, Symbol};
149///
150/// let mut obj = Object::relocatable(Architecture::X86_64);
151/// obj.add_symbol(Symbol::undefined("a_rather_long_symbol_name"));
152/// let bytes = object_lang::elf::write(&obj)?;
153///
154/// let mut limits = Limits::default();
155/// limits.max_name_len = 8;
156/// assert_eq!(
157/// object_lang::elf::read_with_limits(&bytes, &limits),
158/// Err(ReadError::LimitExceeded { limit: "max_name_len" }),
159/// );
160/// # Ok::<(), object_lang::WriteError>(())
161/// ```
162pub fn read_with_limits(bytes: &[u8], limits: &Limits) -> Result<Object, ReadError> {
163 read::read(bytes, limits)
164}
165
166/// The address [`executable`] loads its code at: one page above 4 MiB (`0x40_0000`), the
167/// traditional base of a static Linux executable, with the file headers on the page(s)
168/// below it.
169///
170/// # Examples
171///
172/// ```
173/// use object_lang::{Architecture, elf};
174///
175/// assert_eq!(elf::code_address(Architecture::X86_64), 0x40_1000);
176/// assert_eq!(elf::code_address(Architecture::Aarch64), 0x41_0000);
177/// ```
178#[must_use]
179pub const fn code_address(arch: Architecture) -> u64 {
180 0x40_0000 + arch.page_size()
181}
182
183/// Builds a static executable from machine code: the one-call path to a program the OS
184/// can run.
185///
186/// `code` becomes a `.text` section at [`code_address`], 16-byte aligned, and execution
187/// starts at its first byte, where a global `_start` symbol marks it. The code must not
188/// need relocations or data of its own beyond what it carries; for anything more,
189/// build an executable [`Object`] and [`write()`] it.
190///
191/// # Errors
192///
193/// [`WriteError::Invalid`] with [`EntryNotExecutable`](crate::ModelError::EntryNotExecutable)
194/// if `code` is empty.
195///
196/// # Examples
197///
198/// `exit(42)` on Linux x86-64, hand-assembled:
199///
200/// ```
201/// use object_lang::Architecture;
202///
203/// let code = [
204/// 0xbf, 0x2a, 0x00, 0x00, 0x00, // mov edi, 42
205/// 0xb8, 0x3c, 0x00, 0x00, 0x00, // mov eax, 60 (exit)
206/// 0x0f, 0x05, // syscall
207/// ];
208/// let program = object_lang::elf::executable(Architecture::X86_64, &code)?;
209/// // Write `program` to a file, mark it executable, run it: it exits with status 42.
210/// assert_eq!(&program[..4], b"\x7fELF");
211/// # Ok::<(), object_lang::WriteError>(())
212/// ```
213pub fn executable(arch: Architecture, code: &[u8]) -> Result<Vec<u8>, WriteError> {
214 let address = code_address(arch);
215 let mut object = Object::executable(arch);
216 let text = object.add_section(
217 Section::new(".text", SectionKind::Text)
218 .with_align(16)
219 .with_address(address)
220 .with_data(code.to_vec()),
221 );
222 let size = u64::try_from(code.len()).map_err(|_| WriteError::TooLarge)?;
223 let _start = object.add_symbol(Symbol::function("_start", text, address, size));
224 object.set_entry(address);
225 write(&object)
226}