fugue_ir/
processor.rs

1use crate::deserialise::error::Error as DeserialiseError;
2use crate::deserialise::parse::XmlExt;
3use crate::error::Error;
4
5use ahash::AHashMap as Map;
6
7use std::fs::File;
8use std::io::Read;
9use std::path::Path;
10
11#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
12pub struct Specification {
13    program_counter: String,
14    context_set: Map<String, u32>,
15    tracked_set: Map<String, u32>,
16}
17
18impl Specification {
19    pub fn program_counter(&self) -> &str {
20        self.program_counter.as_ref()
21    }
22
23    pub fn context_set(&self) -> impl Iterator<Item = (&str, u32)> {
24        self.context_set.iter().map(|(n, v)| (n.as_ref(), *v))
25    }
26
27    pub fn from_xml(input: xml::Node) -> Result<Self, DeserialiseError> {
28        // Example .pspec file
29        // <processor_spec>
30        // <properties>
31        //     <property key="addressesDoNotAppearDirectlyInCode" value="true"/>
32        //     <property key="allowOffcutReferencesToFunctionStarts" value="true"/>
33        //     <property key="useNewFunctionStackAnalysis" value="true"/>
34        //     <property key="enableSharedReturnAnalysis" value="false"/>
35        //     <property key="emulateInstructionStateModifierClass" value="ghidra.program.emulation.ARMEmulateInstructionStateModifier"/>
36        // </properties>
37        // <programcounter register="pc"/>
38        // <context_data>
39        //     <context_set space="ram">
40        //     <set name="LRset" val="0" description="0 lr reg not set, 1 for LR set, affects BX as a call"/>
41        //     </context_set>
42        //     <tracked_set space="ram">
43        //     <set name="spsr" val="0"/>
44        //     </tracked_set>
45        // </context_data>
46
47        // <default_symbols>
48        //     <symbol name="Reset" address="ram:0x0" entry="true"/>
49        //      ...
50        // </default_symbols>
51        // </processor_spec>
52        if input.tag_name().name() != "processor_spec" {
53            return Err(DeserialiseError::TagUnexpected(
54                input.tag_name().name().to_owned(),
55            ));
56        }
57
58        let mut program_counter = None;
59        let mut context_set = Map::default();
60        let mut tracked_set = Map::default();
61
62        for child in input.children().filter(xml::Node::is_element) {
63            match child.tag_name().name() {
64                "programcounter" => {
65                    program_counter = Some(child.attribute_string("register")?);
66                }
67                "context_data" => {
68                    for cchild in child.children().filter(xml::Node::is_element) {
69                        match cchild.tag_name().name() {
70                            "context_set" => {
71                                for ct in cchild.children().filter(xml::Node::is_element) {
72                                    context_set.insert(
73                                        ct.attribute_string("name")?,
74                                        ct.attribute_int::<u32>("val")?,
75                                    );
76                                }
77                            }
78                            "tracked_set" => {
79                                for ct in cchild.children().filter(xml::Node::is_element) {
80                                    tracked_set.insert(
81                                        ct.attribute_string("name")?,
82                                        ct.attribute_int::<u32>("val")?,
83                                    );
84                                }
85                            }
86                            _ => (),
87                        }
88                    }
89                }
90                _ => (),
91            }
92        }
93
94        if let Some(program_counter) = program_counter {
95            Ok(Self {
96                program_counter,
97                context_set,
98                tracked_set,
99            })
100        } else {
101            Err(DeserialiseError::Invariant(
102                "processor specification must define a program counter",
103            ))
104        }
105    }
106
107    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
108        let path = path.as_ref();
109        let mut file = File::open(path).map_err(|error| Error::ParseFile {
110            path: path.to_owned(),
111            error,
112        })?;
113
114        let mut input = String::new();
115        file.read_to_string(&mut input)
116            .map_err(|error| Error::ParseFile {
117                path: path.to_owned(),
118                error,
119            })?;
120
121        Self::from_str(&input).map_err(|error| Error::DeserialiseFile {
122            path: path.to_owned(),
123            error,
124        })
125    }
126
127    pub fn from_str<S: AsRef<str>>(input: S) -> Result<Self, DeserialiseError> {
128        let document = xml::Document::parse(input.as_ref()).map_err(DeserialiseError::Xml)?;
129
130        Self::from_xml(document.root_element())
131    }
132}