1use crate::deserialise::error::Error as DeserialiseError;
2use crate::deserialise::parse::XmlExt;
3use crate::error::Error;
4
5use ahash::AHashMap as Map;
6use ustr::UstrSet;
7
8use std::fs::File;
9use std::io::Read;
10use std::iter::FromIterator;
11use std::path::Path;
12
13#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
14pub struct DataOrganisation {
15 pub absolute_max_alignment: u64,
16 pub machine_alignment: u64,
17 pub default_alignment: u64,
18 pub default_pointer_alignment: u64,
19 pub pointer_size: usize,
20 pub wchar_size: usize,
21 pub short_size: usize,
22 pub integer_size: usize,
23 pub long_size: usize,
24 pub long_long_size: usize,
25 pub float_size: usize,
26 pub double_size: usize,
27 pub long_double_size: usize,
28 pub size_alignment_map: Map<usize, u64>,
29}
30
31impl Default for DataOrganisation {
32 fn default() -> Self {
33 Self {
34 absolute_max_alignment: 0,
35 machine_alignment: 1,
36 default_alignment: 1,
37 default_pointer_alignment: 4,
38 pointer_size: 4,
39 wchar_size: 2,
40 short_size: 2,
41 integer_size: 4,
42 long_size: 4,
43 long_long_size: 8,
44 float_size: 4,
45 double_size: 8,
46 long_double_size: 12,
47 size_alignment_map: Map::from_iter(vec![(1, 1), (2, 2), (4, 4), (8, 8)]),
48 }
49 }
50}
51
52impl DataOrganisation {
53 pub fn from_xml(input: xml::Node) -> Result<Self, DeserialiseError> {
54 if input.tag_name().name() != "data_organization" {
55 return Err(DeserialiseError::TagUnexpected(
56 input.tag_name().name().to_owned(),
57 ));
58 }
59
60 let mut data = Self::default();
61
62 for child in input.children().filter(xml::Node::is_element) {
63 match child.tag_name().name() {
64 "absolute_max_alignment" => {
65 data.absolute_max_alignment = child.attribute_int("value")?;
66 }
67 "machine_alignment" => {
68 data.machine_alignment = child.attribute_int("value")?;
69 }
70 "default_alignment" => {
71 data.default_alignment = child.attribute_int("value")?;
72 }
73 "default_pointer_alignment" => {
74 data.default_pointer_alignment = child.attribute_int("value")?;
75 }
76 "pointer_size" => {
77 data.pointer_size = child.attribute_int("value")?;
78 }
79 "wchar_size" => {
80 data.wchar_size = child.attribute_int("value")?;
81 }
82 "short_size" => {
83 data.short_size = child.attribute_int("value")?;
84 }
85 "integer_size" => {
86 data.integer_size = child.attribute_int("value")?;
87 }
88 "long_size" => {
89 data.long_size = child.attribute_int("value")?;
90 }
91 "long_long_size" => {
92 data.long_long_size = child.attribute_int("value")?;
93 }
94 "float_size" => {
95 data.float_size = child.attribute_int("value")?;
96 }
97 "double_size" => {
98 data.double_size = child.attribute_int("value")?;
99 }
100 "long_double_size" => {
101 data.long_double_size = child.attribute_int("value")?;
102 }
103 "size_alignment_map" => {
104 for entry in child
105 .children()
106 .filter(|e| e.is_element() && e.tag_name().name() == "entry")
107 {
108 data.size_alignment_map.insert(
109 entry.attribute_int("size")?,
110 entry.attribute_int("alignment")?,
111 );
112 }
113 }
114 _ => (),
115 }
116 }
117
118 Ok(data)
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
123pub struct StackPointer {
124 pub(crate) register: String,
125 pub(crate) space: String,
126}
127
128impl StackPointer {
129 pub fn from_xml(input: xml::Node) -> Result<Self, DeserialiseError> {
130 if input.tag_name().name() != "stackpointer" {
131 return Err(DeserialiseError::TagUnexpected(
132 input.tag_name().name().to_owned(),
133 ));
134 }
135
136 Ok(Self {
137 register: input.attribute_string("register")?,
138 space: input.attribute_string("space")?,
139 })
140 }
141
142 pub fn register(&self) -> &str {
143 &self.register
144 }
145
146 pub fn space(&self) -> &str {
147 &self.space
148 }
149}
150
151#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
152pub enum ReturnAddress {
153 Register(String),
154 StackRelative { offset: u64, size: usize },
155}
156
157impl ReturnAddress {
158 pub fn from_xml(input: xml::Node) -> Result<Self, DeserialiseError> {
159 if input.tag_name().name() != "returnaddress" {
160 return Err(DeserialiseError::TagUnexpected(
161 input.tag_name().name().to_owned(),
162 ));
163 }
164
165 let mut children = input.children().filter(xml::Node::is_element);
166
167 let node = children
168 .next()
169 .ok_or_else(|| DeserialiseError::Invariant("no children for returnaddress"))?;
170
171 match node.tag_name().name() {
172 "register" => Ok(Self::Register(node.attribute_string("name")?)),
173 "varnode"
174 if node
175 .attribute_string("space")
176 .map(|space| space == "stack")
177 .unwrap_or(false) =>
178 {
179 Ok(Self::StackRelative {
180 offset: node.attribute_int("offset")?,
181 size: node.attribute_int("size")?,
182 })
183 }
184 tag => Err(DeserialiseError::TagUnexpected(tag.to_owned())),
185 }
186 }
187}
188
189#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
190pub enum PrototypeOperand {
191 Register(String),
192 RegisterJoin(String, String),
193 StackRelative(u64),
194}
195
196impl PrototypeOperand {
197 pub fn from_xml(input: xml::Node) -> Result<Self, DeserialiseError> {
198 match input.tag_name().name() {
199 "addr" => match input.attribute_string("space")?.as_ref() {
200 "join" => Ok(Self::RegisterJoin(
201 input.attribute_string("piece1")?,
202 input.attribute_string("piece2")?,
203 )),
204 "stack" => Ok(Self::StackRelative(input.attribute_int("offset")?)),
205 tag => Err(DeserialiseError::TagUnexpected(tag.to_owned())),
206 },
207 "register" => Ok(Self::Register(input.attribute_string("name")?)),
208 tag => Err(DeserialiseError::TagUnexpected(tag.to_owned())),
209 }
210 }
211}
212
213#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
214pub struct PrototypeEntry {
215 pub(crate) killed_by_call: bool,
216 pub(crate) min_size: usize,
217 pub(crate) max_size: usize,
218 pub(crate) alignment: u64,
219 pub(crate) meta_type: Option<String>,
220 pub(crate) extension: Option<String>,
221 pub(crate) operand: PrototypeOperand,
222}
223
224impl PrototypeEntry {
225 pub fn from_xml(input: xml::Node, killed_by_call: bool) -> Result<Self, DeserialiseError> {
226 if input.tag_name().name() != "pentry" {
227 return Err(DeserialiseError::TagUnexpected(
228 input.tag_name().name().to_owned(),
229 ));
230 }
231
232 let min_size = input.attribute_int("minsize")?;
233 let max_size = input.attribute_int("maxsize")?;
234 let alignment = input.attribute_int_opt("alignment", 1)?;
235
236 let meta_type = input
237 .attribute_string("metatype")
238 .map(Some)
239 .unwrap_or_default();
240 let extension = input
241 .attribute_string("extension")
242 .map(Some)
243 .unwrap_or_default();
244
245 let node = input.children().filter(xml::Node::is_element).next();
246 if node.is_none() {
247 return Err(DeserialiseError::Invariant(
248 "compiler specification prototype entry does not define an operand",
249 ));
250 }
251
252 let operand = PrototypeOperand::from_xml(node.unwrap())?;
253
254 Ok(Self {
255 killed_by_call,
256 min_size,
257 max_size,
258 alignment,
259 meta_type,
260 extension,
261 operand,
262 })
263 }
264}
265
266#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
267pub struct Prototype {
268 pub(crate) name: String,
269 pub(crate) extra_pop: u64,
270 pub(crate) stack_shift: u64,
271 pub(crate) inputs: Vec<PrototypeEntry>,
272 pub(crate) outputs: Vec<PrototypeEntry>,
273 pub(crate) unaffected: Vec<PrototypeOperand>,
274 pub(crate) killed_by_call: Vec<PrototypeOperand>,
275 pub(crate) likely_trashed: Vec<PrototypeOperand>,
276}
277
278impl Prototype {
279 pub fn from_xml(input: xml::Node) -> Result<Self, DeserialiseError> {
280 if input.tag_name().name() != "prototype" {
281 return Err(DeserialiseError::TagUnexpected(
282 input.tag_name().name().to_owned(),
283 ));
284 }
285
286 let name = input.attribute_string("name")?;
287 let extra_pop = if matches!(input.attribute("extrapop"), Some("unknown")) {
288 0
289 } else {
290 input.attribute_int("extrapop")?
291 };
292 let stack_shift = input.attribute_int("stackshift")?;
293
294 let mut inputs = Vec::new();
295 let mut outputs = Vec::new();
296 let mut unaffected = Vec::new();
297 let mut killed_by_call = Vec::new();
298 let mut likely_trashed = Vec::new();
299
300 for child in input.children().filter(xml::Node::is_element) {
301 match child.tag_name().name() {
302 "input" => {
303 let mut values = child
304 .children()
305 .filter(xml::Node::is_element)
306 .map(|v| PrototypeEntry::from_xml(v, false))
307 .collect::<Result<Vec<_>, _>>()?;
308 inputs.append(&mut values);
309 }
310 "output" => {
311 let killed = child.attribute_bool("killedbycall").unwrap_or(false);
312 let mut values = child
313 .children()
314 .filter(xml::Node::is_element)
315 .map(|v| PrototypeEntry::from_xml(v, killed))
316 .collect::<Result<Vec<_>, _>>()?;
317 outputs.append(&mut values);
318 }
319 "unaffected" => {
320 let mut values = child
321 .children()
322 .filter(xml::Node::is_element)
323 .filter_map(|op| PrototypeOperand::from_xml(op).ok())
324 .collect::<Vec<_>>();
325 unaffected.append(&mut values);
326 }
327 "killedbycall" => {
328 let mut values = child
329 .children()
330 .filter(xml::Node::is_element)
331 .filter_map(|op| PrototypeOperand::from_xml(op).ok())
332 .collect::<Vec<_>>();
333 killed_by_call.append(&mut values);
334 }
335 "likelytrash" => {
336 let mut values = child
337 .children()
338 .filter(xml::Node::is_element)
339 .filter_map(|op| PrototypeOperand::from_xml(op).ok())
340 .collect::<Vec<_>>();
341 likely_trashed.append(&mut values);
342 }
343 _ => (),
344 }
345 }
346
347 Ok(Self {
348 name,
349 extra_pop,
350 stack_shift,
351 inputs,
352 outputs,
353 unaffected,
354 killed_by_call,
355 likely_trashed,
356 })
357 }
358}
359
360#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
361pub struct Specification {
362 pub(crate) name: String,
363 pub(crate) data_organisation: Option<DataOrganisation>,
364 pub(crate) stack_pointer: StackPointer,
365 pub(crate) return_address: ReturnAddress,
366 pub(crate) default_prototype: Prototype,
367 pub(crate) additional_prototypes: Vec<Prototype>,
368 pub(crate) call_fixups: Vec<CallFixup>,
369}
370
371impl Specification {
372 pub fn named_from_xml<N: Into<String>>(
373 name: N,
374 input: xml::Node,
375 ) -> Result<Self, DeserialiseError> {
376 if input.tag_name().name() != "compiler_spec" {
377 return Err(DeserialiseError::TagUnexpected(
378 input.tag_name().name().to_owned(),
379 ));
380 }
381
382 let mut data_organisation = None;
383 let mut stack_pointer = None;
384 let mut return_address = None;
385 let mut default_prototype = None;
386 let mut additional_prototypes = Vec::new();
387 let mut call_fixups = Vec::new();
388
389 for child in input.children().filter(xml::Node::is_element) {
390 match child.tag_name().name() {
391 "data_organization" => {
392 data_organisation = Some(DataOrganisation::from_xml(child)?);
393 }
394 "stackpointer" => {
395 stack_pointer = Some(StackPointer::from_xml(child)?);
396 }
397 "returnaddress" => {
398 return_address = Some(ReturnAddress::from_xml(child)?);
399 }
400 "default_proto" => {
401 let proto = child.children().filter(xml::Node::is_element).next();
402 if proto.is_none() {
403 return Err(DeserialiseError::Invariant(
404 "compiler specification does not define prototype for default prototype"
405 ));
406 }
407 default_prototype = Some(Prototype::from_xml(proto.unwrap())?);
408 }
409 "prototype" => {
410 additional_prototypes.push(Prototype::from_xml(child)?);
411 }
412 "callfixup" => {
413 call_fixups.push(CallFixup::from_xml(child)?);
414 }
415 _ => (),
416 }
417 }
418
419 if stack_pointer.is_none() {
426 return Err(DeserialiseError::Invariant(
427 "compiler specification does not define stack pointer configuration",
428 ));
429 }
430
431 if return_address.is_none() {
432 return Err(DeserialiseError::Invariant(
433 "compiler specification does not define return address",
434 ));
435 }
436
437 Ok(Self {
438 name: name.into(),
439 data_organisation,
440 stack_pointer: stack_pointer.unwrap(),
441 return_address: return_address.unwrap(),
442 default_prototype: default_prototype.unwrap(),
443 additional_prototypes,
444 call_fixups,
445 })
446 }
447
448 pub fn named_from_file<N: Into<String>, P: AsRef<Path>>(
449 name: N,
450 path: P,
451 ) -> Result<Self, Error> {
452 let path = path.as_ref();
453 let mut file = File::open(path).map_err(|error| Error::ParseFile {
454 path: path.to_owned(),
455 error,
456 })?;
457
458 let mut input = String::new();
459 file.read_to_string(&mut input)
460 .map_err(|error| Error::ParseFile {
461 path: path.to_owned(),
462 error,
463 })?;
464
465 Self::named_from_str(name, &input).map_err(|error| Error::DeserialiseFile {
466 path: path.to_owned(),
467 error,
468 })
469 }
470
471 pub fn named_from_str<N: Into<String>, S: AsRef<str>>(
472 name: N,
473 input: S,
474 ) -> Result<Self, DeserialiseError> {
475 let document = xml::Document::parse(input.as_ref()).map_err(DeserialiseError::Xml)?;
476
477 let res = Self::named_from_xml(name, document.root_element());
478
479 if let Err(ref e) = res {
480 log::debug!("load failed: {:?}", e);
481 }
482
483 res
484 }
485}
486
487#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
488pub struct CallFixup {
489 name: String,
490 shift: i64,
491 targets: UstrSet,
492 pcode: String,
493}
494
495impl CallFixup {
496 pub fn name(&self) -> &str {
497 &self.name
498 }
499
500 pub fn targets(&self) -> &UstrSet {
501 &self.targets
502 }
503
504 pub fn pcode(&self) -> &str {
505 &self.pcode
506 }
507
508 pub fn shift(&self) -> i64 {
509 self.shift
510 }
511}
512
513impl CallFixup {
514 pub fn from_xml(input: xml::Node) -> Result<Self, DeserialiseError> {
515 if input.tag_name().name() != "callfixup" {
516 return Err(DeserialiseError::TagUnexpected(
517 input.tag_name().name().to_owned(),
518 ));
519 }
520
521 let name = input.attribute_string("name")?;
522
523 let mut targets = UstrSet::default();
524 let mut pcode = None;
525 let mut shift = 0;
526
527 for child in input.children().filter(xml::Node::is_element) {
528 match child.tag_name().name() {
529 "target" => {
530 let name = child.attribute_string("name")?;
531 targets.insert(name.into());
532 }
533 "pcode" => {
534 if pcode.is_some() {
535 return Err(DeserialiseError::Invariant(
536 "call fixup has multiple bodies",
537 ));
538 }
539
540 let Some(elt) = child.first_element_child() else {
542 return Err(DeserialiseError::Invariant(
543 "call fixup body does not contain any injectable pcode",
544 ));
545 };
546
547 if elt.tag_name().name() != "body" {
548 return Err(DeserialiseError::TagUnexpected(
549 elt.tag_name().name().to_owned(),
550 ));
551 }
552
553 if let Some(text) = elt.text().map(ToOwned::to_owned) {
554 shift = child.attribute_int_opt("paramshift", 0i64)?;
555 pcode = Some(text);
556 }
557 }
558 _ => (),
559 }
560 }
561
562 if pcode.is_none() {
563 return Err(DeserialiseError::Invariant(
564 "call fixup does not define any injectable pcode",
565 ));
566 }
567
568 Ok(Self {
569 name,
570 targets,
571 shift,
572 pcode: pcode.unwrap(),
573 })
574 }
575}