• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::mem_sniffer::AccessKind;
2 use crate::mem_sniffer::MemSniffer;
3 use crate::DynResult;
4 use armv4t_emu::reg;
5 use armv4t_emu::Cpu;
6 use armv4t_emu::ExampleMem;
7 use armv4t_emu::Memory;
8 use armv4t_emu::Mode;
9 use gdbstub::common::Pid;
10 use gdbstub::target::ext::tracepoints::NewTracepoint;
11 use gdbstub::target::ext::tracepoints::SourceTracepoint;
12 use gdbstub::target::ext::tracepoints::Tracepoint;
13 use gdbstub::target::ext::tracepoints::TracepointAction;
14 use gdbstub::target::ext::tracepoints::TracepointEnumerateState;
15 use std::collections::BTreeMap;
16 
17 const HLE_RETURN_ADDR: u32 = 0x12345678;
18 
19 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
20 pub enum Event {
21     DoneStep,
22     Halted,
23     Break,
24     WatchWrite(u32),
25     WatchRead(u32),
26 }
27 
28 pub enum ExecMode {
29     Step,
30     Continue,
31     RangeStep(u32, u32),
32 }
33 
34 #[derive(Debug)]
35 pub struct TraceFrame {
36     pub number: Tracepoint,
37     pub snapshot: Cpu,
38 }
39 
40 /// incredibly barebones armv4t-based emulator
41 pub struct Emu {
42     start_addr: u32,
43 
44     // example custom register. only read/written to from the GDB client
45     pub(crate) custom_reg: u32,
46 
47     pub(crate) exec_mode: ExecMode,
48 
49     pub(crate) cpu: Cpu,
50     pub(crate) mem: ExampleMem,
51 
52     pub(crate) watchpoints: Vec<u32>,
53     pub(crate) breakpoints: Vec<u32>,
54     pub(crate) files: Vec<Option<std::fs::File>>,
55 
56     pub(crate) tracepoints: BTreeMap<
57         Tracepoint,
58         (
59             NewTracepoint<u32>,
60             Vec<SourceTracepoint<'static, u32>>,
61             Vec<TracepointAction<'static, u32>>,
62         ),
63     >,
64     pub(crate) traceframes: Vec<TraceFrame>,
65     pub(crate) tracepoint_enumerate_state: TracepointEnumerateState<u32>,
66     pub(crate) tracing: bool,
67     pub(crate) selected_frame: Option<usize>,
68 
69     pub(crate) reported_pid: Pid,
70 }
71 
72 impl Emu {
new(program_elf: &[u8]) -> DynResult<Emu>73     pub fn new(program_elf: &[u8]) -> DynResult<Emu> {
74         // set up emulated system
75         let mut cpu = Cpu::new();
76         let mut mem = ExampleMem::new();
77 
78         // load ELF
79         let elf_header = goblin::elf::Elf::parse(program_elf)?;
80 
81         // copy all in-memory sections from the ELF file into system RAM
82         let sections = elf_header
83             .section_headers
84             .iter()
85             .filter(|h| h.is_alloc() && h.sh_type != goblin::elf::section_header::SHT_NOBITS);
86 
87         for h in sections {
88             eprintln!(
89                 "loading section {:?} into memory from [{:#010x?}..{:#010x?}]",
90                 elf_header.shdr_strtab.get_at(h.sh_name).unwrap(),
91                 h.sh_addr,
92                 h.sh_addr + h.sh_size,
93             );
94 
95             for (i, b) in program_elf[h.file_range().unwrap()].iter().enumerate() {
96                 mem.w8(h.sh_addr as u32 + i as u32, *b);
97             }
98         }
99 
100         // setup execution state
101         eprintln!("Setting PC to {:#010x?}", elf_header.entry);
102         cpu.reg_set(Mode::User, reg::SP, 0x10000000);
103         cpu.reg_set(Mode::User, reg::LR, HLE_RETURN_ADDR);
104         cpu.reg_set(Mode::User, reg::PC, elf_header.entry as u32);
105         cpu.reg_set(Mode::User, reg::CPSR, 0x10); // user mode
106 
107         Ok(Emu {
108             start_addr: elf_header.entry as u32,
109 
110             custom_reg: 0x12345678,
111 
112             exec_mode: ExecMode::Continue,
113 
114             cpu,
115             mem,
116 
117             watchpoints: Vec::new(),
118             breakpoints: Vec::new(),
119             files: Vec::new(),
120 
121             tracepoints: BTreeMap::new(),
122             traceframes: Vec::new(),
123             tracepoint_enumerate_state: Default::default(),
124             tracing: false,
125             selected_frame: None,
126 
127             reported_pid: Pid::new(1).unwrap(),
128         })
129     }
130 
reset(&mut self)131     pub(crate) fn reset(&mut self) {
132         self.cpu.reg_set(Mode::User, reg::SP, 0x10000000);
133         self.cpu.reg_set(Mode::User, reg::LR, HLE_RETURN_ADDR);
134         self.cpu.reg_set(Mode::User, reg::PC, self.start_addr);
135         self.cpu.reg_set(Mode::User, reg::CPSR, 0x10);
136     }
137 
138     /// single-step the interpreter
step(&mut self) -> Option<Event>139     pub fn step(&mut self) -> Option<Event> {
140         if self.tracing {
141             let pc = self.cpu.reg_get(self.cpu.mode(), reg::PC);
142             let frames: Vec<_> = self
143                 .tracepoints
144                 .iter()
145                 .filter(|(_tracepoint, (ctp, _source, _actions))| ctp.enabled && ctp.addr == pc)
146                 .map(|(tracepoint, _definition)| {
147                     // our `tracepoint_define` restricts our loaded tracepoints to only contain
148                     // register collect actions. instead of only collecting the registers requested
149                     // in the register mask and recording a minimal trace frame, we just collect
150                     // all of them by cloning the cpu itself.
151                     TraceFrame {
152                         number: *tracepoint,
153                         snapshot: self.cpu,
154                     }
155                 })
156                 .collect();
157             self.traceframes.extend(frames);
158         }
159 
160         let mut hit_watchpoint = None;
161 
162         let mut sniffer = MemSniffer::new(&mut self.mem, &self.watchpoints, |access| {
163             hit_watchpoint = Some(access)
164         });
165 
166         self.cpu.step(&mut sniffer);
167         let pc = self.cpu.reg_get(Mode::User, reg::PC);
168 
169         if let Some(access) = hit_watchpoint {
170             let fixup = if self.cpu.thumb_mode() { 2 } else { 4 };
171             self.cpu.reg_set(Mode::User, reg::PC, pc - fixup);
172 
173             return Some(match access.kind {
174                 AccessKind::Read => Event::WatchRead(access.addr),
175                 AccessKind::Write => Event::WatchWrite(access.addr),
176             });
177         }
178 
179         if self.breakpoints.contains(&pc) {
180             return Some(Event::Break);
181         }
182 
183         if pc == HLE_RETURN_ADDR {
184             return Some(Event::Halted);
185         }
186 
187         None
188     }
189 
190     /// run the emulator in accordance with the currently set `ExecutionMode`.
191     ///
192     /// since the emulator runs in the same thread as the GDB loop, the emulator
193     /// will use the provided callback to poll the connection for incoming data
194     /// every 1024 steps.
run(&mut self, mut poll_incoming_data: impl FnMut() -> bool) -> RunEvent195     pub fn run(&mut self, mut poll_incoming_data: impl FnMut() -> bool) -> RunEvent {
196         match self.exec_mode {
197             ExecMode::Step => RunEvent::Event(self.step().unwrap_or(Event::DoneStep)),
198             ExecMode::Continue => {
199                 let mut cycles = 0;
200                 loop {
201                     if cycles % 1024 == 0 {
202                         // poll for incoming data
203                         if poll_incoming_data() {
204                             break RunEvent::IncomingData;
205                         }
206                     }
207                     cycles += 1;
208 
209                     if let Some(event) = self.step() {
210                         break RunEvent::Event(event);
211                     };
212                 }
213             }
214             // just continue, but with an extra PC check
215             ExecMode::RangeStep(start, end) => {
216                 let mut cycles = 0;
217                 loop {
218                     if cycles % 1024 == 0 {
219                         // poll for incoming data
220                         if poll_incoming_data() {
221                             break RunEvent::IncomingData;
222                         }
223                     }
224                     cycles += 1;
225 
226                     if let Some(event) = self.step() {
227                         break RunEvent::Event(event);
228                     };
229 
230                     if !(start..end).contains(&self.cpu.reg_get(self.cpu.mode(), reg::PC)) {
231                         break RunEvent::Event(Event::DoneStep);
232                     }
233                 }
234             }
235         }
236     }
237 }
238 
239 pub enum RunEvent {
240     IncomingData,
241     Event(Event),
242 }
243