metaemu_loader/
lib.rs

1pub use either::Either;
2
3use fugue::bytes::Order;
4use fugue_db::{Database, DatabaseImporter, Segment};
5use fugue::ir::{LanguageDB, Translator};
6use fugue::ir::convention::Convention;
7
8use metaemu_state::flat::FlatState;
9use metaemu_state::paged::{PagedState, Segment as LoadedSegment};
10use metaemu_state::pcode::PCodeState;
11
12#[cfg(feature = "idapro")]
13use fugue_idapro as idapro;
14#[cfg(feature = "ghidra")]
15use fugue_ghidra as ghidra;
16#[cfg(feature = "radare")]
17use fugue_radare as radare;
18
19use std::path::Path;
20use std::sync::Arc;
21
22use thiserror::Error;
23
24#[derive(Debug, Error)]
25pub enum Error {
26    #[error("database import: {0}")]
27    Import(#[from] fugue_db::Error),
28}
29
30pub trait LoaderMapping<S> {
31    fn database(&self) -> Option<Arc<Database>> {
32        None
33    }
34
35    fn translator(&self) -> Arc<Translator>;
36    fn into_state(self) -> S;
37}
38
39#[derive(Clone)]
40pub struct MappedDatabase<S> {
41    database: Arc<Database>,
42    state: S,
43    translator: Arc<Translator>,
44}
45
46impl MappedDatabase<PagedState<u8>> {
47    pub fn from_database_with<F>(database: Database, mut segment_filter: F) -> Self
48    where F: FnMut(&Segment) -> bool {
49        let translator = database.default_translator();
50        let space = translator.manager().default_space();
51        let mut backing = Vec::default();
52        let ivt = database.segments().iter(..).filter(|(_, v)| segment_filter(v)).map(|(k, v)| {
53            let kv = (translator.address(k.start).into()..translator.address(1 + k.end).into(),
54                      LoadedSegment::new(v.name(), backing.len()));
55
56            backing.extend_from_slice(v.bytes());
57
58            let diff = (1 + k.end - k.start) as usize;
59            if v.bytes().len() < diff {
60                let to_add = diff - v.bytes().len();
61                backing.resize_with(backing.len() + to_add, Default::default);
62            }
63
64            kv
65        }).collect::<Vec<_>>();
66
67        let flat = FlatState::from_vec(space, backing);
68        let state = PagedState::from_parts(ivt.into_iter(), flat);
69
70        Self {
71            database: Arc::new(database),
72            translator: Arc::new(translator),
73            state,
74        }
75    }
76
77    pub fn from_database(database: Database) -> Self {
78        Self::from_database_with(database, |_| true)
79    }
80
81    pub fn from_path_with<P, F>(path: P, language_db: &LanguageDB, segment_filter: F) -> Result<Self, Error>
82    where P: AsRef<Path>,
83          F: FnMut(&Segment) -> bool {
84        #[allow(unused_mut)]
85        let mut dbi = DatabaseImporter::new(path);
86
87        #[cfg(feature = "idapro")]
88        dbi.register_backend(idapro::IDA::new().unwrap_or_default());
89
90        #[cfg(feature = "ghidra")]
91        dbi.register_backend(ghidra::Ghidra::new().unwrap_or_default());
92
93        #[cfg(feature = "radare")]
94        dbi.register_backend(radare::Radare::new().unwrap_or_default());
95
96        let db = dbi.import(language_db)?;
97
98        Ok(Self::from_database_with(db, segment_filter))
99    }
100
101    pub fn from_path<P>(path: P, language_db: &LanguageDB) -> Result<Self, Error>
102    where P: AsRef<Path> {
103        Self::from_path_with(path, language_db, |_| true)
104    }
105
106    pub fn pcode_state<O: Order>(self, convention: &Convention) -> MappedDatabase<PCodeState<u8, O>> {
107        MappedDatabase {
108            state: PCodeState::new(self.state, &self.translator, convention),
109            database: self.database,
110            translator: self.translator,
111        }
112    }
113
114    pub fn pcode_state_with<O: Order, C: AsRef<str>>(self, convention: C) -> Either<MappedDatabase<PCodeState<u8, O>>, Self> {
115        let convention = convention.as_ref();
116        if let Some(convention) = self.translator.compiler_conventions().get(convention) {
117            Either::Left(MappedDatabase {
118                state: PCodeState::new(self.state, &self.translator, convention),
119                database: self.database,
120                translator: self.translator,
121            })
122        } else {
123            Either::Right(self)
124        }
125    }
126}
127
128impl<S> LoaderMapping<S> for MappedDatabase<S> {
129    fn database(&self) -> Option<Arc<Database>> {
130        Some(self.database.clone())
131    }
132
133    fn translator(&self) -> Arc<Translator> {
134        self.translator.clone()
135    }
136
137    fn into_state(self) -> S {
138        self.state
139    }
140}