1 use core::convert::TryInto;
2
3 use armv4t_emu::{reg, Memory};
4 use gdbstub::common::Signal;
5 use gdbstub::target;
6 use gdbstub::target::ext::base::singlethread::{SingleThreadBase, SingleThreadResume};
7 use gdbstub::target::{Target, TargetError, TargetResult};
8 use gdbstub_arch::arm::reg::id::ArmCoreRegId;
9
10 use crate::emu::{Emu, ExecMode};
11
12 // Additional GDB extensions
13
14 mod auxv;
15 mod breakpoints;
16 mod catch_syscalls;
17 mod exec_file;
18 mod extended_mode;
19 mod host_io;
20 mod lldb_register_info_override;
21 mod memory_map;
22 mod monitor_cmd;
23 mod section_offsets;
24 mod target_description_xml_override;
25
26 /// Turn a `ArmCoreRegId` into an internal register number of `armv4t_emu`.
cpu_reg_id(id: ArmCoreRegId) -> Option<u8>27 fn cpu_reg_id(id: ArmCoreRegId) -> Option<u8> {
28 match id {
29 ArmCoreRegId::Gpr(i) => Some(i),
30 ArmCoreRegId::Sp => Some(reg::SP),
31 ArmCoreRegId::Lr => Some(reg::LR),
32 ArmCoreRegId::Pc => Some(reg::PC),
33 ArmCoreRegId::Cpsr => Some(reg::CPSR),
34 _ => None,
35 }
36 }
37
38 /// Copy all bytes of `data` to `buf`.
39 /// Return the size of data copied.
copy_to_buf(data: &[u8], buf: &mut [u8]) -> usize40 pub fn copy_to_buf(data: &[u8], buf: &mut [u8]) -> usize {
41 let len = buf.len().min(data.len());
42 buf[..len].copy_from_slice(&data[..len]);
43 len
44 }
45
46 /// Copy a range of `data` (start at `offset` with a size of `length`) to `buf`.
47 /// Return the size of data copied. Returns 0 if `offset >= buf.len()`.
48 ///
49 /// Mainly used by qXfer:_object_:read commands.
copy_range_to_buf(data: &[u8], offset: u64, length: usize, buf: &mut [u8]) -> usize50 pub fn copy_range_to_buf(data: &[u8], offset: u64, length: usize, buf: &mut [u8]) -> usize {
51 let offset = offset as usize;
52 if offset > data.len() {
53 return 0;
54 }
55
56 let start = offset;
57 let end = (offset + length).min(data.len());
58 copy_to_buf(&data[start..end], buf)
59 }
60
61 impl Target for Emu {
62 // As an example, I've defined a custom architecture based off
63 // `gdbstub_arch::arm::Armv4t`. The implementation is in the `custom_arch`
64 // module at the bottom of this file.
65 //
66 // unless you're working with a particularly funky architecture that uses custom
67 // registers, you should probably stick to using the simple `target.xml`
68 // implementations from the `gdbstub_arch` repo (i.e: `target.xml` files that
69 // only specify the <architecture> and <feature>s of the arch, instead of
70 // listing out all the registers out manually).
71 type Arch = custom_arch::Armv4tCustom;
72 type Error = &'static str;
73
74 // --------------- IMPORTANT NOTE ---------------
75 // Always remember to annotate IDET enable methods with `inline(always)`!
76 // Without this annotation, LLVM might fail to dead-code-eliminate nested IDET
77 // implementations, resulting in unnecessary binary bloat.
78
79 #[inline(always)]
base_ops(&mut self) -> target::ext::base::BaseOps<'_, Self::Arch, Self::Error>80 fn base_ops(&mut self) -> target::ext::base::BaseOps<'_, Self::Arch, Self::Error> {
81 target::ext::base::BaseOps::SingleThread(self)
82 }
83
84 #[inline(always)]
support_breakpoints( &mut self, ) -> Option<target::ext::breakpoints::BreakpointsOps<'_, Self>>85 fn support_breakpoints(
86 &mut self,
87 ) -> Option<target::ext::breakpoints::BreakpointsOps<'_, Self>> {
88 Some(self)
89 }
90
91 #[inline(always)]
support_extended_mode( &mut self, ) -> Option<target::ext::extended_mode::ExtendedModeOps<'_, Self>>92 fn support_extended_mode(
93 &mut self,
94 ) -> Option<target::ext::extended_mode::ExtendedModeOps<'_, Self>> {
95 Some(self)
96 }
97
98 #[inline(always)]
support_monitor_cmd(&mut self) -> Option<target::ext::monitor_cmd::MonitorCmdOps<'_, Self>>99 fn support_monitor_cmd(&mut self) -> Option<target::ext::monitor_cmd::MonitorCmdOps<'_, Self>> {
100 Some(self)
101 }
102
103 #[inline(always)]
support_section_offsets( &mut self, ) -> Option<target::ext::section_offsets::SectionOffsetsOps<'_, Self>>104 fn support_section_offsets(
105 &mut self,
106 ) -> Option<target::ext::section_offsets::SectionOffsetsOps<'_, Self>> {
107 Some(self)
108 }
109
110 #[inline(always)]
support_target_description_xml_override( &mut self, ) -> Option< target::ext::target_description_xml_override::TargetDescriptionXmlOverrideOps<'_, Self>, >111 fn support_target_description_xml_override(
112 &mut self,
113 ) -> Option<
114 target::ext::target_description_xml_override::TargetDescriptionXmlOverrideOps<'_, Self>,
115 > {
116 Some(self)
117 }
118
119 #[inline(always)]
support_lldb_register_info_override( &mut self, ) -> Option<target::ext::lldb_register_info_override::LldbRegisterInfoOverrideOps<'_, Self>>120 fn support_lldb_register_info_override(
121 &mut self,
122 ) -> Option<target::ext::lldb_register_info_override::LldbRegisterInfoOverrideOps<'_, Self>>
123 {
124 Some(self)
125 }
126
127 #[inline(always)]
support_memory_map(&mut self) -> Option<target::ext::memory_map::MemoryMapOps<'_, Self>>128 fn support_memory_map(&mut self) -> Option<target::ext::memory_map::MemoryMapOps<'_, Self>> {
129 Some(self)
130 }
131
132 #[inline(always)]
support_catch_syscalls( &mut self, ) -> Option<target::ext::catch_syscalls::CatchSyscallsOps<'_, Self>>133 fn support_catch_syscalls(
134 &mut self,
135 ) -> Option<target::ext::catch_syscalls::CatchSyscallsOps<'_, Self>> {
136 Some(self)
137 }
138
139 #[inline(always)]
support_host_io(&mut self) -> Option<target::ext::host_io::HostIoOps<'_, Self>>140 fn support_host_io(&mut self) -> Option<target::ext::host_io::HostIoOps<'_, Self>> {
141 Some(self)
142 }
143
144 #[inline(always)]
support_exec_file(&mut self) -> Option<target::ext::exec_file::ExecFileOps<'_, Self>>145 fn support_exec_file(&mut self) -> Option<target::ext::exec_file::ExecFileOps<'_, Self>> {
146 Some(self)
147 }
148
149 #[inline(always)]
support_auxv(&mut self) -> Option<target::ext::auxv::AuxvOps<'_, Self>>150 fn support_auxv(&mut self) -> Option<target::ext::auxv::AuxvOps<'_, Self>> {
151 Some(self)
152 }
153 }
154
155 impl SingleThreadBase for Emu {
read_registers( &mut self, regs: &mut custom_arch::ArmCoreRegsCustom, ) -> TargetResult<(), Self>156 fn read_registers(
157 &mut self,
158 regs: &mut custom_arch::ArmCoreRegsCustom,
159 ) -> TargetResult<(), Self> {
160 let mode = self.cpu.mode();
161
162 for i in 0..13 {
163 regs.core.r[i] = self.cpu.reg_get(mode, i as u8);
164 }
165 regs.core.sp = self.cpu.reg_get(mode, reg::SP);
166 regs.core.lr = self.cpu.reg_get(mode, reg::LR);
167 regs.core.pc = self.cpu.reg_get(mode, reg::PC);
168 regs.core.cpsr = self.cpu.reg_get(mode, reg::CPSR);
169
170 regs.custom = self.custom_reg;
171
172 Ok(())
173 }
174
write_registers(&mut self, regs: &custom_arch::ArmCoreRegsCustom) -> TargetResult<(), Self>175 fn write_registers(&mut self, regs: &custom_arch::ArmCoreRegsCustom) -> TargetResult<(), Self> {
176 let mode = self.cpu.mode();
177
178 for i in 0..13 {
179 self.cpu.reg_set(mode, i, regs.core.r[i as usize]);
180 }
181 self.cpu.reg_set(mode, reg::SP, regs.core.sp);
182 self.cpu.reg_set(mode, reg::LR, regs.core.lr);
183 self.cpu.reg_set(mode, reg::PC, regs.core.pc);
184 self.cpu.reg_set(mode, reg::CPSR, regs.core.cpsr);
185
186 self.custom_reg = regs.custom;
187
188 Ok(())
189 }
190
191 #[inline(always)]
support_single_register_access( &mut self, ) -> Option<target::ext::base::single_register_access::SingleRegisterAccessOps<'_, (), Self>>192 fn support_single_register_access(
193 &mut self,
194 ) -> Option<target::ext::base::single_register_access::SingleRegisterAccessOps<'_, (), Self>>
195 {
196 Some(self)
197 }
198
read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self>199 fn read_addrs(&mut self, start_addr: u32, data: &mut [u8]) -> TargetResult<(), Self> {
200 for (addr, val) in (start_addr..).zip(data.iter_mut()) {
201 *val = self.mem.r8(addr)
202 }
203 Ok(())
204 }
205
write_addrs(&mut self, start_addr: u32, data: &[u8]) -> TargetResult<(), Self>206 fn write_addrs(&mut self, start_addr: u32, data: &[u8]) -> TargetResult<(), Self> {
207 for (addr, val) in (start_addr..).zip(data.iter().copied()) {
208 self.mem.w8(addr, val)
209 }
210 Ok(())
211 }
212
213 #[inline(always)]
support_resume( &mut self, ) -> Option<target::ext::base::singlethread::SingleThreadResumeOps<'_, Self>>214 fn support_resume(
215 &mut self,
216 ) -> Option<target::ext::base::singlethread::SingleThreadResumeOps<'_, Self>> {
217 Some(self)
218 }
219 }
220
221 impl SingleThreadResume for Emu {
resume(&mut self, signal: Option<Signal>) -> Result<(), Self::Error>222 fn resume(&mut self, signal: Option<Signal>) -> Result<(), Self::Error> {
223 // Upon returning from the `resume` method, the target being debugged should be
224 // configured to run according to whatever resume actions the GDB client has
225 // specified (as specified by `set_resume_action`, `resume_range_step`,
226 // `reverse_{step, continue}`, etc...)
227 //
228 // In this basic `armv4t` example, the `resume` method simply sets the exec mode
229 // of the emulator's interpreter loop and returns.
230 //
231 // In more complex implementations, it's likely that the target being debugged
232 // will be running in another thread / process, and will require some kind of
233 // external "orchestration" to set it's execution mode (e.g: modifying the
234 // target's process state via platform specific debugging syscalls).
235
236 if signal.is_some() {
237 return Err("no support for continuing with signal");
238 }
239
240 self.exec_mode = ExecMode::Continue;
241
242 Ok(())
243 }
244
245 #[inline(always)]
support_reverse_cont( &mut self, ) -> Option<target::ext::base::reverse_exec::ReverseContOps<'_, (), Self>>246 fn support_reverse_cont(
247 &mut self,
248 ) -> Option<target::ext::base::reverse_exec::ReverseContOps<'_, (), Self>> {
249 Some(self)
250 }
251
252 #[inline(always)]
support_reverse_step( &mut self, ) -> Option<target::ext::base::reverse_exec::ReverseStepOps<'_, (), Self>>253 fn support_reverse_step(
254 &mut self,
255 ) -> Option<target::ext::base::reverse_exec::ReverseStepOps<'_, (), Self>> {
256 Some(self)
257 }
258
259 #[inline(always)]
support_single_step( &mut self, ) -> Option<target::ext::base::singlethread::SingleThreadSingleStepOps<'_, Self>>260 fn support_single_step(
261 &mut self,
262 ) -> Option<target::ext::base::singlethread::SingleThreadSingleStepOps<'_, Self>> {
263 Some(self)
264 }
265
266 #[inline(always)]
support_range_step( &mut self, ) -> Option<target::ext::base::singlethread::SingleThreadRangeSteppingOps<'_, Self>>267 fn support_range_step(
268 &mut self,
269 ) -> Option<target::ext::base::singlethread::SingleThreadRangeSteppingOps<'_, Self>> {
270 Some(self)
271 }
272 }
273
274 impl target::ext::base::singlethread::SingleThreadSingleStep for Emu {
step(&mut self, signal: Option<Signal>) -> Result<(), Self::Error>275 fn step(&mut self, signal: Option<Signal>) -> Result<(), Self::Error> {
276 if signal.is_some() {
277 return Err("no support for stepping with signal");
278 }
279
280 self.exec_mode = ExecMode::Step;
281
282 Ok(())
283 }
284 }
285
286 impl target::ext::base::single_register_access::SingleRegisterAccess<()> for Emu {
read_register( &mut self, _tid: (), reg_id: custom_arch::ArmCoreRegIdCustom, buf: &mut [u8], ) -> TargetResult<usize, Self>287 fn read_register(
288 &mut self,
289 _tid: (),
290 reg_id: custom_arch::ArmCoreRegIdCustom,
291 buf: &mut [u8],
292 ) -> TargetResult<usize, Self> {
293 match reg_id {
294 custom_arch::ArmCoreRegIdCustom::Core(reg_id) => {
295 if let Some(i) = cpu_reg_id(reg_id) {
296 let w = self.cpu.reg_get(self.cpu.mode(), i);
297 buf.copy_from_slice(&w.to_le_bytes());
298 Ok(buf.len())
299 } else {
300 Err(().into())
301 }
302 }
303 custom_arch::ArmCoreRegIdCustom::Custom => {
304 buf.copy_from_slice(&self.custom_reg.to_le_bytes());
305 Ok(buf.len())
306 }
307 custom_arch::ArmCoreRegIdCustom::Time => {
308 buf.copy_from_slice(
309 &(std::time::SystemTime::now()
310 .duration_since(std::time::UNIX_EPOCH)
311 .unwrap()
312 .as_millis() as u32)
313 .to_le_bytes(),
314 );
315 Ok(buf.len())
316 }
317 custom_arch::ArmCoreRegIdCustom::Unavailable => Ok(0),
318 }
319 }
320
write_register( &mut self, _tid: (), reg_id: custom_arch::ArmCoreRegIdCustom, val: &[u8], ) -> TargetResult<(), Self>321 fn write_register(
322 &mut self,
323 _tid: (),
324 reg_id: custom_arch::ArmCoreRegIdCustom,
325 val: &[u8],
326 ) -> TargetResult<(), Self> {
327 let w = u32::from_le_bytes(
328 val.try_into()
329 .map_err(|_| TargetError::Fatal("invalid data"))?,
330 );
331 match reg_id {
332 custom_arch::ArmCoreRegIdCustom::Core(reg_id) => {
333 if let Some(i) = cpu_reg_id(reg_id) {
334 self.cpu.reg_set(self.cpu.mode(), i, w);
335 Ok(())
336 } else {
337 Err(().into())
338 }
339 }
340 custom_arch::ArmCoreRegIdCustom::Custom => {
341 self.custom_reg = w;
342 Ok(())
343 }
344 // ignore writes
345 custom_arch::ArmCoreRegIdCustom::Unavailable
346 | custom_arch::ArmCoreRegIdCustom::Time => Ok(()),
347 }
348 }
349 }
350
351 impl target::ext::base::reverse_exec::ReverseCont<()> for Emu {
reverse_cont(&mut self) -> Result<(), Self::Error>352 fn reverse_cont(&mut self) -> Result<(), Self::Error> {
353 // FIXME: actually implement reverse step
354 eprintln!(
355 "FIXME: Not actually reverse-continuing. Performing forwards continue instead..."
356 );
357 self.exec_mode = ExecMode::Continue;
358 Ok(())
359 }
360 }
361
362 impl target::ext::base::reverse_exec::ReverseStep<()> for Emu {
reverse_step(&mut self, _tid: ()) -> Result<(), Self::Error>363 fn reverse_step(&mut self, _tid: ()) -> Result<(), Self::Error> {
364 // FIXME: actually implement reverse step
365 eprintln!(
366 "FIXME: Not actually reverse-stepping. Performing single forwards step instead..."
367 );
368 self.exec_mode = ExecMode::Step;
369 Ok(())
370 }
371 }
372
373 impl target::ext::base::singlethread::SingleThreadRangeStepping for Emu {
resume_range_step(&mut self, start: u32, end: u32) -> Result<(), Self::Error>374 fn resume_range_step(&mut self, start: u32, end: u32) -> Result<(), Self::Error> {
375 self.exec_mode = ExecMode::RangeStep(start, end);
376 Ok(())
377 }
378 }
379
380 mod custom_arch {
381 use core::num::NonZeroUsize;
382
383 use gdbstub::arch::lldb::{Encoding, Format, Generic, Register, RegisterInfo};
384 use gdbstub::arch::{Arch, RegId, Registers, SingleStepGdbBehavior};
385
386 use gdbstub_arch::arm::reg::id::ArmCoreRegId;
387 use gdbstub_arch::arm::reg::ArmCoreRegs;
388 use gdbstub_arch::arm::ArmBreakpointKind;
389
390 /// Implements `Arch` for ARMv4T
391 pub enum Armv4tCustom {}
392
393 #[derive(Debug, Default, Clone, Eq, PartialEq)]
394 pub struct ArmCoreRegsCustom {
395 pub core: ArmCoreRegs,
396 pub custom: u32,
397 }
398
399 impl Registers for ArmCoreRegsCustom {
400 type ProgramCounter = u32;
401
pc(&self) -> Self::ProgramCounter402 fn pc(&self) -> Self::ProgramCounter {
403 self.core.pc
404 }
405
gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>))406 fn gdb_serialize(&self, mut write_byte: impl FnMut(Option<u8>)) {
407 self.core.gdb_serialize(&mut write_byte);
408
409 macro_rules! write_bytes {
410 ($bytes:expr) => {
411 for b in $bytes {
412 write_byte(Some(*b))
413 }
414 };
415 }
416
417 write_bytes!(&self.custom.to_le_bytes());
418 }
419
gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()>420 fn gdb_deserialize(&mut self, bytes: &[u8]) -> Result<(), ()> {
421 // ensure bytes.chunks_exact(4) won't panic
422 if bytes.len() % 4 != 0 {
423 return Err(());
424 }
425
426 use core::convert::TryInto;
427 let mut regs = bytes
428 .chunks_exact(4)
429 .map(|c| u32::from_le_bytes(c.try_into().unwrap()));
430
431 // copied from ArmCoreRegs
432 {
433 for reg in self.core.r.iter_mut() {
434 *reg = regs.next().ok_or(())?
435 }
436 self.core.sp = regs.next().ok_or(())?;
437 self.core.lr = regs.next().ok_or(())?;
438 self.core.pc = regs.next().ok_or(())?;
439
440 // Floating point registers (unused)
441 for _ in 0..25 {
442 regs.next().ok_or(())?;
443 }
444
445 self.core.cpsr = regs.next().ok_or(())?;
446 }
447
448 self.custom = regs.next().ok_or(())?;
449
450 if regs.next().is_some() {
451 return Err(());
452 }
453
454 Ok(())
455 }
456 }
457
458 #[derive(Debug)]
459 pub enum ArmCoreRegIdCustom {
460 Core(ArmCoreRegId),
461 Custom,
462 // not sent as part of `struct ArmCoreRegsCustom`, and only accessible via the single
463 // register read/write functions
464 Time,
465 /// This pseudo-register is valid but never available
466 Unavailable,
467 }
468
469 impl RegId for ArmCoreRegIdCustom {
from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)>470 fn from_raw_id(id: usize) -> Option<(Self, Option<NonZeroUsize>)> {
471 let reg = match id {
472 26 => Self::Custom,
473 27 => Self::Time,
474 28 => Self::Unavailable,
475 _ => {
476 let (reg, size) = ArmCoreRegId::from_raw_id(id)?;
477 return Some((Self::Core(reg), size));
478 }
479 };
480 Some((reg, Some(NonZeroUsize::new(4)?)))
481 }
482 }
483
484 impl Arch for Armv4tCustom {
485 type Usize = u32;
486 type Registers = ArmCoreRegsCustom;
487 type RegId = ArmCoreRegIdCustom;
488 type BreakpointKind = ArmBreakpointKind;
489
490 // for _purely demonstrative purposes_, i'll return dummy data from this
491 // function, as it will be overwritten by TargetDescriptionXmlOverride.
492 //
493 // See `examples/armv4t/gdb/target_description_xml_override.rs`
494 //
495 // in an actual implementation, you'll want to return an actual string here!
target_description_xml() -> Option<&'static str>496 fn target_description_xml() -> Option<&'static str> {
497 Some("never gets returned")
498 }
499
500 // (LLDB extension)
501 //
502 // for _purely demonstrative purposes_, even though this provides a working
503 // example, it will get overwritten by RegisterInfoOverride.
504 //
505 // See `examples/armv4t/gdb/register_info_override.rs`
lldb_register_info(reg_id: usize) -> Option<RegisterInfo<'static>>506 fn lldb_register_info(reg_id: usize) -> Option<RegisterInfo<'static>> {
507 match ArmCoreRegIdCustom::from_raw_id(reg_id) {
508 Some((_, None)) | None => Some(RegisterInfo::Done),
509 Some((r, Some(size))) => {
510 let name = match r {
511 // For the purpose of demonstration, we end the qRegisterInfo packet
512 // exchange when reaching the Time register id, so that this register can
513 // only be explicitly queried via the single-register read packet.
514 ArmCoreRegIdCustom::Time => return Some(RegisterInfo::Done),
515 ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(i)) => match i {
516 0 => "r0",
517 1 => "r1",
518 2 => "r2",
519 3 => "r3",
520 4 => "r4",
521 5 => "r5",
522 6 => "r6",
523 7 => "r7",
524 8 => "r8",
525 9 => "r9",
526 10 => "r10",
527 11 => "r11",
528 12 => "r12",
529 _ => "unknown",
530 },
531 ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) => "sp",
532 ArmCoreRegIdCustom::Core(ArmCoreRegId::Lr) => "lr",
533 ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) => "pc",
534 ArmCoreRegIdCustom::Core(ArmCoreRegId::Fpr(_i)) => "padding",
535 ArmCoreRegIdCustom::Core(ArmCoreRegId::Fps) => "padding",
536 ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr) => "cpsr",
537 ArmCoreRegIdCustom::Custom => "custom",
538 ArmCoreRegIdCustom::Unavailable => "Unavailable",
539 _ => "unknown",
540 };
541 let encoding = match r {
542 ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => Encoding::Uint,
543 ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp)
544 | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc)
545 | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr)
546 | ArmCoreRegIdCustom::Unavailable
547 | ArmCoreRegIdCustom::Custom => Encoding::Uint,
548 _ => Encoding::Vector,
549 };
550 let format = match r {
551 ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => Format::Hex,
552 ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp)
553 | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc)
554 | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr)
555 | ArmCoreRegIdCustom::Unavailable
556 | ArmCoreRegIdCustom::Custom => Format::Hex,
557 _ => Format::VectorUInt8,
558 };
559 let set = match r {
560 ArmCoreRegIdCustom::Core(ArmCoreRegId::Gpr(_i)) => {
561 "General Purpose Registers"
562 }
563 ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp)
564 | ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc)
565 | ArmCoreRegIdCustom::Core(ArmCoreRegId::Cpsr)
566 | ArmCoreRegIdCustom::Unavailable
567 | ArmCoreRegIdCustom::Custom => "General Purpose Registers",
568 _ => "Floating Point Registers",
569 };
570 let generic = match r {
571 ArmCoreRegIdCustom::Core(ArmCoreRegId::Sp) => Some(Generic::Sp),
572 ArmCoreRegIdCustom::Core(ArmCoreRegId::Pc) => Some(Generic::Pc),
573 _ => None,
574 };
575 let reg = Register {
576 name,
577 alt_name: None,
578 bitsize: (usize::from(size)) * 8,
579 offset: reg_id * (usize::from(size)),
580 encoding,
581 format,
582 set,
583 gcc: None,
584 dwarf: Some(reg_id),
585 generic,
586 container_regs: None,
587 invalidate_regs: None,
588 };
589 Some(RegisterInfo::Register(reg))
590 }
591 }
592 }
593 // armv4t supports optional single stepping.
594 //
595 // notably, x86 is an example of an arch that does _not_ support
596 // optional single stepping.
597 #[inline(always)]
single_step_gdb_behavior() -> SingleStepGdbBehavior598 fn single_step_gdb_behavior() -> SingleStepGdbBehavior {
599 SingleStepGdbBehavior::Optional
600 }
601 }
602 }
603