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