1use std::collections::BTreeMap;
13use std::fmt;
14
15#[derive(Debug, Clone, PartialEq)]
17pub enum EvaluationResult {
18 DirectValue(DirectValueResult),
20
21 MemoryLocation(LocationResult),
23
24 Optimized,
26
27 Composite(Vec<PieceResult>),
29}
30
31#[derive(Debug, Clone, PartialEq)]
33pub enum DirectValueResult {
34 Constant(i64),
36
37 AbsoluteAddress(u64),
40
41 ImplicitValue(Vec<u8>),
43
44 RegisterValue(u16),
46
47 ComputedValue {
50 steps: Vec<ComputeStep>,
52 result_size: MemoryAccessSize,
54 },
55}
56
57#[derive(Debug, Clone, PartialEq)]
59pub enum LocationResult {
60 Address(u64),
62
63 RegisterAddress {
66 register: u16, offset: Option<i64>,
68 size: Option<u64>, },
70
71 ComputedLocation {
74 steps: Vec<ComputeStep>,
76 },
77}
78
79#[derive(Debug, Clone, PartialEq)]
81pub enum CfaResult {
82 RegisterPlusOffset {
84 register: u16, offset: i64,
86 },
87 Expression { steps: Vec<ComputeStep> },
89}
90
91#[derive(Debug, Clone, PartialEq)]
93pub struct CallerFrameRecovery {
94 pub cfa_steps: Vec<ComputeStep>,
96 pub return_address_register: u16,
98 pub caller_pc_steps: Vec<ComputeStep>,
100 pub register_recovery_steps: BTreeMap<u16, Vec<ComputeStep>>,
102}
103
104#[derive(Debug, Clone, PartialEq)]
106pub struct PieceResult {
107 pub location: EvaluationResult,
109 pub size: u64,
111 pub bit_offset: Option<u64>,
113}
114
115#[derive(Debug, Clone, PartialEq)]
117pub struct EntryValueCase {
118 pub caller_return_pc: u64,
120 pub value_steps: Vec<ComputeStep>,
122}
123
124#[derive(Debug, Clone, PartialEq)]
127pub enum ComputeStep {
128 LoadRegister(u16), PushConstant(i64),
133
134 Dereference {
136 size: MemoryAccessSize,
137 },
138
139 Add,
141 Sub,
142 Mul,
143 Div,
144 Mod,
145
146 And,
148 Or,
149 Xor,
150 Shl,
151 Shr,
152 Shra, Not,
156 Neg,
157 Abs,
158
159 Dup,
161 Drop,
162 Swap,
163 Rot,
164 Pick(u8), Eq,
168 Ne,
169 Lt,
170 Le,
171 Gt,
172 Ge,
173
174 If {
176 then_branch: Vec<ComputeStep>,
177 else_branch: Vec<ComputeStep>,
178 },
179
180 EntryValueLookup {
183 caller_pc_steps: Vec<ComputeStep>,
184 cases: Vec<EntryValueCase>,
185 },
186}
187
188#[derive(Debug, Clone, Copy, PartialEq)]
190pub enum MemoryAccessSize {
191 U8, U16, U32, U64, }
196
197impl MemoryAccessSize {
198 pub fn bytes(&self) -> usize {
200 match self {
201 MemoryAccessSize::U8 => 1,
202 MemoryAccessSize::U16 => 2,
203 MemoryAccessSize::U32 => 4,
204 MemoryAccessSize::U64 => 8,
205 }
206 }
207
208 pub fn from_size(size: u64) -> Self {
210 match size {
211 1 => MemoryAccessSize::U8,
212 2 => MemoryAccessSize::U16,
213 4 => MemoryAccessSize::U32,
214 8 => MemoryAccessSize::U64,
215 _ if size <= 8 => MemoryAccessSize::U64, _ => MemoryAccessSize::U64, }
218 }
219}
220
221impl EvaluationResult {
222 pub fn as_constant(&self) -> Option<i64> {
224 match self {
225 EvaluationResult::DirectValue(DirectValueResult::Constant(c)) => Some(*c),
226 _ => None,
227 }
228 }
229
230 pub fn merge_with_cfa(self, cfa: CfaResult, frame_offset: i64) -> Self {
233 match cfa {
234 CfaResult::RegisterPlusOffset { register, offset } => {
235 EvaluationResult::MemoryLocation(LocationResult::RegisterAddress {
237 register,
238 offset: Some(offset.saturating_add(frame_offset)),
239 size: None,
240 })
241 }
242 CfaResult::Expression { mut steps } => {
243 steps.push(ComputeStep::PushConstant(frame_offset));
245 steps.push(ComputeStep::Add);
246 EvaluationResult::MemoryLocation(LocationResult::ComputedLocation { steps })
247 }
248 }
249 }
250}
251
252impl DirectValueResult {
253 pub fn is_compile_time_constant(&self) -> bool {
255 matches!(
256 self,
257 DirectValueResult::Constant(_) | DirectValueResult::ImplicitValue(_)
258 )
259 }
260
261 fn steps_to_expression(steps: &[ComputeStep]) -> String {
263 use ghostscope_platform::register_mapping::dwarf_reg_to_name;
264
265 let mut stack: Vec<String> = Vec::new();
267
268 for step in steps {
269 match step {
270 ComputeStep::LoadRegister(r) => {
271 let reg_name = dwarf_reg_to_name(*r).unwrap_or("r?").to_string();
272 stack.push(reg_name);
273 }
274 ComputeStep::PushConstant(v) => {
275 if *v >= 0 && *v <= 0xFF {
276 stack.push(format!("{v}"));
277 } else {
278 stack.push(format!("0x{v:x}"));
279 }
280 }
281 ComputeStep::Add => {
282 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
283 if a.chars()
285 .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit())
286 && b.parse::<i64>().is_ok()
287 && b.parse::<i64>().unwrap().abs() < 1000
288 {
289 stack.push(format!("{a}+{b}"));
290 } else {
291 stack.push(format!("({a}+{b})"));
292 }
293 } else {
294 stack.push("?+?".to_string());
295 }
296 }
297 ComputeStep::Sub => {
298 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
299 stack.push(format!("({a}-{b})"));
300 } else {
301 stack.push("?-?".to_string());
302 }
303 }
304 ComputeStep::Mul => {
305 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
306 stack.push(format!("{a}*{b}"));
307 } else {
308 stack.push("?*?".to_string());
309 }
310 }
311 ComputeStep::Div => {
312 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
313 stack.push(format!("({a}/{b})"));
314 } else {
315 stack.push("?/?".to_string());
316 }
317 }
318 ComputeStep::Mod => {
319 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
320 stack.push(format!("({a}%{b})"));
321 } else {
322 stack.push("?%?".to_string());
323 }
324 }
325 ComputeStep::And => {
326 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
327 stack.push(format!("({a}&{b})"));
328 } else {
329 stack.push("?&?".to_string());
330 }
331 }
332 ComputeStep::Or => {
333 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
334 stack.push(format!("({a}|{b})"));
335 } else {
336 stack.push("?|?".to_string());
337 }
338 }
339 ComputeStep::Xor => {
340 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
341 stack.push(format!("({a}^{b})"));
342 } else {
343 stack.push("?^?".to_string());
344 }
345 }
346 ComputeStep::Shl => {
347 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
348 stack.push(format!("({a}<<{b})"));
349 } else {
350 stack.push("?<<?".to_string());
351 }
352 }
353 ComputeStep::Shr => {
354 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
355 stack.push(format!("({a}>>{b})"));
356 } else {
357 stack.push("?>>?".to_string());
358 }
359 }
360 ComputeStep::Shra => {
361 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
362 stack.push(format!("({a}>>>{b})"));
363 } else {
364 stack.push("?>>>?".to_string());
365 }
366 }
367 ComputeStep::Not => {
368 if let Some(a) = stack.pop() {
369 stack.push(format!("~{a}"));
370 } else {
371 stack.push("~?".to_string());
372 }
373 }
374 ComputeStep::Neg => {
375 if let Some(a) = stack.pop() {
376 stack.push(format!("-{a}"));
377 } else {
378 stack.push("-?".to_string());
379 }
380 }
381 ComputeStep::Abs => {
382 if let Some(a) = stack.pop() {
383 stack.push(format!("|{a}|"));
384 } else {
385 stack.push("|?|".to_string());
386 }
387 }
388 ComputeStep::Dereference { size } => {
389 if let Some(a) = stack.pop() {
390 stack.push(format!("*({a} as {size})"));
391 } else {
392 stack.push(format!("*(? as {size})"));
393 }
394 }
395 ComputeStep::Dup => {
396 if let Some(top) = stack.last() {
397 stack.push(top.clone());
398 }
399 }
400 ComputeStep::Drop => {
401 stack.pop();
402 }
403 ComputeStep::Swap => {
404 if stack.len() >= 2 {
405 let len = stack.len();
406 stack.swap(len - 1, len - 2);
407 }
408 }
409 ComputeStep::Rot => {
410 if stack.len() >= 3 {
411 let len = stack.len();
412 let third = stack.remove(len - 3);
413 stack.push(third);
414 }
415 }
416 ComputeStep::Pick(n) => {
417 if stack.len() > *n as usize {
418 let idx = stack.len() - 1 - (*n as usize);
419 let val = stack[idx].clone();
420 stack.push(val);
421 } else {
422 stack.push("?".to_string());
423 }
424 }
425 ComputeStep::Eq => {
426 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
427 stack.push(format!("({a}=={b})"));
428 } else {
429 stack.push("?==?".to_string());
430 }
431 }
432 ComputeStep::Ne => {
433 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
434 stack.push(format!("({a}!={b})"));
435 } else {
436 stack.push("?!=?".to_string());
437 }
438 }
439 ComputeStep::Lt => {
440 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
441 stack.push(format!("({a}<{b})"));
442 } else {
443 stack.push("?<?".to_string());
444 }
445 }
446 ComputeStep::Le => {
447 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
448 stack.push(format!("({a}<={b})"));
449 } else {
450 stack.push("?<=?".to_string());
451 }
452 }
453 ComputeStep::Gt => {
454 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
455 stack.push(format!("({a}>{b})"));
456 } else {
457 stack.push("?>?".to_string());
458 }
459 }
460 ComputeStep::Ge => {
461 if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
462 stack.push(format!("({a}>={b})"));
463 } else {
464 stack.push("?>=?".to_string());
465 }
466 }
467 ComputeStep::If {
468 then_branch,
469 else_branch,
470 } => {
471 if let Some(cond) = stack.pop() {
472 stack.push(format!("if {cond} then ... else ..."));
473 } else {
474 stack.push("if ? then ... else ...".to_string());
475 }
476 _ = then_branch;
478 _ = else_branch;
479 }
480 ComputeStep::EntryValueLookup { cases, .. } => {
481 stack.push(format!("entry_value[{} cases]", cases.len()));
482 }
483 }
484 }
485
486 stack.pop().unwrap_or_else(|| "?".to_string())
488 }
489}
490
491impl LocationResult {
492 pub fn is_simple(&self) -> bool {
494 matches!(
495 self,
496 LocationResult::Address(_) | LocationResult::RegisterAddress { .. }
497 )
498 }
499
500 fn steps_to_expression(steps: &[ComputeStep]) -> String {
502 DirectValueResult::steps_to_expression(steps)
503 }
504}
505
506impl fmt::Display for EvaluationResult {
507 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508 match self {
509 EvaluationResult::DirectValue(dv) => write!(f, "[DirectValue] {dv}"),
510 EvaluationResult::MemoryLocation(loc) => write!(f, "[Memory] {loc}"),
511 EvaluationResult::Optimized => write!(f, "<optimized out>"),
512 EvaluationResult::Composite(pieces) => {
513 write!(f, "Composite[{} pieces]", pieces.len())
514 }
515 }
516 }
517}
518
519impl fmt::Display for DirectValueResult {
520 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
521 use ghostscope_platform::register_mapping::dwarf_reg_to_name;
522
523 match self {
524 DirectValueResult::Constant(c) => {
525 if *c >= 0 && *c <= 0xFF {
526 write!(f, "{c} (0x{c:x})")
527 } else {
528 write!(f, "0x{c:x}")
529 }
530 }
531 DirectValueResult::AbsoluteAddress(addr) => write!(f, "&@0x{addr:x}"),
532 DirectValueResult::RegisterValue(r) => {
533 if let Some(name) = dwarf_reg_to_name(*r) {
534 write!(f, "{name}")
535 } else {
536 write!(f, "r{r}")
537 }
538 }
539 DirectValueResult::ImplicitValue(bytes) => {
540 if bytes.len() <= 8 {
541 write!(f, "implicit[")?;
542 for (i, b) in bytes.iter().enumerate() {
543 if i > 0 {
544 write!(f, " ")?;
545 }
546 write!(f, "{b:02x}")?;
547 }
548 write!(f, "]")
549 } else {
550 write!(f, "implicit[{} bytes]", bytes.len())
551 }
552 }
553 DirectValueResult::ComputedValue {
554 steps,
555 result_size: _,
556 } => {
557 write!(f, "=")?;
559
560 let expr = Self::steps_to_expression(steps);
562 write!(f, "{expr}")
563 }
564 }
565 }
566}
567
568impl fmt::Display for LocationResult {
569 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
570 use ghostscope_platform::register_mapping::dwarf_reg_to_name;
571
572 match self {
573 LocationResult::Address(addr) => write!(f, "@0x{addr:x}"),
574 LocationResult::RegisterAddress {
575 register,
576 offset,
577 size,
578 } => {
579 let reg_name = dwarf_reg_to_name(*register).unwrap_or("r?");
580
581 match (offset, size) {
582 (Some(o), Some(s)) => {
583 let offset = *o;
584 if offset >= 0 {
585 write!(f, "@[{reg_name}+{offset}]:{s}")
586 } else {
587 let neg = -offset;
588 write!(f, "@[{reg_name}-{neg}]:{s}")
589 }
590 }
591 (Some(o), None) => {
592 let offset = *o;
593 if offset >= 0 {
594 write!(f, "@[{reg_name}+{offset}]")
595 } else {
596 let neg = -offset;
597 write!(f, "@[{reg_name}-{neg}]")
598 }
599 }
600 (None, Some(s)) => write!(f, "@[{reg_name}]:{s}"),
601 (None, None) => write!(f, "@[{reg_name}]"),
602 }
603 }
604 LocationResult::ComputedLocation { steps } => {
605 write!(f, "@[")?;
607 let expr = Self::steps_to_expression(steps);
608 write!(f, "{expr}]")
609 }
610 }
611 }
612}
613
614impl fmt::Display for MemoryAccessSize {
615 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
616 match self {
617 MemoryAccessSize::U8 => write!(f, "u8"),
618 MemoryAccessSize::U16 => write!(f, "u16"),
619 MemoryAccessSize::U32 => write!(f, "u32"),
620 MemoryAccessSize::U64 => write!(f, "u64"),
621 }
622 }
623}
624
625impl fmt::Display for ComputeStep {
626 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
627 use ghostscope_platform::register_mapping::dwarf_reg_to_name;
628
629 match self {
630 ComputeStep::LoadRegister(r) => {
631 if let Some(name) = dwarf_reg_to_name(*r) {
632 write!(f, "load {name}")
633 } else {
634 write!(f, "load r{r}")
635 }
636 }
637 ComputeStep::PushConstant(v) => write!(f, "push {v}"),
638 ComputeStep::Dereference { size } => write!(f, "deref {size}"),
639 ComputeStep::Add => write!(f, "add"),
640 ComputeStep::Sub => write!(f, "sub"),
641 ComputeStep::Mul => write!(f, "mul"),
642 ComputeStep::Div => write!(f, "div"),
643 ComputeStep::Mod => write!(f, "mod"),
644 ComputeStep::And => write!(f, "and"),
645 ComputeStep::Or => write!(f, "or"),
646 ComputeStep::Xor => write!(f, "xor"),
647 ComputeStep::Shl => write!(f, "shl"),
648 ComputeStep::Shr => write!(f, "shr"),
649 ComputeStep::Shra => write!(f, "shra"),
650 ComputeStep::Not => write!(f, "not"),
651 ComputeStep::Neg => write!(f, "neg"),
652 ComputeStep::Abs => write!(f, "abs"),
653 ComputeStep::Dup => write!(f, "dup"),
654 ComputeStep::Drop => write!(f, "drop"),
655 ComputeStep::Swap => write!(f, "swap"),
656 ComputeStep::Rot => write!(f, "rot"),
657 ComputeStep::Pick(n) => write!(f, "pick {n}"),
658 ComputeStep::Eq => write!(f, "eq"),
659 ComputeStep::Ne => write!(f, "ne"),
660 ComputeStep::Lt => write!(f, "lt"),
661 ComputeStep::Le => write!(f, "le"),
662 ComputeStep::Gt => write!(f, "gt"),
663 ComputeStep::Ge => write!(f, "ge"),
664 ComputeStep::If {
665 then_branch,
666 else_branch,
667 } => {
668 write!(
669 f,
670 "if[then:{} else:{}]",
671 then_branch.len(),
672 else_branch.len()
673 )
674 }
675 ComputeStep::EntryValueLookup { cases, .. } => {
676 write!(f, "entry_value_lookup[cases:{}]", cases.len())
677 }
678 }
679 }
680}
681
682#[cfg(test)]
683mod tests {
684 use super::{CfaResult, EvaluationResult, LocationResult};
685
686 #[test]
687 fn merge_with_cfa_saturates_register_plus_offset() {
688 let merged = EvaluationResult::Optimized.merge_with_cfa(
689 CfaResult::RegisterPlusOffset {
690 register: 7,
691 offset: i64::MAX - 2,
692 },
693 10,
694 );
695
696 assert_eq!(
697 merged,
698 EvaluationResult::MemoryLocation(LocationResult::RegisterAddress {
699 register: 7,
700 offset: Some(i64::MAX),
701 size: None,
702 })
703 );
704 }
705}