Skip to main content

binate_core/
binary.rs

1use crate::error::Result;
2use crate::symbol::SymbolTable;
3use memmap2::Mmap;
4use object::{Architecture, BinaryFormat, Object, ObjectSection, SectionKind};
5use std::fs::File;
6use std::path::{Path, PathBuf};
7use std::sync::Arc;
8
9/// An anonymous in-memory path label used when constructing from raw bytes.
10const IN_MEMORY_PATH: &str = "<in-memory>";
11
12pub struct SectionInfo {
13    pub name: String,
14    pub kind: SectionKind,
15    pub address: u64,
16    pub file_range: Option<(u64, u64)>,
17}
18
19pub trait BinaryProvider: Send + Sync {
20    fn format(&self) -> BinaryFormat;
21    fn architecture(&self) -> Architecture;
22    /// Returns (data, base_address, kind) for a named section.
23    fn section_data(&self, name: &str) -> Option<(&[u8], u64, SectionKind)>;
24    fn symbol_table(&self) -> &SymbolTable;
25    fn path(&self) -> &Path;
26    fn sections(&self) -> &[SectionInfo];
27    /// Iterate over all sections yielding (name, kind, address, data).
28    fn visit_sections(&self, visitor: &mut dyn FnMut(&str, SectionKind, u64, &[u8]) -> Result<()>) -> Result<()>;
29}
30
31struct ParsedSection {
32    name: String,
33    kind: SectionKind,
34    address: u64,
35    offset: usize,
36    size: usize,
37}
38
39pub struct MmapBinaryProvider {
40    path: PathBuf,
41    mmap: Arc<Mmap>,
42    sections: Vec<ParsedSection>,
43    symbols: SymbolTable,
44    format: BinaryFormat,
45    arch: Architecture,
46    section_infos: Vec<SectionInfo>,
47}
48
49impl MmapBinaryProvider {
50    /// Open a binary from a file path. The file is memory-mapped; no full read occurs.
51    pub fn open(path: &Path) -> Result<Self> {
52        let file = File::open(path)?;
53        let mmap = unsafe { Mmap::map(&file)? };
54        Self::from_mmap(mmap, path.to_owned())
55    }
56
57    /// Parse a binary from raw bytes already in memory.
58    /// The path is recorded as `<in-memory>` in `DiffResult`.
59    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
60        // Copy into an anonymous mmap so we can hand out `&[u8]` slices safely.
61        let mut mmap = memmap2::MmapMut::map_anon(bytes.len().max(1))?;
62        mmap[..bytes.len()].copy_from_slice(bytes);
63        let mmap = mmap.make_read_only()?;
64        Self::from_mmap(mmap, PathBuf::from(IN_MEMORY_PATH))
65    }
66
67    fn from_mmap(mmap: Mmap, path: PathBuf) -> Result<Self> {
68        let obj = object::File::parse(&*mmap)?;
69
70        let format = obj.format();
71        let arch = obj.architecture();
72        let symbols = SymbolTable::build(&obj)?;
73
74        let mut sections = Vec::new();
75        let mut section_infos = Vec::new();
76
77        for section in obj.sections() {
78            let name = section.name().unwrap_or("").to_owned();
79            let kind = section.kind();
80            let address = section.address();
81            let file_range = section.file_range();
82
83            let (offset, size) = match file_range {
84                Some((off, sz)) => (off as usize, sz as usize),
85                None => {
86                    section_infos.push(SectionInfo {
87                        name: name.clone(),
88                        kind,
89                        address,
90                        file_range: None,
91                    });
92                    sections.push(ParsedSection {
93                        name,
94                        kind,
95                        address,
96                        offset: 0,
97                        size: 0,
98                    });
99                    continue;
100                }
101            };
102
103            section_infos.push(SectionInfo {
104                name: name.clone(),
105                kind,
106                address,
107                file_range: Some((offset as u64, size as u64)),
108            });
109            sections.push(ParsedSection {
110                name,
111                kind,
112                address,
113                offset,
114                size,
115            });
116        }
117
118        Ok(Self {
119            path,
120            mmap: Arc::new(mmap),
121            sections,
122            symbols,
123            format,
124            arch,
125            section_infos,
126        })
127    }
128}
129
130impl BinaryProvider for MmapBinaryProvider {
131    fn format(&self) -> BinaryFormat {
132        self.format
133    }
134
135    fn architecture(&self) -> Architecture {
136        self.arch
137    }
138
139    fn section_data(&self, name: &str) -> Option<(&[u8], u64, SectionKind)> {
140        self.sections.iter().find(|s| s.name == name).and_then(|s| {
141            if s.size == 0 {
142                return None;
143            }
144            let end = s.offset + s.size;
145            if end > self.mmap.len() {
146                return None;
147            }
148            Some((&self.mmap[s.offset..end], s.address, s.kind))
149        })
150    }
151
152    fn visit_sections(&self, visitor: &mut dyn FnMut(&str, SectionKind, u64, &[u8]) -> Result<()>) -> Result<()> {
153        for s in &self.sections {
154            if s.size == 0 {
155                continue;
156            }
157            let end = s.offset + s.size;
158            if end > self.mmap.len() {
159                continue;
160            }
161            visitor(&s.name, s.kind, s.address, &self.mmap[s.offset..end])?;
162        }
163        Ok(())
164    }
165
166    fn symbol_table(&self) -> &SymbolTable {
167        &self.symbols
168    }
169
170    fn path(&self) -> &Path {
171        &self.path
172    }
173
174    fn sections(&self) -> &[SectionInfo] {
175        &self.section_infos
176    }
177}