• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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