Skip to main content

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}