linker_layout/
lib.rs

1//! This crate defines a format for providing information about where a linker put stuff.
2
3use anyhow::Context;
4use anyhow::Result;
5use serde::Deserialize;
6use serde::Serialize;
7use std::io::Write;
8use std::ops::Range;
9use std::path::Path;
10use std::path::PathBuf;
11
12#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
13pub struct Layout {
14    /// The input files to the linker.
15    pub files: Vec<InputFile>,
16}
17
18#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
19pub struct InputFile {
20    /// Path to the input file on disk. In case of archives, multiple inputs may have the same path.
21    pub path: PathBuf,
22
23    /// If the input is an archive, then contains information about where in the archive the file
24    /// came from.
25    pub archive_entry: Option<ArchiveEntryInfo>,
26
27    /// Sections that were written to the output. Indexes correspond to the sections in the input
28    /// file. Contains None for sections that were discarded or weren't fully copied.
29    pub sections: Vec<Option<Section>>,
30}
31
32#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
33pub struct ArchiveEntryInfo {
34    /// The range within the file that contains the archive entry (not including the entry header).
35    pub range: Range<usize>,
36
37    pub identifier: Vec<u8>,
38}
39
40#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
41pub struct Section {
42    pub mem_range: Range<u64>,
43}
44
45impl Layout {
46    pub fn write(&self, writer: &mut impl Write) -> Result<()> {
47        postcard::to_io(self, writer)?;
48        Ok(())
49    }
50
51    pub fn to_bytes(&self) -> Result<Vec<u8>> {
52        Ok(postcard::to_stdvec(self)?)
53    }
54
55    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
56        postcard::from_bytes(bytes).context("Invalid linker layout")
57    }
58}
59
60#[must_use]
61pub fn layout_path(base_path: &Path) -> PathBuf {
62    // We always want to append, not use with_extension, since we don't want to remove any existing
63    // extension, otherwise we'd likely get collisions.
64    let mut s = base_path.as_os_str().to_owned();
65    s.push(".layout");
66    PathBuf::from(s)
67}
68
69impl std::fmt::Display for InputFile {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        self.path.display().fmt(f)?;
72        if let Some(e) = self.archive_entry.as_ref() {
73            write!(f, " @ {}", String::from_utf8_lossy(&e.identifier))?;
74        }
75        Ok(())
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn test_round_trip() {
85        let layout = Layout {
86            files: vec![InputFile {
87                path: PathBuf::new(),
88                archive_entry: None,
89                sections: vec![Some(Section { mem_range: 42..48 })],
90            }],
91        };
92        let bytes = layout.to_bytes().unwrap();
93        let layout2 = Layout::from_bytes(&bytes).unwrap();
94        assert_eq!(layout, layout2);
95    }
96}