forked from encounter/decomp-toolkit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexecutor.rs
153 lines (136 loc) · 4.74 KB
/
executor.rs
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use anyhow::{ensure, Result};
use fixedbitset::FixedBitSet;
use ppc750cl::Ins;
use crate::{
analysis::{
cfa::SectionAddress,
disassemble,
vm::{StepResult, VM},
},
obj::{ObjInfo, ObjSection, ObjSectionKind},
};
/// Space-efficient implementation for tracking visited code addresses
struct VisitedAddresses {
inner: Vec<FixedBitSet>,
}
impl VisitedAddresses {
pub fn new(obj: &ObjInfo) -> Self {
let mut inner = Vec::with_capacity(obj.sections.len() as usize);
for (_, section) in obj.sections.iter() {
if section.kind == ObjSectionKind::Code {
let size = (section.size / 4) as usize;
inner.push(FixedBitSet::with_capacity(size));
} else {
// Empty
inner.push(FixedBitSet::new())
}
}
Self { inner }
}
pub fn contains(&self, section_address: u32, address: SectionAddress) -> bool {
self.inner[address.section as usize]
.contains(Self::bit_for(section_address, address.address))
}
pub fn insert(&mut self, section_address: u32, address: SectionAddress) {
self.inner[address.section as usize]
.insert(Self::bit_for(section_address, address.address));
}
#[inline]
fn bit_for(section_address: u32, address: u32) -> usize {
((address - section_address) / 4) as usize
}
}
pub struct VMState {
pub vm: Box<VM>,
pub address: SectionAddress,
}
/// Helper for branched VM execution, only visiting addresses once.
pub struct Executor {
vm_stack: Vec<VMState>,
visited: VisitedAddresses,
}
pub struct ExecCbData<'a> {
pub executor: &'a mut Executor,
pub vm: &'a mut VM,
pub result: StepResult,
pub ins_addr: SectionAddress,
pub section: &'a ObjSection,
pub ins: Ins,
pub block_start: SectionAddress,
}
pub enum ExecCbResult<T = ()> {
Continue,
Jump(SectionAddress),
EndBlock,
End(T),
}
impl Executor {
pub fn new(obj: &ObjInfo) -> Self {
Self { vm_stack: vec![], visited: VisitedAddresses::new(obj) }
}
pub fn run<Cb, R>(&mut self, obj: &ObjInfo, mut cb: Cb) -> Result<Option<R>>
where Cb: FnMut(ExecCbData) -> Result<ExecCbResult<R>> {
while let Some(mut state) = self.vm_stack.pop() {
let section = &obj.sections[state.address.section];
ensure!(
section.contains(state.address.address),
"Invalid address {:#010X} within section {} ({:#010X}-{:#010X})",
state.address.address,
section.name,
section.address,
section.address + section.size
);
if section.kind != ObjSectionKind::Code {
log::warn!("Attempted to visit non-code address {:#010X}", state.address);
continue;
}
// Already visited block
let section_address = section.address as u32;
if self.visited.contains(section_address, state.address) {
continue;
}
let mut block_start = state.address;
loop {
self.visited.insert(section_address, state.address);
let ins = match disassemble(section, state.address.address) {
Some(ins) => ins,
None => return Ok(None),
};
let result = state.vm.step(obj, state.address, ins);
match cb(ExecCbData {
executor: self,
vm: &mut state.vm,
result,
ins_addr: state.address,
section,
ins,
block_start,
})? {
ExecCbResult::Continue => {
state.address += 4;
}
ExecCbResult::Jump(addr) => {
if self.visited.contains(section_address, addr) {
break;
}
block_start = addr;
state.address = addr;
}
ExecCbResult::EndBlock => break,
ExecCbResult::End(result) => return Ok(Some(result)),
}
}
}
Ok(None)
}
pub fn push(&mut self, address: SectionAddress, vm: Box<VM>, sort: bool) {
self.vm_stack.push(VMState { address, vm });
if sort {
// Sort lowest to highest, so we always go highest address first
self.vm_stack.sort_by_key(|state| state.address);
}
}
pub fn visited(&self, section_address: u32, address: SectionAddress) -> bool {
self.visited.contains(section_address, address)
}
}