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