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