• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use armv4t_emu::{reg, Cpu, ExampleMem, Memory, Mode};
2 
3 use crate::mem_sniffer::{AccessKind, MemSniffer};
4 use crate::DynResult;
5 
6 const HLE_RETURN_ADDR: u32 = 0x12345678;
7 
8 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
9 pub enum Event {
10     DoneStep,
11     Halted,
12     Break,
13     WatchWrite(u32),
14     WatchRead(u32),
15 }
16 
17 pub enum ExecMode {
18     Step,
19     Continue,
20     RangeStep(u32, u32),
21 }
22 
23 /// incredibly barebones armv4t-based emulator
24 pub struct Emu {
25     start_addr: u32,
26 
27     // example custom register. only read/written to from the GDB client
28     pub(crate) custom_reg: u32,
29 
30     pub(crate) exec_mode: ExecMode,
31 
32     pub(crate) cpu: Cpu,
33     pub(crate) mem: ExampleMem,
34 
35     pub(crate) watchpoints: Vec<u32>,
36     pub(crate) breakpoints: Vec<u32>,
37     pub(crate) files: Vec<Option<std::fs::File>>,
38 }
39 
40 impl Emu {
new(program_elf: &[u8]) -> DynResult<Emu>41     pub fn new(program_elf: &[u8]) -> DynResult<Emu> {
42         // set up emulated system
43         let mut cpu = Cpu::new();
44         let mut mem = ExampleMem::new();
45 
46         // load ELF
47         let elf_header = goblin::elf::Elf::parse(program_elf)?;
48 
49         // copy all in-memory sections from the ELF file into system RAM
50         let sections = elf_header
51             .section_headers
52             .iter()
53             .filter(|h| h.is_alloc() && h.sh_type != goblin::elf::section_header::SHT_NOBITS);
54 
55         for h in sections {
56             eprintln!(
57                 "loading section {:?} into memory from [{:#010x?}..{:#010x?}]",
58                 elf_header.shdr_strtab.get_at(h.sh_name).unwrap(),
59                 h.sh_addr,
60                 h.sh_addr + h.sh_size,
61             );
62 
63             for (i, b) in program_elf[h.file_range().unwrap()].iter().enumerate() {
64                 mem.w8(h.sh_addr as u32 + i as u32, *b);
65             }
66         }
67 
68         // setup execution state
69         eprintln!("Setting PC to {:#010x?}", elf_header.entry);
70         cpu.reg_set(Mode::User, reg::SP, 0x10000000);
71         cpu.reg_set(Mode::User, reg::LR, HLE_RETURN_ADDR);
72         cpu.reg_set(Mode::User, reg::PC, elf_header.entry as u32);
73         cpu.reg_set(Mode::User, reg::CPSR, 0x10); // user mode
74 
75         Ok(Emu {
76             start_addr: elf_header.entry as u32,
77 
78             custom_reg: 0x12345678,
79 
80             exec_mode: ExecMode::Continue,
81 
82             cpu,
83             mem,
84 
85             watchpoints: Vec::new(),
86             breakpoints: Vec::new(),
87             files: Vec::new(),
88         })
89     }
90 
reset(&mut self)91     pub(crate) fn reset(&mut self) {
92         self.cpu.reg_set(Mode::User, reg::SP, 0x10000000);
93         self.cpu.reg_set(Mode::User, reg::LR, HLE_RETURN_ADDR);
94         self.cpu.reg_set(Mode::User, reg::PC, self.start_addr);
95         self.cpu.reg_set(Mode::User, reg::CPSR, 0x10);
96     }
97 
98     /// single-step the interpreter
step(&mut self) -> Option<Event>99     pub fn step(&mut self) -> Option<Event> {
100         let mut hit_watchpoint = None;
101 
102         let mut sniffer = MemSniffer::new(&mut self.mem, &self.watchpoints, |access| {
103             hit_watchpoint = Some(access)
104         });
105 
106         self.cpu.step(&mut sniffer);
107         let pc = self.cpu.reg_get(Mode::User, reg::PC);
108 
109         if let Some(access) = hit_watchpoint {
110             let fixup = if self.cpu.thumb_mode() { 2 } else { 4 };
111             self.cpu.reg_set(Mode::User, reg::PC, pc - fixup);
112 
113             return Some(match access.kind {
114                 AccessKind::Read => Event::WatchRead(access.addr),
115                 AccessKind::Write => Event::WatchWrite(access.addr),
116             });
117         }
118 
119         if self.breakpoints.contains(&pc) {
120             return Some(Event::Break);
121         }
122 
123         if pc == HLE_RETURN_ADDR {
124             return Some(Event::Halted);
125         }
126 
127         None
128     }
129 
130     /// run the emulator in accordance with the currently set `ExecutionMode`.
131     ///
132     /// since the emulator runs in the same thread as the GDB loop, the emulator
133     /// will use the provided callback to poll the connection for incoming data
134     /// every 1024 steps.
run(&mut self, mut poll_incoming_data: impl FnMut() -> bool) -> RunEvent135     pub fn run(&mut self, mut poll_incoming_data: impl FnMut() -> bool) -> RunEvent {
136         match self.exec_mode {
137             ExecMode::Step => RunEvent::Event(self.step().unwrap_or(Event::DoneStep)),
138             ExecMode::Continue => {
139                 let mut cycles = 0;
140                 loop {
141                     if cycles % 1024 == 0 {
142                         // poll for incoming data
143                         if poll_incoming_data() {
144                             break RunEvent::IncomingData;
145                         }
146                     }
147                     cycles += 1;
148 
149                     if let Some(event) = self.step() {
150                         break RunEvent::Event(event);
151                     };
152                 }
153             }
154             // just continue, but with an extra PC check
155             ExecMode::RangeStep(start, end) => {
156                 let mut cycles = 0;
157                 loop {
158                     if cycles % 1024 == 0 {
159                         // poll for incoming data
160                         if poll_incoming_data() {
161                             break RunEvent::IncomingData;
162                         }
163                     }
164                     cycles += 1;
165 
166                     if let Some(event) = self.step() {
167                         break RunEvent::Event(event);
168                     };
169 
170                     if !(start..end).contains(&self.cpu.reg_get(self.cpu.mode(), reg::PC)) {
171                         break RunEvent::Event(Event::DoneStep);
172                     }
173                 }
174             }
175         }
176     }
177 }
178 
179 pub enum RunEvent {
180     IncomingData,
181     Event(Event),
182 }
183