Skip to main content

linker_lang/
object.rs

1//! The input to a link: an [`Object`] and the pieces it is built from.
2
3use alloc::string::String;
4use alloc::vec::Vec;
5
6/// The size of the address a [relocation](Object::relocate) patches in.
7///
8/// A relocation writes a resolved address into a section as a little-endian integer of
9/// this width. Pick the width that matches the slot the producer left: a 32-bit pointer
10/// table uses [`U32`](Width::U32), a 64-bit one uses [`U64`](Width::U64). The link fails
11/// with [`RelocationOverflow`](crate::LinkError::RelocationOverflow) if a resolved address
12/// does not fit the chosen width.
13///
14/// # Examples
15///
16/// ```
17/// use linker_lang::Width;
18///
19/// assert_eq!(Width::U32.bytes(), 4);
20/// assert_eq!(Width::U64.bytes(), 8);
21/// ```
22#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub enum Width {
25    /// A 32-bit address: four little-endian bytes.
26    U32,
27    /// A 64-bit address: eight little-endian bytes.
28    U64,
29}
30
31impl Width {
32    /// Returns the number of bytes a relocation of this width occupies.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// use linker_lang::Width;
38    ///
39    /// assert_eq!(Width::U32.bytes(), 4);
40    /// assert_eq!(Width::U64.bytes(), 8);
41    /// ```
42    #[must_use]
43    pub const fn bytes(self) -> usize {
44        match self {
45            Width::U32 => 4,
46            Width::U64 => 8,
47        }
48    }
49
50    /// The largest unsigned value this width can hold, as an `i128` so an addend can push a
51    /// candidate address above it and still be compared without wrapping.
52    pub(crate) const fn max_value(self) -> i128 {
53        match self {
54            Width::U32 => u32::MAX as i128,
55            Width::U64 => u64::MAX as i128,
56        }
57    }
58}
59
60/// A named run of bytes contributed by an object — the unit sections merge by.
61pub(crate) struct Section {
62    pub(crate) name: String,
63    pub(crate) data: Vec<u8>,
64}
65
66/// A symbol definition: `name` lives at `offset` inside the object's section `section`.
67pub(crate) struct SymbolDef {
68    pub(crate) name: String,
69    pub(crate) section: String,
70    pub(crate) offset: u64,
71}
72
73/// A relocation: the `width` bytes at `offset` in `section` must hold the address of
74/// `target`, plus `addend`.
75pub(crate) struct Relocation {
76    pub(crate) section: String,
77    pub(crate) offset: u64,
78    pub(crate) target: String,
79    pub(crate) width: Width,
80    pub(crate) addend: i64,
81}
82
83/// One compilation unit handed to the linker: named byte sections, the symbols defined in
84/// them, and the relocations still to be patched.
85///
86/// An object is the linker's input island — the form a backend or an object-file reader
87/// produces. Build one with [`new`](Object::new), append bytes with
88/// [`section`](Object::section), mark addresses with [`define`](Object::define), and
89/// record holes to fill with [`relocate`](Object::relocate). The object carries no
90/// addresses of its own; [`link`](crate::link) assigns them when it lays every object out
91/// together.
92///
93/// Sections are addressed by name. Appending to a name that already exists extends that
94/// section rather than starting a new one, so an object holds at most one section per
95/// name.
96///
97/// # Examples
98///
99/// Build an object with a `.text` section and a symbol at its start:
100///
101/// ```
102/// use linker_lang::Object;
103///
104/// let mut obj = Object::new("greeter");
105/// obj.section(".text", b"\xc3".to_vec()); // a one-byte `ret`
106/// obj.define("greet", ".text", 0);
107///
108/// assert_eq!(obj.name(), "greeter");
109/// ```
110pub struct Object {
111    pub(crate) name: String,
112    pub(crate) sections: Vec<Section>,
113    pub(crate) symbols: Vec<SymbolDef>,
114    pub(crate) relocations: Vec<Relocation>,
115}
116
117impl Object {
118    /// Creates an empty object with the given name.
119    ///
120    /// The name identifies the object in diagnostics — for example, which object a
121    /// [`RelocationOutOfRange`](crate::LinkError::RelocationOutOfRange) came from — and is
122    /// not otherwise linked into the image.
123    ///
124    /// # Examples
125    ///
126    /// ```
127    /// use linker_lang::Object;
128    ///
129    /// let obj = Object::new("module.o");
130    /// assert_eq!(obj.name(), "module.o");
131    /// ```
132    #[must_use]
133    pub fn new(name: impl Into<String>) -> Self {
134        Object {
135            name: name.into(),
136            sections: Vec::new(),
137            symbols: Vec::new(),
138            relocations: Vec::new(),
139        }
140    }
141
142    /// Returns the object's name.
143    #[must_use]
144    pub fn name(&self) -> &str {
145        &self.name
146    }
147
148    /// Appends `data` to the section named `name`, creating the section if it is new, and
149    /// returns the offset within that section where `data` begins.
150    ///
151    /// The returned offset is the natural anchor for a symbol or relocation you place next:
152    /// it is `0` for a fresh section and the previous length when extending one. Sections
153    /// with the same name across objects merge at link time in object order.
154    ///
155    /// # Examples
156    ///
157    /// Two appends build one section; the second starts where the first ended:
158    ///
159    /// ```
160    /// use linker_lang::Object;
161    ///
162    /// let mut obj = Object::new("data");
163    /// assert_eq!(obj.section(".data", [1, 2, 3, 4]), 0);
164    /// assert_eq!(obj.section(".data", [5, 6]), 4); // appended after the first chunk
165    /// ```
166    pub fn section(&mut self, name: impl Into<String>, data: impl Into<Vec<u8>>) -> u64 {
167        let name = name.into();
168        let data = data.into();
169        if let Some(section) = self.sections.iter_mut().find(|s| s.name == name) {
170            let start = section.data.len() as u64;
171            section.data.extend_from_slice(&data);
172            start
173        } else {
174            self.sections.push(Section { name, data });
175            0
176        }
177    }
178
179    /// Defines a symbol named `name` at `offset` bytes into the section `section`.
180    ///
181    /// The symbol resolves to an absolute address once the image is laid out. Defining the
182    /// same name in two objects is a [`DuplicateSymbol`](crate::LinkError::DuplicateSymbol)
183    /// error at link time; naming a section the object never created is an
184    /// [`InvalidSection`](crate::LinkError::InvalidSection) error.
185    ///
186    /// # Examples
187    ///
188    /// ```
189    /// use linker_lang::Object;
190    ///
191    /// let mut obj = Object::new("code");
192    /// obj.section(".text", [0u8; 16]);
193    /// obj.define("entry", ".text", 0);
194    /// obj.define("helper", ".text", 8);
195    /// ```
196    pub fn define(&mut self, name: impl Into<String>, section: impl Into<String>, offset: u64) {
197        self.symbols.push(SymbolDef {
198            name: name.into(),
199            section: section.into(),
200            offset,
201        });
202    }
203
204    /// Records a relocation: the `width` bytes at `offset` in `section` hold the address of
205    /// `target`, plus `addend`.
206    ///
207    /// At link time the target is resolved to its address, `addend` is added, and the
208    /// result is written into the section's bytes little-endian. A target no object defines
209    /// is an [`UndefinedSymbol`](crate::LinkError::UndefinedSymbol); a slot that runs past
210    /// the section's bytes is a [`RelocationOutOfRange`](crate::LinkError::RelocationOutOfRange);
211    /// a resolved value too large for `width` is a
212    /// [`RelocationOverflow`](crate::LinkError::RelocationOverflow).
213    ///
214    /// # Examples
215    ///
216    /// A pointer slot that should hold the address of `entry`, and a second that should
217    /// hold the address eight bytes past it:
218    ///
219    /// ```
220    /// use linker_lang::{Object, Width};
221    ///
222    /// let mut obj = Object::new("vectors");
223    /// obj.section(".data", [0u8; 16]);
224    /// obj.relocate(".data", 0, "entry", Width::U64, 0);
225    /// obj.relocate(".data", 8, "entry", Width::U64, 8);
226    /// ```
227    pub fn relocate(
228        &mut self,
229        section: impl Into<String>,
230        offset: u64,
231        target: impl Into<String>,
232        width: Width,
233        addend: i64,
234    ) {
235        self.relocations.push(Relocation {
236            section: section.into(),
237            offset,
238            target: target.into(),
239            width,
240            addend,
241        });
242    }
243
244    /// The bytes of the section named `name`, or `None` if the object has no such section.
245    pub(crate) fn section_data(&self, name: &str) -> Option<&[u8]> {
246        self.sections
247            .iter()
248            .find(|s| s.name == name)
249            .map(|s| s.data.as_slice())
250    }
251}
252
253#[cfg(test)]
254#[allow(
255    clippy::unwrap_used,
256    reason = "tests assert on known values; a missing one should fail the test loudly"
257)]
258mod tests {
259    use super::{Object, Width};
260
261    #[test]
262    fn test_width_bytes_match_the_variant() {
263        assert_eq!(Width::U32.bytes(), 4);
264        assert_eq!(Width::U64.bytes(), 8);
265    }
266
267    #[test]
268    fn test_new_object_is_named_and_empty() {
269        let obj = Object::new("unit");
270        assert_eq!(obj.name(), "unit");
271        assert!(obj.sections.is_empty());
272        assert!(obj.symbols.is_empty());
273        assert!(obj.relocations.is_empty());
274    }
275
276    #[test]
277    fn test_section_returns_zero_for_a_fresh_section() {
278        let mut obj = Object::new("o");
279        assert_eq!(obj.section(".text", [1, 2, 3]), 0);
280    }
281
282    #[test]
283    fn test_section_appends_to_an_existing_name_and_returns_the_join_offset() {
284        let mut obj = Object::new("o");
285        assert_eq!(obj.section(".data", [0u8; 4]), 0);
286        assert_eq!(obj.section(".data", [9u8; 2]), 4);
287        // The two appends became one six-byte section.
288        assert_eq!(obj.section_data(".data").unwrap().len(), 6);
289        assert_eq!(obj.sections.len(), 1);
290    }
291
292    #[test]
293    fn test_distinct_names_make_distinct_sections() {
294        let mut obj = Object::new("o");
295        let _ = obj.section(".text", [0u8; 3]);
296        let _ = obj.section(".data", [0u8; 5]);
297        assert_eq!(obj.sections.len(), 2);
298        assert_eq!(obj.section_data(".text").unwrap().len(), 3);
299        assert_eq!(obj.section_data(".data").unwrap().len(), 5);
300    }
301
302    #[test]
303    fn test_section_data_is_none_for_an_unknown_name() {
304        let obj = Object::new("o");
305        assert!(obj.section_data(".bss").is_none());
306    }
307}