fugue_ir/
convention.rs

1use ustr::Ustr;
2
3use crate::compiler::{self, CallFixup, Specification};
4use crate::deserialise::error::Error as DeserialiseError;
5use crate::disassembly::VarnodeData;
6use crate::register::RegisterNames;
7use crate::space::AddressSpace;
8use crate::space_manager::SpaceManager;
9
10use std::sync::Arc;
11
12#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
13pub enum PrototypeOperand {
14    Register {
15        name: Ustr,
16        varnode: VarnodeData,
17    },
18    RegisterJoin {
19        first_name: Ustr,
20        first_varnode: VarnodeData,
21        second_name: Ustr,
22        second_varnode: VarnodeData,
23    },
24    StackRelative(u64),
25}
26
27impl PrototypeOperand {
28    pub fn from_spec(
29        spec: &compiler::PrototypeOperand,
30        registers: &RegisterNames,
31    ) -> Result<Self, DeserialiseError> {
32        match spec {
33            compiler::PrototypeOperand::Register(ref name) => {
34                let (name, offset, size) = registers.get_by_name(&**name).ok_or_else(|| {
35                    DeserialiseError::Invariant("register for prototype operand invalid")
36                })?;
37                Ok(Self::Register {
38                    name: name.clone(),
39                    varnode: VarnodeData::new(registers.register_space(), offset, size),
40                })
41            }
42            compiler::PrototypeOperand::RegisterJoin(ref first_name, ref second_name) => {
43                let (first_name, foffset, fsize) =
44                    registers.get_by_name(&**first_name).ok_or_else(|| {
45                        DeserialiseError::Invariant("register for prototype operand invalid")
46                    })?;
47
48                let (second_name, soffset, ssize) =
49                    registers.get_by_name(&**second_name).ok_or_else(|| {
50                        DeserialiseError::Invariant("register for prototype operand invalid")
51                    })?;
52
53                Ok(Self::RegisterJoin {
54                    first_name: first_name.clone(),
55                    first_varnode: VarnodeData::new(registers.register_space(), foffset, fsize),
56                    second_name: second_name.clone(),
57                    second_varnode: VarnodeData::new(registers.register_space(), soffset, ssize),
58                })
59            }
60            compiler::PrototypeOperand::StackRelative(offset) => Ok(Self::StackRelative(*offset)),
61        }
62    }
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
66pub struct PrototypeEntry {
67    min_size: usize,
68    max_size: usize,
69    alignment: u64,
70    meta_type: Option<String>,
71    extension: Option<String>,
72    operand: PrototypeOperand,
73}
74
75impl PrototypeEntry {
76    pub fn from_spec(
77        spec: &compiler::PrototypeEntry,
78        registers: &RegisterNames,
79    ) -> Result<Self, DeserialiseError> {
80        Ok(Self {
81            min_size: spec.min_size,
82            max_size: spec.max_size,
83            alignment: spec.alignment,
84            meta_type: spec.meta_type.clone(),
85            extension: spec.extension.clone(),
86            operand: PrototypeOperand::from_spec(&spec.operand, registers)?,
87        })
88    }
89
90    pub fn min_size(&self) -> usize {
91        self.min_size
92    }
93
94    pub fn max_size(&self) -> usize {
95        self.max_size
96    }
97
98    pub fn alignment(&self) -> u64 {
99        self.alignment
100    }
101
102    pub fn meta_type(&self) -> &Option<String> {
103        &self.meta_type
104    }
105
106    pub fn extension(&self) -> &Option<String> {
107        &self.extension
108    }
109
110    pub fn operand(&self) -> &PrototypeOperand {
111        &self.operand
112    }
113}
114
115#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
116pub struct Prototype {
117    name: String,
118    extra_pop: u64,
119    stack_shift: u64,
120    inputs: Vec<PrototypeEntry>,
121    outputs: Vec<PrototypeEntry>,
122    unaffected: Vec<PrototypeOperand>,
123    killed_by_call: Vec<PrototypeOperand>,
124    likely_trashed: Vec<PrototypeOperand>,
125}
126
127impl Prototype {
128    pub fn from_spec(
129        spec: &compiler::Prototype,
130        registers: &RegisterNames,
131    ) -> Result<Self, DeserialiseError> {
132        Ok(Self {
133            name: spec.name.clone(),
134            extra_pop: spec.extra_pop,
135            stack_shift: spec.stack_shift,
136            inputs: spec
137                .inputs
138                .iter()
139                .map(|input| PrototypeEntry::from_spec(input, registers))
140                .collect::<Result<_, _>>()?,
141            outputs: spec
142                .outputs
143                .iter()
144                .map(|output| PrototypeEntry::from_spec(output, registers))
145                .collect::<Result<_, _>>()?,
146            unaffected: spec
147                .unaffected
148                .iter()
149                .map(|unaffected| PrototypeOperand::from_spec(unaffected, registers))
150                .collect::<Result<_, _>>()?,
151            killed_by_call: spec
152                .killed_by_call
153                .iter()
154                .map(|killed| PrototypeOperand::from_spec(killed, registers))
155                .collect::<Result<_, _>>()?,
156            likely_trashed: spec
157                .likely_trashed
158                .iter()
159                .map(|trashed| PrototypeOperand::from_spec(trashed, registers))
160                .collect::<Result<_, _>>()?,
161        })
162    }
163
164    pub fn name(&self) -> &str {
165        &self.name
166    }
167
168    pub fn extra_pop(&self) -> u64 {
169        self.extra_pop
170    }
171
172    pub fn stack_shift(&self) -> u64 {
173        self.stack_shift
174    }
175
176    pub fn inputs(&self) -> &[PrototypeEntry] {
177        &self.inputs
178    }
179
180    pub fn outputs(&self) -> &[PrototypeEntry] {
181        &self.outputs
182    }
183
184    pub fn unaffected(&self) -> &[PrototypeOperand] {
185        &self.unaffected
186    }
187
188    pub fn killed_by_call(&self) -> &[PrototypeOperand] {
189        &self.killed_by_call
190    }
191
192    pub fn likely_trashed(&self) -> &[PrototypeOperand] {
193        &self.likely_trashed
194    }
195}
196
197#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
198pub enum ReturnAddress {
199    Register { name: Ustr, varnode: VarnodeData },
200    StackRelative { offset: u64, size: usize },
201}
202
203impl ReturnAddress {
204    pub fn from_spec(
205        spec: &compiler::ReturnAddress,
206        registers: &RegisterNames,
207    ) -> Result<Self, DeserialiseError> {
208        match spec {
209            compiler::ReturnAddress::Register(ref name) => {
210                let (name, offset, size) = registers.get_by_name(&**name).ok_or_else(|| {
211                    DeserialiseError::Invariant("register for return address invalid")
212                })?;
213                Ok(Self::Register {
214                    name: name.clone(),
215                    varnode: VarnodeData::new(registers.register_space(), offset, size),
216                })
217            }
218            compiler::ReturnAddress::StackRelative { offset, size } => Ok(Self::StackRelative {
219                offset: *offset,
220                size: *size,
221            }),
222        }
223    }
224}
225
226#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
227pub struct StackPointer {
228    name: Ustr,
229    varnode: VarnodeData,
230    space: Arc<AddressSpace>,
231}
232
233impl StackPointer {
234    pub fn from_spec(
235        spec: &compiler::StackPointer,
236        registers: &RegisterNames,
237        manager: &SpaceManager,
238    ) -> Result<Self, DeserialiseError> {
239        let space = manager.space_by_name(&spec.space).ok_or_else(|| {
240            DeserialiseError::Invariant("stack pointer space for convention invalid")
241        })?;
242        let (name, offset, size) = registers
243            .get_by_name(&*spec.register)
244            .ok_or_else(|| DeserialiseError::Invariant("named stack pointer invalid"))?;
245
246        Ok(Self {
247            name: name.clone(),
248            varnode: VarnodeData::new(registers.register_space(), offset, size),
249            space,
250        })
251    }
252
253    pub fn varnode(&self) -> &VarnodeData {
254        &self.varnode
255    }
256}
257
258#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
259pub struct Convention {
260    name: String,
261    data_organisation: Option<compiler::DataOrganisation>,
262    stack_pointer: StackPointer,
263    return_address: ReturnAddress,
264    default_prototype: Prototype,
265    additional_prototypes: Vec<Prototype>,
266    call_fixups: Vec<CallFixup>,
267}
268
269impl Convention {
270    pub fn from_spec(
271        spec: &Specification,
272        registers: &RegisterNames,
273        manager: &SpaceManager,
274    ) -> Result<Self, DeserialiseError> {
275        Ok(Self {
276            name: spec.name.clone(),
277            data_organisation: spec.data_organisation.clone(),
278            stack_pointer: StackPointer::from_spec(&spec.stack_pointer, registers, manager)?,
279            return_address: ReturnAddress::from_spec(&spec.return_address, registers)?,
280            default_prototype: Prototype::from_spec(&spec.default_prototype, registers)?,
281            additional_prototypes: spec
282                .additional_prototypes
283                .iter()
284                .map(|prototype| Prototype::from_spec(prototype, registers))
285                .collect::<Result<_, _>>()?,
286            call_fixups: spec.call_fixups.clone(),
287        })
288    }
289
290    pub fn name(&self) -> &str {
291        &self.name
292    }
293
294    pub fn stack_pointer(&self) -> &StackPointer {
295        &self.stack_pointer
296    }
297
298    pub fn return_address(&self) -> &ReturnAddress {
299        &self.return_address
300    }
301
302    pub fn default_prototype(&self) -> &Prototype {
303        &self.default_prototype
304    }
305
306    pub fn additional_prototypes(&self) -> &[Prototype] {
307        &self.additional_prototypes
308    }
309
310    pub fn prototypes(&self) -> impl Iterator<Item = &Prototype> {
311        std::iter::once(&self.default_prototype).chain(self.additional_prototypes.iter())
312    }
313
314    pub fn call_fixups(&self) -> &[CallFixup] {
315        &self.call_fixups
316    }
317}