1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
use super::{Blk, ExternSymbol, Sub};
use crate::prelude::*;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt;

/// The `Program` structure represents a disassembled binary.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Program {
    /// The known functions contained in the binary
    pub subs: BTreeMap<Tid, Term<Sub>>,
    /// Extern symbols linked to the binary by the linker.
    pub extern_symbols: BTreeMap<Tid, ExternSymbol>,
    /// Entry points into to binary,
    /// i.e. the term identifiers of functions that may be called from outside of the binary.
    pub entry_points: BTreeSet<Tid>,
    /// An offset that has been added to all addresses in the program compared to the addresses
    /// as specified in the binary file.
    ///
    /// In certain cases, e.g. if the binary specifies a segment to be loaded at address 0,
    /// the Ghidra backend may shift the whole binary image by a constant value in memory.
    /// Thus addresses as specified by the binary and addresses as reported by Ghidra may differ by a constant offset,
    /// which is stored in this value.
    pub address_base_offset: u64,
}

impl Program {
    /// Find a block term by its term identifier.
    /// WARNING: The function simply iterates through all blocks,
    /// i.e. it is very inefficient for large projects!
    pub fn find_block(&self, tid: &Tid) -> Option<&Term<Blk>> {
        self.subs
            .iter()
            .flat_map(|(_, sub)| sub.term.blocks.iter())
            .find(|block| block.tid == *tid)
    }

    /// Find the sub containing a specific jump instruction (including call instructions).
    /// WARNING: The function simply iterates though all blocks,
    /// i.e. it is very inefficient for large projects!
    pub fn find_sub_containing_jump(&self, jmp_tid: &Tid) -> Option<Tid> {
        for sub in self.subs.values() {
            for blk in &sub.term.blocks {
                for jmp in &blk.term.jmps {
                    if &jmp.tid == jmp_tid {
                        return Some(sub.tid.clone());
                    }
                }
            }
        }
        None
    }
}

impl fmt::Display for Program {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for Term { tid, term: sub } in self.subs.values() {
            writeln!(
                f,
                "SUB [{}] name:{} entry:{}",
                tid,
                sub.name,
                if self.entry_points.contains(tid) {
                    "yes"
                } else {
                    "no"
                }
            )?;
            for Term { tid, term: blk } in sub.blocks.iter() {
                writeln!(f, "  BLK [{}]", tid)?;
                for Term { tid, term: def } in blk.defs.iter() {
                    writeln!(f, "    DEF [{}] {}", tid, def)?;
                }
                for Term { tid, term: jmp } in blk.jmps.iter() {
                    writeln!(f, "    JMP [{}] {}", tid, jmp)?;
                }
            }
        }
        for ext in self.extern_symbols.values() {
            writeln!(f, "EXT {}", ext)?;
        }
        Ok(())
    }
}