libpcode/
arch.rs

1use crate::sleigh::address::*;
2use crate::sleigh::varnode::*;
3use crate::sleigh::pcode::PcodeOp;
4use crate::sleigh::opcode::OpCode;
5use crate::sleigh::op::op_matches;
6
7use std::collections::{HashSet, HashMap};
8use roxmltree::{Document, Node};
9
10use std::path::{Path, PathBuf};
11use std::env;
12use std::fs;
13
14#[derive(Eq, PartialEq, Debug, Clone)]
15pub struct Prototype {
16    pub name: String,
17    pub extrapop: u64,
18    pub stackshift: u64,
19    pub inputs: HashMap<String, Vec<Varnode>>,
20    pub outputs: HashMap<String, Vec<Varnode>>,
21    pub killed: HashSet<Varnode>,
22    pub unaff: HashSet<Varnode>,
23}
24
25#[derive(Eq, PartialEq, Debug, Clone)]
26pub struct CompilerSpec {
27    pub stack_pointer: Varnode,
28    pub return_address: Varnode,
29    pub default_proto: String,
30    pub prototypes: HashMap<String, Prototype>,
31}
32
33#[derive(Eq, PartialEq, Debug, Clone)]
34pub struct ProcessorSpec {
35    pub pc_reg: Varnode,
36    pub defaults: HashMap<String, u32>,
37}
38
39#[derive(Eq, PartialEq, Debug, Clone)]
40pub struct Language {
41    pub name: String,
42    pub pspec: ProcessorSpec,
43    pub cspecs: HashMap<String, CompilerSpec>,
44    pub cspec: CompilerSpec,
45}
46
47fn parse_int(input: &str) -> u64 {
48    if let Some(stripped) = input.strip_prefix("0x") {
49        u64::from_str_radix(stripped, 16).unwrap()
50    } else if let Some(stripped) = input.strip_prefix("0b") {
51        u64::from_str_radix(stripped, 2).unwrap()
52    } else {
53        input.parse().unwrap()
54    }
55}
56
57fn find<'a, 'b>(node: &Node<'a, 'b>, tag: &str) -> Option<Node<'a, 'b>> {
58    node.children().find(|&child| child.tag_name().name() == tag)
59}
60
61fn find_rec<'a, 'b>(node: &Node<'a, 'b>, tag: &str) -> Option<Node<'a, 'b>> {
62    node.descendants().find(|&child| child.tag_name().name() == tag)
63}
64
65fn parse_varnode_tag(varnode_tag: &Node, registers: &HashMap<String, Varnode>) -> Varnode {
66    match varnode_tag.tag_name().name() {
67        "register" => {
68            let name = varnode_tag.attribute("name").unwrap().to_string();
69            registers[&name].clone()
70        },
71        "varnode" => Varnode {
72            name: None,
73            space: varnode_tag.attribute("space").unwrap().into(),
74            offset: parse_int(varnode_tag.attribute("offset").unwrap()),
75            size: parse_int(varnode_tag.attribute("size").unwrap()),
76        },
77        _ => unimplemented!("{}", varnode_tag.tag_name().name()),
78    }
79}
80
81fn parse_pentries(pentries: Option<&Node>, registers: &HashMap<String, Varnode>) -> HashMap<String, Vec<Varnode>> {
82    let mut entries: HashMap<String, Vec<Varnode>> = HashMap::default();
83
84    if let Some(pentries) = pentries {
85        for pentry in pentries.children() {
86            if pentry.tag_name().name() != "pentry" {
87                continue;
88            }
89            let metatype = pentry.attribute("metatype").unwrap_or("int").to_string();
90
91            // TODO: Handle stack/joined parameters.
92            if let Some(varnode_tag) = find(&pentry, "register") {
93                let varnode = parse_varnode_tag(&varnode_tag, registers);
94                entries.entry(metatype).or_default().push(varnode);
95            }
96        }
97    }
98
99    entries
100}
101
102fn parse_varnodes(varnode_tags: Option<&Node>, registers: &HashMap<String, Varnode>) -> HashSet<Varnode> {
103    let mut varnodes = HashSet::default();
104
105    if let Some(varnode_tags) = varnode_tags {
106        for varnode_tag in varnode_tags.children() {
107            if varnode_tag.tag_name().name().is_empty() {
108                continue;
109            }
110            let varnode = parse_varnode_tag(&varnode_tag, registers);
111
112            for off in varnode.offset..varnode.offset + varnode.size {
113                varnodes.insert(varnode.atom(off));
114            }
115        }
116    }
117
118    varnodes
119}
120
121impl Prototype {
122    pub fn new(elem: &Node, registers: &HashMap<String, Varnode>) -> Self {
123        let name = elem.attribute("name").unwrap_or_default().to_string();
124        let extrapop = elem.attribute("extrapop").unwrap_or_default().parse().unwrap_or_default();
125        let stackshift = elem.attribute("stackshift").unwrap_or_default().parse().unwrap_or_default();
126
127        let inputs = parse_pentries(find(elem, "input").as_ref(), registers);
128        let outputs = parse_pentries(find(elem, "output").as_ref(), registers);
129        let killed = parse_varnodes(find(elem, "killedbycall").as_ref(), registers);
130        let unaff = parse_varnodes(find(elem, "unaffected").as_ref(), registers);
131
132        Prototype { name, extrapop, stackshift, inputs, outputs, killed, unaff }
133    }
134}
135
136impl CompilerSpec {
137    fn new(cspec_elem: &Node, registers: &HashMap<String, Varnode>) -> Self {
138        let sp_name = find_rec(cspec_elem, "stackpointer").unwrap().attribute("register").unwrap().to_string();
139        let stack_pointer = registers[&sp_name].clone();
140
141        let ra_elem = find_rec(cspec_elem, "returnaddress").unwrap();
142        let return_address = {
143            let mut res = None;
144            for child in ra_elem.children() {
145                if !child.tag_name().name().is_empty() {
146                    res = Some(parse_varnode_tag(&child, registers));
147                    break;
148                }
149            }
150            res.unwrap()
151        };
152
153        let proto_elem = find_rec(cspec_elem, "default_proto").unwrap();
154        let proto_elem = find(&proto_elem, "prototype").unwrap();
155        let default_proto = Prototype::new(&proto_elem, registers);
156
157        let mut prototypes = HashMap::default();
158        prototypes.insert(default_proto.name.clone(), default_proto.clone());
159
160        for proto_elem in cspec_elem.descendants() {
161            if proto_elem.tag_name().name() != "prototype" {
162                continue;
163            }
164            let proto = Prototype::new(&proto_elem, registers);
165            prototypes.insert(proto.name.clone(), proto.clone());
166        }
167
168        Self {
169            stack_pointer,
170            return_address,
171            default_proto: default_proto.name.clone(),
172            prototypes,
173        }
174    }
175
176    pub fn default_proto(&self) -> &Prototype {
177        &self.prototypes[&self.default_proto]
178    }
179
180    pub fn is_call_setup(&self, mut ops: &[PcodeOp], ft: u64) -> bool {
181        let sp = &self.stack_pointer;
182        let ra = &self.return_address;
183
184        let shift = Varnode::constant(self.default_proto().stackshift, sp.size);
185        let ft = Varnode::constant(ft, ra.size);
186
187        if shift.offset > 0 {
188            if op_matches!(ops[0], (sp => IntSub sp, &shift)) {
189                ops = &ops[1..];
190            } else {
191                return false;
192            }
193        }
194
195        if ra.space == AddressSpace::Stack {
196            op_matches!(ops[0], (Store sp, &ft))
197        } else {
198            op_matches!(ops[0], (ra => Copy &ft))
199        }
200    }
201}
202
203impl ProcessorSpec {
204    fn new(elem: &Node, reg_names: &HashMap<String, Varnode>) -> Self {
205        let pc_reg = reg_names[find_rec(elem, "programcounter").unwrap().attribute("register").unwrap()].clone();
206        let mut defaults: HashMap<String, u32> = HashMap::default();
207
208        if let Some(ctx_data_elem) = find_rec(elem, "context_data") &&
209            let Some(ctx_set_elem) = find(&ctx_data_elem, "context_set")
210        {
211            for var_elem in ctx_set_elem.children() {
212                if var_elem.tag_name().name() != "set" {
213                    continue;
214                }
215                if let (Some(name), Some(val)) = (var_elem.attribute("name"), var_elem.attribute("val")) {
216                    defaults.insert(name.to_string(), parse_int(val) as u32);
217                }
218            }
219        }
220
221        Self { pc_reg, defaults }
222    }
223}
224
225pub fn read_file(path: PathBuf, _root: &Path) -> String {
226    fs::read_to_string(path.to_str().unwrap()).expect("Could not read file")
227}
228
229pub fn get_sla(arch_name: &str, language_id: &str) -> Option<String> {
230    let ghidra_root_path = Path::new(file!()).parent().unwrap().parent().unwrap();
231
232    let arch_path = ghidra_root_path.join("Ghidra")
233                                    .join("Processors")
234                                    .join(arch_name)
235                                    .join("data")
236                                    .join("languages");
237
238    // TODO: Make wasm search ldef paths.
239    let ldef_path = arch_path.join(format!("{}.ldefs", arch_name));
240    let ldef_contents = read_file(ldef_path, &arch_path);
241
242    let ldef = Document::parse(&ldef_contents).unwrap();
243    let ldef_elem = find(&ldef.root(), "language_definitions").unwrap();
244
245    for language_elem in ldef_elem.children() {
246        if language_elem.tag_name().name() != "language" {
247            continue;
248        }
249        if let (Some(lang_id), Some(sla_file)) = (language_elem.attribute("id"), language_elem.attribute("slafile")) {
250            if lang_id.eq(language_id) {
251                let sla = read_file(arch_path.join(sla_file), &arch_path);
252                return Some(sla);
253            }
254        }
255    }
256
257    None
258}
259
260pub fn get_language(
261    arch_name: &str,
262    language_id: &str,
263    compiler_id: &str,
264    registers: &HashMap<String, Varnode>,
265) -> Option<Language> {
266    let ghidra_path_env = env::var("GHIDRA_PATH");
267
268    let ghidra_root_path = match &ghidra_path_env {
269        Ok(p) => Path::new(p),
270        Err(_) => Path::new(file!()).parent().unwrap().parent().unwrap(),
271    };
272
273    let arch_path = ghidra_root_path.join("Ghidra")
274                                    .join("Processors")
275                                    .join(arch_name)
276                                    .join("data")
277                                    .join("languages");
278
279    // TODO: Make wasm search ldef paths.
280    let ldef_path = arch_path.join(format!("{}.ldefs", arch_name));
281    let ldef_contents = read_file(ldef_path, &arch_path);
282
283    let ldef = Document::parse(&ldef_contents).unwrap();
284    let ldef_elem = find(&ldef.root(), "language_definitions").unwrap();
285
286    for language_elem in ldef_elem.children() {
287        if language_elem.tag_name().name() != "language" {
288            continue;
289        }
290        if let Some(lang_id) = language_elem.attribute("id") {
291            if lang_id.eq(language_id) {
292                let language = Language::new(&arch_path, &language_elem, compiler_id, registers);
293                return Some(language);
294            }
295        }
296    }
297
298    None
299}
300
301impl Language {
302    pub fn new(
303        arch_path: &Path,
304        lang: &Node,
305        compiler_id: &str,
306        registers: &HashMap<String, Varnode>,
307    ) -> Self {
308        let name = lang.attribute("id").unwrap().to_string();
309
310        let pspec_filename = lang.attribute("processorspec").unwrap();
311        let pspec_path = arch_path.join(pspec_filename);
312        let pspec_contents =
313            fs::read_to_string(pspec_path.to_str().unwrap()).expect("Could not read pspec");
314
315        let pspec_elem = Document::parse(&pspec_contents).unwrap();
316        let pspec = ProcessorSpec::new(&pspec_elem.root(), registers);
317
318        let mut cspecs: HashMap<String, CompilerSpec> = HashMap::default();
319
320        for compiler_elem in lang.children() {
321            if compiler_elem.tag_name().name() != "compiler" {
322                continue;
323            }
324            let cspec_name = compiler_elem.attribute("name").unwrap();
325            let cspec_filename = compiler_elem.attribute("spec").unwrap();
326            let cspec_path = arch_path.join(cspec_filename);
327            let cspec_contents =
328                fs::read_to_string(cspec_path.to_str().unwrap()).expect("Could not read cspec");
329
330            let cspec_elem = Document::parse(&cspec_contents).unwrap();
331            let cspec = CompilerSpec::new(&cspec_elem.root(), registers);
332            cspecs.insert(cspec_name.to_string(), cspec);
333        }
334
335        let cspec = cspecs[compiler_id].clone();
336        Self { name, pspec, cspecs, cspec, }
337    }
338}