1 use core::convert::TryInto;
2
3 use armv4t_emu::{reg, Memory};
4 use gdbstub::target;
5 use gdbstub::target::ext::base::singlethread::{
6 GdbInterrupt, ResumeAction, SingleThreadOps, SingleThreadReverseContOps,
7 SingleThreadReverseStepOps, StopReason,
8 };
9 use gdbstub::target::ext::breakpoints::WatchKind;
10 use gdbstub::target::{Target, TargetError, TargetResult};
11 use gdbstub_arch::arm::reg::id::ArmCoreRegId;
12
13 use crate::emu::{Emu, Event};
14
15 // Additional GDB extensions
16
17 mod breakpoints;
18 mod extended_mode;
19 mod monitor_cmd;
20 mod section_offsets;
21 mod target_description_xml_override;
22
23 /// Turn a `ArmCoreRegId` into an internal register number of `armv4t_emu`.
cpu_reg_id(id: ArmCoreRegId) -> Option<u8>24 fn cpu_reg_id(id: ArmCoreRegId) -> Option<u8> {
25 match id {
26 ArmCoreRegId::Gpr(i) => Some(i),
27 ArmCoreRegId::Sp => Some(reg::SP),
28 ArmCoreRegId::Lr => Some(reg::LR),
29 ArmCoreRegId::Pc => Some(reg::PC),
30 ArmCoreRegId::Cpsr => Some(reg::CPSR),
31 _ => None,
32 }
33 }
34
35 impl Target for Emu {
36 type Arch = gdbstub_arch::arm::Armv4t;
37 type Error = &'static str;
38
39 // --------------- IMPORTANT NOTE ---------------
40 // Always remember to annotate IDET enable methods with `inline(always)`!
41 // Without this annotation, LLVM might fail to dead-code-eliminate nested IDET
42 // implementations, resulting in unnecessary binary bloat.
43
44 #[inline(always)]
base_ops(&mut self) -> target::ext::base::BaseOps<Self::Arch, Self::Error>45 fn base_ops(&mut self) -> target::ext::base::BaseOps<Self::Arch, Self::Error> {
46 target::ext::base::BaseOps::SingleThread(self)
47 }
48
49 #[inline(always)]
breakpoints(&mut self) -> Option<target::ext::breakpoints::BreakpointsOps<Self>>50 fn breakpoints(&mut self) -> Option<target::ext::breakpoints::BreakpointsOps<Self>> {
51 Some(self)
52 }
53
54 #[inline(always)]
extended_mode(&mut self) -> Option<target::ext::extended_mode::ExtendedModeOps<Self>>55 fn extended_mode(&mut self) -> Option<target::ext::extended_mode::ExtendedModeOps<Self>> {
56 Some(self)
57 }
58
59 #[inline(always)]
monitor_cmd(&mut self) -> Option<target::ext::monitor_cmd::MonitorCmdOps<Self>>60 fn monitor_cmd(&mut self) -> Option<target::ext::monitor_cmd::MonitorCmdOps<Self>> {
61 Some(self)
62 }
63
64 #[inline(always)]
section_offsets(&mut self) -> Option<target::ext::section_offsets::SectionOffsetsOps<Self>>65 fn section_offsets(&mut self) -> Option<target::ext::section_offsets::SectionOffsetsOps<Self>> {
66 Some(self)
67 }
68
69 #[inline(always)]
target_description_xml_override( &mut self, ) -> Option<target::ext::target_description_xml_override::TargetDescriptionXmlOverrideOps<Self>>70 fn target_description_xml_override(
71 &mut self,
72 ) -> Option<target::ext::target_description_xml_override::TargetDescriptionXmlOverrideOps<Self>>
73 {
74 Some(self)
75 }
76 }
77
78 impl Emu {
inner_resume( &mut self, action: ResumeAction, mut check_gdb_interrupt: impl FnMut() -> bool, ) -> Result<StopReason<u32>, &'static str>79 fn inner_resume(
80 &mut self,
81 action: ResumeAction,
82 mut check_gdb_interrupt: impl FnMut() -> bool,
83 ) -> Result<StopReason<u32>, &'static str> {
84 let event = match action {
85 ResumeAction::Step => match self.step() {
86 Some(e) => e,
87 None => return Ok(StopReason::DoneStep),
88 },
89 ResumeAction::Continue => {
90 let mut cycles = 0;
91 loop {
92 if let Some(event) = self.step() {
93 break event;
94 };
95
96 // check for GDB interrupt every 1024 instructions
97 cycles += 1;
98 if cycles % 1024 == 0 && check_gdb_interrupt() {
99 return Ok(StopReason::GdbInterrupt);
100 }
101 }
102 }
103 _ => return Err("cannot resume with signal"),
104 };
105
106 Ok(match event {
107 Event::Halted => StopReason::Terminated(19), // SIGSTOP
108 Event::Break => StopReason::SwBreak,
109 Event::WatchWrite(addr) => StopReason::Watch {
110 kind: WatchKind::Write,
111 addr,
112 },
113 Event::WatchRead(addr) => StopReason::Watch {
114 kind: WatchKind::Read,
115 addr,
116 },
117 })
118 }
119 }
120
121 impl SingleThreadOps for Emu {
resume( &mut self, action: ResumeAction, gdb_interrupt: GdbInterrupt<'_>, ) -> Result<StopReason<u32>, Self::Error>122 fn resume(
123 &mut self,
124 action: ResumeAction,
125 gdb_interrupt: GdbInterrupt<'_>,
126 ) -> Result<StopReason<u32>, Self::Error> {
127 let mut gdb_interrupt = gdb_interrupt.no_async();
128 self.inner_resume(action, || gdb_interrupt.pending())
129 }
130
read_registers( &mut self, regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs, ) -> TargetResult<(), Self>131 fn read_registers(
132 &mut self,
133 regs: &mut gdbstub_arch::arm::reg::ArmCoreRegs,
134 ) -> TargetResult<(), Self> {
135 let mode = self.cpu.mode();
136
137 for i in 0..13 {
138 regs.r[i] = self.cpu.reg_get(mode, i as u8);
139 }
140 regs.sp = self.cpu.reg_get(mode, reg::SP);
141 regs.lr = self.cpu.reg_get(mode, reg::LR);
142 regs.pc = self.cpu.reg_get(mode, reg::PC);
143 regs.cpsr = self.cpu.reg_get(mode, reg::CPSR);
144
145 Ok(())
146 }
147
write_registers( &mut self, regs: &gdbstub_arch::arm::reg::ArmCoreRegs, ) -> TargetResult<(), Self>148 fn write_registers(
149 &mut self,
150 regs: &gdbstub_arch::arm::reg::ArmCoreRegs,
151 ) -> TargetResult<(), Self> {
152 let mode = self.cpu.mode();
153
154 for i in 0..13 {
155 self.cpu.reg_set(mode, i, regs.r[i as usize]);
156 }
157 self.cpu.reg_set(mode, reg::SP, regs.sp);
158 self.cpu.reg_set(mode, reg::LR, regs.lr);
159 self.cpu.reg_set(mode, reg::PC, regs.pc);
160 self.cpu.reg_set(mode, reg::CPSR, regs.cpsr);
161
162 Ok(())
163 }
164
read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self>165 fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self> {
166 for (addr, val) in (start_addr..).zip(data.iter_mut()) {
167 *val = self.mem.r8(addr)
168 }
169 Ok(())
170 }
171
write_addrs(&mut self, start_addr: u32, data: &[u8]) -> TargetResult<(), Self>172 fn write_addrs(&mut self, start_addr: u32, data: &[u8]) -> TargetResult<(), Self> {
173 for (addr, val) in (start_addr..).zip(data.iter().copied()) {
174 self.mem.w8(addr, val)
175 }
176 Ok(())
177 }
178
179 #[inline(always)]
single_register_access( &mut self, ) -> Option<target::ext::base::SingleRegisterAccessOps<(), Self>>180 fn single_register_access(
181 &mut self,
182 ) -> Option<target::ext::base::SingleRegisterAccessOps<(), Self>> {
183 Some(self)
184 }
185
186 #[inline(always)]
support_reverse_cont(&mut self) -> Option<SingleThreadReverseContOps<Self>>187 fn support_reverse_cont(&mut self) -> Option<SingleThreadReverseContOps<Self>> {
188 Some(self)
189 }
190
191 #[inline(always)]
support_reverse_step(&mut self) -> Option<SingleThreadReverseStepOps<Self>>192 fn support_reverse_step(&mut self) -> Option<SingleThreadReverseStepOps<Self>> {
193 Some(self)
194 }
195
196 #[inline(always)]
support_resume_range_step( &mut self, ) -> Option<target::ext::base::singlethread::SingleThreadRangeSteppingOps<Self>>197 fn support_resume_range_step(
198 &mut self,
199 ) -> Option<target::ext::base::singlethread::SingleThreadRangeSteppingOps<Self>> {
200 Some(self)
201 }
202 }
203
204 impl target::ext::base::SingleRegisterAccess<()> for Emu {
read_register( &mut self, _tid: (), reg_id: gdbstub_arch::arm::reg::id::ArmCoreRegId, dst: &mut [u8], ) -> TargetResult<(), Self>205 fn read_register(
206 &mut self,
207 _tid: (),
208 reg_id: gdbstub_arch::arm::reg::id::ArmCoreRegId,
209 dst: &mut [u8],
210 ) -> TargetResult<(), Self> {
211 if let Some(i) = cpu_reg_id(reg_id) {
212 let w = self.cpu.reg_get(self.cpu.mode(), i);
213 dst.copy_from_slice(&w.to_le_bytes());
214 Ok(())
215 } else {
216 Err(().into())
217 }
218 }
219
write_register( &mut self, _tid: (), reg_id: gdbstub_arch::arm::reg::id::ArmCoreRegId, val: &[u8], ) -> TargetResult<(), Self>220 fn write_register(
221 &mut self,
222 _tid: (),
223 reg_id: gdbstub_arch::arm::reg::id::ArmCoreRegId,
224 val: &[u8],
225 ) -> TargetResult<(), Self> {
226 let w = u32::from_le_bytes(
227 val.try_into()
228 .map_err(|_| TargetError::Fatal("invalid data"))?,
229 );
230 if let Some(i) = cpu_reg_id(reg_id) {
231 self.cpu.reg_set(self.cpu.mode(), i, w);
232 Ok(())
233 } else {
234 Err(().into())
235 }
236 }
237 }
238
239 impl target::ext::base::singlethread::SingleThreadReverseCont for Emu {
reverse_cont( &mut self, gdb_interrupt: GdbInterrupt<'_>, ) -> Result<StopReason<u32>, Self::Error>240 fn reverse_cont(
241 &mut self,
242 gdb_interrupt: GdbInterrupt<'_>,
243 ) -> Result<StopReason<u32>, Self::Error> {
244 // FIXME: actually implement reverse step
245 eprintln!(
246 "FIXME: Not actually reverse-continuing. Performing forwards continue instead..."
247 );
248 self.resume(ResumeAction::Continue, gdb_interrupt)
249 }
250 }
251
252 impl target::ext::base::singlethread::SingleThreadReverseStep for Emu {
reverse_step( &mut self, gdb_interrupt: GdbInterrupt<'_>, ) -> Result<StopReason<u32>, Self::Error>253 fn reverse_step(
254 &mut self,
255 gdb_interrupt: GdbInterrupt<'_>,
256 ) -> Result<StopReason<u32>, Self::Error> {
257 // FIXME: actually implement reverse step
258 eprintln!(
259 "FIXME: Not actually reverse-stepping. Performing single forwards step instead..."
260 );
261 self.resume(ResumeAction::Step, gdb_interrupt)
262 }
263 }
264
265 impl target::ext::base::singlethread::SingleThreadRangeStepping for Emu {
resume_range_step( &mut self, start: u32, end: u32, gdb_interrupt: GdbInterrupt<'_>, ) -> Result<StopReason<u32>, Self::Error>266 fn resume_range_step(
267 &mut self,
268 start: u32,
269 end: u32,
270 gdb_interrupt: GdbInterrupt<'_>,
271 ) -> Result<StopReason<u32>, Self::Error> {
272 let mut gdb_interrupt = gdb_interrupt.no_async();
273 loop {
274 match self.inner_resume(ResumeAction::Step, || gdb_interrupt.pending())? {
275 StopReason::DoneStep => {}
276 stop_reason => return Ok(stop_reason),
277 }
278
279 if !(start..end).contains(&self.cpu.reg_get(self.cpu.mode(), reg::PC)) {
280 return Ok(StopReason::DoneStep);
281 }
282 }
283 }
284 }
285