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 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 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 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}