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}