linker_lang/image.rs
1//! The output of a link: an [`Image`] and the [`OutputSection`]s it is laid out from.
2
3use alloc::collections::BTreeMap;
4use alloc::string::String;
5use alloc::vec::Vec;
6use core::fmt;
7
8/// One laid-out section in a linked [`Image`]: a name, the address it begins at, and its
9/// merged, relocated bytes.
10///
11/// An output section is the concatenation of every input section that shared its name,
12/// in object order, placed at a fixed [`address`](OutputSection::address). Its
13/// [`data`](OutputSection::data) is final — relocations into it have been patched — so it
14/// is ready to be written to a file or loaded into memory at that address.
15///
16/// # Examples
17///
18/// ```
19/// use linker_lang::{link, Object};
20///
21/// let mut obj = Object::new("o");
22/// obj.section(".text", [1, 2, 3, 4]);
23/// let image = link(&[obj]).unwrap();
24///
25/// let text = image.section(".text").unwrap();
26/// assert_eq!(text.name(), ".text");
27/// assert_eq!(text.address(), 0);
28/// assert_eq!(text.data(), &[1, 2, 3, 4]);
29/// ```
30#[derive(Clone, PartialEq, Eq, Debug)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct OutputSection {
33 pub(crate) name: String,
34 pub(crate) address: u64,
35 pub(crate) data: Vec<u8>,
36}
37
38impl OutputSection {
39 /// Returns the section's name.
40 #[must_use]
41 pub fn name(&self) -> &str {
42 &self.name
43 }
44
45 /// Returns the address the section begins at in the laid-out image.
46 #[must_use]
47 pub const fn address(&self) -> u64 {
48 self.address
49 }
50
51 /// Returns the section's final bytes, with every relocation into it patched.
52 #[must_use]
53 pub fn data(&self) -> &[u8] {
54 &self.data
55 }
56
57 /// Returns the number of bytes in the section.
58 #[must_use]
59 pub fn len(&self) -> usize {
60 self.data.len()
61 }
62
63 /// Returns `true` if the section has no bytes.
64 #[must_use]
65 pub fn is_empty(&self) -> bool {
66 self.data.is_empty()
67 }
68}
69
70/// A linked image: the laid-out sections, the resolved symbol table, and the entry point.
71///
72/// An image is what [`link`](crate::link) produces on success. Its
73/// [`sections`](Image::sections) are placed at fixed addresses with their relocations
74/// patched; its symbol table maps every defined name to the address it resolved to; and
75/// its [`entry`](Image::entry) is the address of the configured entry point, if any. Look
76/// up an address with [`symbol`](Image::symbol), or read a section's bytes with
77/// [`section`](Image::section).
78///
79/// The [`Display`](fmt::Display) implementation renders a readable link map: the entry
80/// point, each section with its address and size, and the symbol table sorted by name.
81///
82/// # Examples
83///
84/// ```
85/// use linker_lang::{Linker, Object};
86///
87/// let mut obj = Object::new("o");
88/// obj.section(".text", [0u8; 8]);
89/// obj.define("main", ".text", 0);
90/// obj.define("loop", ".text", 4);
91///
92/// let image = Linker::new().entry("main").link(&[obj]).unwrap();
93///
94/// assert_eq!(image.symbol("main"), Some(0));
95/// assert_eq!(image.symbol("loop"), Some(4));
96/// assert_eq!(image.entry(), Some(0));
97/// assert_eq!(image.symbols().count(), 2);
98/// ```
99#[derive(Clone, PartialEq, Eq, Debug)]
100#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
101pub struct Image {
102 pub(crate) sections: Vec<OutputSection>,
103 pub(crate) symbols: BTreeMap<String, u64>,
104 pub(crate) entry: Option<u64>,
105}
106
107impl Image {
108 /// Returns the laid-out sections, in address order.
109 #[must_use]
110 pub fn sections(&self) -> &[OutputSection] {
111 &self.sections
112 }
113
114 /// Returns the section named `name`, or `None` if the image has no such section.
115 ///
116 /// # Examples
117 ///
118 /// ```
119 /// use linker_lang::{link, Object};
120 ///
121 /// let mut obj = Object::new("o");
122 /// obj.section(".data", [7, 7]);
123 /// let image = link(&[obj]).unwrap();
124 ///
125 /// assert!(image.section(".data").is_some());
126 /// assert!(image.section(".bss").is_none());
127 /// ```
128 #[must_use]
129 pub fn section(&self, name: &str) -> Option<&OutputSection> {
130 self.sections.iter().find(|s| s.name == name)
131 }
132
133 /// Returns the resolved address of the symbol named `name`, or `None` if no object
134 /// defined it.
135 ///
136 /// # Examples
137 ///
138 /// ```
139 /// use linker_lang::{link, Object};
140 ///
141 /// let mut obj = Object::new("o");
142 /// obj.section(".text", [0u8; 4]);
143 /// obj.define("f", ".text", 2);
144 /// let image = link(&[obj]).unwrap();
145 ///
146 /// assert_eq!(image.symbol("f"), Some(2));
147 /// assert_eq!(image.symbol("missing"), None);
148 /// ```
149 #[must_use]
150 pub fn symbol(&self, name: &str) -> Option<u64> {
151 self.symbols.get(name).copied()
152 }
153
154 /// Returns the symbol table as `(name, address)` pairs, sorted by name.
155 ///
156 /// # Examples
157 ///
158 /// ```
159 /// use linker_lang::{link, Object};
160 ///
161 /// let mut obj = Object::new("o");
162 /// obj.section(".text", [0u8; 8]);
163 /// obj.define("b", ".text", 4);
164 /// obj.define("a", ".text", 0);
165 /// let image = link(&[obj]).unwrap();
166 ///
167 /// let names: Vec<&str> = image.symbols().map(|(name, _)| name).collect();
168 /// assert_eq!(names, ["a", "b"]); // sorted, not insertion order
169 /// ```
170 pub fn symbols(&self) -> impl Iterator<Item = (&str, u64)> {
171 self.symbols
172 .iter()
173 .map(|(name, &addr)| (name.as_str(), addr))
174 }
175
176 /// Returns the address of the entry point, or `None` if the link was not configured
177 /// with one. See [`Linker::entry`](crate::Linker::entry).
178 #[must_use]
179 pub const fn entry(&self) -> Option<u64> {
180 self.entry
181 }
182
183 /// Returns the number of sections in the image.
184 #[must_use]
185 pub fn len(&self) -> usize {
186 self.sections.len()
187 }
188
189 /// Returns `true` if the image has no sections.
190 #[must_use]
191 pub fn is_empty(&self) -> bool {
192 self.sections.is_empty()
193 }
194}
195
196impl fmt::Display for Image {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 match self.entry {
199 Some(addr) => writeln!(f, "entry = {addr:#018x}")?,
200 None => writeln!(f, "entry = none")?,
201 }
202 for section in &self.sections {
203 writeln!(
204 f,
205 "{} @ {:#018x} ({} bytes)",
206 section.name,
207 section.address,
208 section.data.len()
209 )?;
210 }
211 if !self.symbols.is_empty() {
212 f.write_str("symbols:\n")?;
213 for (name, addr) in &self.symbols {
214 writeln!(f, " {addr:#018x} {name}")?;
215 }
216 }
217 Ok(())
218 }
219}
220
221#[cfg(test)]
222#[allow(
223 clippy::unwrap_used,
224 reason = "tests build known-valid links, so they cannot fail"
225)]
226mod tests {
227 use crate::{Linker, Object, link};
228 use alloc::string::ToString;
229 use alloc::vec::Vec;
230
231 #[test]
232 fn test_output_section_accessors_report_layout() {
233 let mut obj = Object::new("o");
234 obj.section(".text", [1, 2, 3, 4]);
235 let image = link(&[obj]).unwrap();
236
237 let text = image.section(".text").unwrap();
238 assert_eq!(text.name(), ".text");
239 assert_eq!(text.address(), 0);
240 assert_eq!(text.data(), &[1, 2, 3, 4]);
241 assert_eq!(text.len(), 4);
242 assert!(!text.is_empty());
243 }
244
245 #[test]
246 fn test_symbols_iterate_in_name_order() {
247 let mut obj = Object::new("o");
248 obj.section(".text", [0u8; 8]);
249 obj.define("zebra", ".text", 0);
250 obj.define("alpha", ".text", 4);
251 let image = link(&[obj]).unwrap();
252
253 let names: Vec<&str> = image.symbols().map(|(n, _)| n).collect();
254 assert_eq!(names, ["alpha", "zebra"]);
255 }
256
257 #[test]
258 fn test_display_renders_a_link_map() {
259 let mut obj = Object::new("o");
260 obj.section(".text", [0u8; 4]);
261 obj.define("main", ".text", 0);
262 let image = Linker::new().entry("main").link(&[obj]).unwrap();
263
264 let map = image.to_string();
265 assert!(map.contains("entry = 0x0000000000000000"));
266 assert!(map.contains(".text @ 0x0000000000000000 (4 bytes)"));
267 assert!(map.contains("0x0000000000000000 main"));
268 }
269
270 #[test]
271 fn test_display_without_entry_says_none() {
272 let mut obj = Object::new("o");
273 obj.section(".text", [0u8; 1]);
274 let image = link(&[obj]).unwrap();
275 assert!(image.to_string().contains("entry = none"));
276 }
277
278 #[test]
279 fn test_empty_link_is_an_empty_image() {
280 let image = link(&[]).unwrap();
281 assert!(image.is_empty());
282 assert_eq!(image.len(), 0);
283 assert_eq!(image.entry(), None);
284 assert_eq!(image.symbols().count(), 0);
285 }
286}