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