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}