// Copyright 2015 Ted Mielczarek. See the COPYRIGHT // file at the top-level directory of this distribution. //! CPU contexts. use num_traits::FromPrimitive; use scroll::Pread; use std::collections::HashSet; use std::fmt; use std::io; use std::io::prelude::*; use std::mem; use tracing::warn; use crate::iostuff::*; use crate::{MinidumpMiscInfo, MinidumpSystemInfo}; use minidump_common::format as md; use minidump_common::format::ContextFlagsCpu; /// The CPU-specific context structure. #[derive(Debug, Clone)] #[cfg_attr(feature = "arbitrary_impls", derive(arbitrary::Arbitrary))] pub enum MinidumpRawContext { X86(md::CONTEXT_X86), Ppc(md::CONTEXT_PPC), Ppc64(md::CONTEXT_PPC64), Amd64(md::CONTEXT_AMD64), Sparc(md::CONTEXT_SPARC), Arm(md::CONTEXT_ARM), Arm64(md::CONTEXT_ARM64), OldArm64(md::CONTEXT_ARM64_OLD), Mips(md::CONTEXT_MIPS), } /// Generic over the specifics of a CPU context. pub trait CpuContext { /// The word size of general-purpose registers in the context. type Register: fmt::LowerHex; /// General purpose registers in this context type. const REGISTERS: &'static [&'static str]; /// Gets whether the given register is valid /// /// This is exposed so that the context can map aliases. For instance /// "lr" and "x30" are aliases in ARM64. fn register_is_valid(&self, reg: &str, valid: &MinidumpContextValidity) -> bool { if let MinidumpContextValidity::Some(ref which) = *valid { which.contains(reg) } else { self.memoize_register(reg).is_some() } } /// Get a register value if it is valid. /// /// Get the value of the register named `reg` from this CPU context /// if `valid` indicates that it has a valid value, otherwise return /// `None`. fn get_register(&self, reg: &str, valid: &MinidumpContextValidity) -> Option { if self.register_is_valid(reg, valid) { Some(self.get_register_always(reg)) } else { None } } /// Get a register value regardless of whether it is valid. fn get_register_always(&self, reg: &str) -> Self::Register; /// Set a register value, if that register name it exists. /// /// Returns None if the register name isn't supported. fn set_register(&mut self, reg: &str, val: Self::Register) -> Option<()>; /// Gets a static version of the given register name, if possible. /// /// Returns the default name of the register for register name aliases. fn memoize_register(&self, reg: &str) -> Option<&'static str> { default_memoize_register(Self::REGISTERS, reg) } /// Return a String containing the value of `reg` formatted to its natural width. fn format_register(&self, reg: &str) -> String { format!( "0x{:01$x}", self.get_register_always(reg), mem::size_of::() * 2 ) } /// An iterator over all registers in this context. /// /// This iterator yields registers and values regardless of whether the register is valid. To /// get valid values, use [`valid_registers`](Self::valid_registers), instead. fn registers(&self) -> CpuRegisters<'_, Self> { self.valid_registers(&MinidumpContextValidity::All) } /// An iterator over valid registers in this context. /// /// This iterator yields valid registers and their values. fn valid_registers<'a>(&'a self, valid: &'a MinidumpContextValidity) -> CpuRegisters<'a, Self> { let regs = match valid { MinidumpContextValidity::All => CpuRegistersInner::Slice(Self::REGISTERS.iter()), MinidumpContextValidity::Some(valid) => CpuRegistersInner::Set(valid.iter()), }; CpuRegisters { regs, context: self, } } /// Gets the name of the stack pointer register (for use with get_register/set_register). fn stack_pointer_register_name(&self) -> &'static str; /// Gets the name of the instruction pointer register (for use with get_register/set_register). fn instruction_pointer_register_name(&self) -> &'static str; } /// Default implementation for `CpuContext::memoize_register`. fn default_memoize_register(registers: &[&'static str], reg: &str) -> Option<&'static str> { let idx = registers.iter().position(|val| *val == reg)?; Some(registers[idx]) } #[derive(Debug, Clone)] enum CpuRegistersInner<'a> { Slice(std::slice::Iter<'a, &'static str>), Set(std::collections::hash_set::Iter<'a, &'static str>), } /// An iterator over registers and values in a [`CpuContext`]. /// /// Returned by [`CpuContext::registers`] and [`CpuContext::valid_registers`]. #[derive(Clone, Debug)] pub struct CpuRegisters<'a, T: ?Sized> { regs: CpuRegistersInner<'a>, context: &'a T, } impl Iterator for CpuRegisters<'_, T> where T: CpuContext, { type Item = (&'static str, T::Register); fn next(&mut self) -> Option { let reg = match &mut self.regs { CpuRegistersInner::Slice(iter) => iter.next(), CpuRegistersInner::Set(iter) => iter.next(), }?; Some((reg, self.context.get_register_always(reg))) } } impl CpuContext for md::CONTEXT_X86 { type Register = u32; const REGISTERS: &'static [&'static str] = &[ "eip", "esp", "ebp", "ebx", "esi", "edi", "eax", "ecx", "edx", "eflags", ]; fn get_register_always(&self, reg: &str) -> u32 { match reg { "eip" => self.eip, "esp" => self.esp, "ebp" => self.ebp, "ebx" => self.ebx, "esi" => self.esi, "edi" => self.edi, "eax" => self.eax, "ecx" => self.ecx, "edx" => self.edx, "eflags" => self.eflags, _ => unreachable!("Invalid x86 register! {}", reg), } } fn set_register(&mut self, reg: &str, val: Self::Register) -> Option<()> { match reg { "eip" => self.eip = val, "esp" => self.esp = val, "ebp" => self.ebp = val, "ebx" => self.ebx = val, "esi" => self.esi = val, "edi" => self.edi = val, "eax" => self.eax = val, "ecx" => self.ecx = val, "edx" => self.edx = val, "eflags" => self.eflags = val, _ => return None, } Some(()) } fn stack_pointer_register_name(&self) -> &'static str { "esp" } fn instruction_pointer_register_name(&self) -> &'static str { "eip" } } impl CpuContext for md::CONTEXT_AMD64 { type Register = u64; const REGISTERS: &'static [&'static str] = &[ "rax", "rdx", "rcx", "rbx", "rsi", "rdi", "rbp", "rsp", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "rip", ]; fn get_register_always(&self, reg: &str) -> u64 { match reg { "rax" => self.rax, "rdx" => self.rdx, "rcx" => self.rcx, "rbx" => self.rbx, "rsi" => self.rsi, "rdi" => self.rdi, "rbp" => self.rbp, "rsp" => self.rsp, "r8" => self.r8, "r9" => self.r9, "r10" => self.r10, "r11" => self.r11, "r12" => self.r12, "r13" => self.r13, "r14" => self.r14, "r15" => self.r15, "rip" => self.rip, _ => unreachable!("Invalid x86-64 register! {}", reg), } } fn set_register(&mut self, reg: &str, val: Self::Register) -> Option<()> { match reg { "rax" => self.rax = val, "rdx" => self.rdx = val, "rcx" => self.rcx = val, "rbx" => self.rbx = val, "rsi" => self.rsi = val, "rdi" => self.rdi = val, "rbp" => self.rbp = val, "rsp" => self.rsp = val, "r8" => self.r8 = val, "r9" => self.r9 = val, "r10" => self.r10 = val, "r11" => self.r11 = val, "r12" => self.r12 = val, "r13" => self.r13 = val, "r14" => self.r14 = val, "r15" => self.r15 = val, "rip" => self.rip = val, _ => return None, } Some(()) } fn stack_pointer_register_name(&self) -> &'static str { "rsp" } fn instruction_pointer_register_name(&self) -> &'static str { "rip" } } impl CpuContext for md::CONTEXT_ARM { type Register = u32; const REGISTERS: &'static [&'static str] = &[ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r12", "fp", "sp", "lr", "pc", ]; fn memoize_register(&self, reg: &str) -> Option<&'static str> { match reg { "r11" => Some("fp"), "r13" => Some("sp"), "r14" => Some("lr"), "r15" => Some("pc"), _ => default_memoize_register(Self::REGISTERS, reg), } } fn register_is_valid(&self, reg: &str, valid: &MinidumpContextValidity) -> bool { if let MinidumpContextValidity::Some(ref which) = valid { match reg { "r11" | "fp" => which.contains("r11") || which.contains("fp"), "r13" | "sp" => which.contains("r13") || which.contains("sp"), "r14" | "lr" => which.contains("r14") || which.contains("lr"), "r15" | "pc" => which.contains("r15") || which.contains("pc"), _ => which.contains(reg), } } else { self.memoize_register(reg).is_some() } } fn get_register_always(&self, reg: &str) -> u32 { match reg { "r0" => self.iregs[0], "r1" => self.iregs[1], "r2" => self.iregs[2], "r3" => self.iregs[3], "r4" => self.iregs[4], "r5" => self.iregs[5], "r6" => self.iregs[6], "r7" => self.iregs[7], "r8" => self.iregs[8], "r9" => self.iregs[9], "r10" => self.iregs[10], "r11" => self.iregs[11], "r12" => self.iregs[12], "r13" => self.iregs[13], "r14" => self.iregs[14], "r15" => self.iregs[15], "pc" => self.iregs[md::ArmRegisterNumbers::ProgramCounter as usize], "lr" => self.iregs[md::ArmRegisterNumbers::LinkRegister as usize], "fp" => self.iregs[md::ArmRegisterNumbers::FramePointer as usize], "sp" => self.iregs[md::ArmRegisterNumbers::StackPointer as usize], _ => unreachable!("Invalid arm register! {}", reg), } } fn set_register(&mut self, reg: &str, val: Self::Register) -> Option<()> { match reg { "r0" => self.iregs[0] = val, "r1" => self.iregs[1] = val, "r2" => self.iregs[2] = val, "r3" => self.iregs[3] = val, "r4" => self.iregs[4] = val, "r5" => self.iregs[5] = val, "r6" => self.iregs[6] = val, "r7" => self.iregs[7] = val, "r8" => self.iregs[8] = val, "r9" => self.iregs[9] = val, "r10" => self.iregs[10] = val, "r11" => self.iregs[11] = val, "r12" => self.iregs[12] = val, "r13" => self.iregs[13] = val, "r14" => self.iregs[14] = val, "r15" => self.iregs[15] = val, "pc" => self.iregs[md::ArmRegisterNumbers::ProgramCounter as usize] = val, "lr" => self.iregs[md::ArmRegisterNumbers::LinkRegister as usize] = val, "fp" => self.iregs[md::ArmRegisterNumbers::FramePointer as usize] = val, "sp" => self.iregs[md::ArmRegisterNumbers::StackPointer as usize] = val, _ => return None, } Some(()) } fn stack_pointer_register_name(&self) -> &'static str { "sp" } fn instruction_pointer_register_name(&self) -> &'static str { "pc" } } impl CpuContext for md::CONTEXT_ARM64_OLD { type Register = u64; const REGISTERS: &'static [&'static str] = &[ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "fp", "lr", "sp", "pc", ]; fn memoize_register(&self, reg: &str) -> Option<&'static str> { match reg { "x29" => Some("fp"), "x30" => Some("lr"), _ => default_memoize_register(Self::REGISTERS, reg), } } fn register_is_valid(&self, reg: &str, valid: &MinidumpContextValidity) -> bool { if let MinidumpContextValidity::Some(ref which) = valid { match reg { "x29" | "fp" => which.contains("x29") || which.contains("fp"), "x30" | "lr" => which.contains("x30") || which.contains("lr"), _ => which.contains(reg), } } else { self.memoize_register(reg).is_some() } } fn get_register_always(&self, reg: &str) -> u64 { match reg { "x0" => self.iregs[0], "x1" => self.iregs[1], "x2" => self.iregs[2], "x3" => self.iregs[3], "x4" => self.iregs[4], "x5" => self.iregs[5], "x6" => self.iregs[6], "x7" => self.iregs[7], "x8" => self.iregs[8], "x9" => self.iregs[9], "x10" => self.iregs[10], "x11" => self.iregs[11], "x12" => self.iregs[12], "x13" => self.iregs[13], "x14" => self.iregs[14], "x15" => self.iregs[15], "x16" => self.iregs[16], "x17" => self.iregs[17], "x18" => self.iregs[18], "x19" => self.iregs[19], "x20" => self.iregs[20], "x21" => self.iregs[21], "x22" => self.iregs[22], "x23" => self.iregs[23], "x24" => self.iregs[24], "x25" => self.iregs[25], "x26" => self.iregs[26], "x27" => self.iregs[27], "x28" => self.iregs[28], "x29" => self.iregs[29], "x30" => self.iregs[30], "pc" => self.pc, "sp" => self.sp, "lr" => self.iregs[md::Arm64RegisterNumbers::LinkRegister as usize], "fp" => self.iregs[md::Arm64RegisterNumbers::FramePointer as usize], _ => unreachable!("Invalid aarch64 register! {}", reg), } } fn set_register(&mut self, reg: &str, val: Self::Register) -> Option<()> { match reg { "x0" => self.iregs[0] = val, "x1" => self.iregs[1] = val, "x2" => self.iregs[2] = val, "x3" => self.iregs[3] = val, "x4" => self.iregs[4] = val, "x5" => self.iregs[5] = val, "x6" => self.iregs[6] = val, "x7" => self.iregs[7] = val, "x8" => self.iregs[8] = val, "x9" => self.iregs[9] = val, "x10" => self.iregs[10] = val, "x11" => self.iregs[11] = val, "x12" => self.iregs[12] = val, "x13" => self.iregs[13] = val, "x14" => self.iregs[14] = val, "x15" => self.iregs[15] = val, "x16" => self.iregs[16] = val, "x17" => self.iregs[17] = val, "x18" => self.iregs[18] = val, "x19" => self.iregs[19] = val, "x20" => self.iregs[20] = val, "x21" => self.iregs[21] = val, "x22" => self.iregs[22] = val, "x23" => self.iregs[23] = val, "x24" => self.iregs[24] = val, "x25" => self.iregs[25] = val, "x26" => self.iregs[26] = val, "x27" => self.iregs[27] = val, "x28" => self.iregs[28] = val, "x29" => self.iregs[29] = val, "x30" => self.iregs[30] = val, "pc" => self.pc = val, "sp" => self.sp = val, "lr" => self.iregs[md::Arm64RegisterNumbers::LinkRegister as usize] = val, "fp" => self.iregs[md::Arm64RegisterNumbers::FramePointer as usize] = val, _ => return None, } Some(()) } fn stack_pointer_register_name(&self) -> &'static str { "sp" } fn instruction_pointer_register_name(&self) -> &'static str { "pc" } } impl CpuContext for md::CONTEXT_ARM64 { type Register = u64; const REGISTERS: &'static [&'static str] = &[ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "fp", "lr", "sp", "pc", ]; fn memoize_register(&self, reg: &str) -> Option<&'static str> { match reg { "x29" => Some("fp"), "x30" => Some("lr"), _ => default_memoize_register(Self::REGISTERS, reg), } } fn register_is_valid(&self, reg: &str, valid: &MinidumpContextValidity) -> bool { if let MinidumpContextValidity::Some(ref which) = valid { match reg { "x29" | "fp" => which.contains("x29") || which.contains("fp"), "x30" | "lr" => which.contains("x30") || which.contains("lr"), _ => which.contains(reg), } } else { self.memoize_register(reg).is_some() } } fn get_register_always(&self, reg: &str) -> u64 { match reg { "x0" => self.iregs[0], "x1" => self.iregs[1], "x2" => self.iregs[2], "x3" => self.iregs[3], "x4" => self.iregs[4], "x5" => self.iregs[5], "x6" => self.iregs[6], "x7" => self.iregs[7], "x8" => self.iregs[8], "x9" => self.iregs[9], "x10" => self.iregs[10], "x11" => self.iregs[11], "x12" => self.iregs[12], "x13" => self.iregs[13], "x14" => self.iregs[14], "x15" => self.iregs[15], "x16" => self.iregs[16], "x17" => self.iregs[17], "x18" => self.iregs[18], "x19" => self.iregs[19], "x20" => self.iregs[20], "x21" => self.iregs[21], "x22" => self.iregs[22], "x23" => self.iregs[23], "x24" => self.iregs[24], "x25" => self.iregs[25], "x26" => self.iregs[26], "x27" => self.iregs[27], "x28" => self.iregs[28], "x29" => self.iregs[29], "x30" => self.iregs[30], "pc" => self.pc, "sp" => self.sp, "lr" => self.iregs[md::Arm64RegisterNumbers::LinkRegister as usize], "fp" => self.iregs[md::Arm64RegisterNumbers::FramePointer as usize], _ => unreachable!("Invalid aarch64 register! {}", reg), } } fn set_register(&mut self, reg: &str, val: Self::Register) -> Option<()> { match reg { "x0" => self.iregs[0] = val, "x1" => self.iregs[1] = val, "x2" => self.iregs[2] = val, "x3" => self.iregs[3] = val, "x4" => self.iregs[4] = val, "x5" => self.iregs[5] = val, "x6" => self.iregs[6] = val, "x7" => self.iregs[7] = val, "x8" => self.iregs[8] = val, "x9" => self.iregs[9] = val, "x10" => self.iregs[10] = val, "x11" => self.iregs[11] = val, "x12" => self.iregs[12] = val, "x13" => self.iregs[13] = val, "x14" => self.iregs[14] = val, "x15" => self.iregs[15] = val, "x16" => self.iregs[16] = val, "x17" => self.iregs[17] = val, "x18" => self.iregs[18] = val, "x19" => self.iregs[19] = val, "x20" => self.iregs[20] = val, "x21" => self.iregs[21] = val, "x22" => self.iregs[22] = val, "x23" => self.iregs[23] = val, "x24" => self.iregs[24] = val, "x25" => self.iregs[25] = val, "x26" => self.iregs[26] = val, "x27" => self.iregs[27] = val, "x28" => self.iregs[28] = val, "x29" => self.iregs[29] = val, "x30" => self.iregs[30] = val, "pc" => self.pc = val, "sp" => self.sp = val, "lr" => self.iregs[md::Arm64RegisterNumbers::LinkRegister as usize] = val, "fp" => self.iregs[md::Arm64RegisterNumbers::FramePointer as usize] = val, _ => return None, } Some(()) } fn stack_pointer_register_name(&self) -> &'static str { "sp" } fn instruction_pointer_register_name(&self) -> &'static str { "pc" } } impl CpuContext for md::CONTEXT_PPC { type Register = u32; const REGISTERS: &'static [&'static str] = &[ "srr0", "srr1", "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "cr", "xer", "lr", "ctr", "mq", "vrsave", ]; fn get_register_always(&self, reg: &str) -> Self::Register { match reg { "srr0" => self.srr0, "srr1" => self.srr1, "r0" => self.gpr[0], "r1" => self.gpr[1], "r2" => self.gpr[2], "r3" => self.gpr[3], "r4" => self.gpr[4], "r5" => self.gpr[5], "r6" => self.gpr[6], "r7" => self.gpr[7], "r8" => self.gpr[8], "r9" => self.gpr[9], "r10" => self.gpr[10], "r11" => self.gpr[11], "r12" => self.gpr[12], "r13" => self.gpr[13], "r14" => self.gpr[14], "r15" => self.gpr[15], "r16" => self.gpr[16], "r17" => self.gpr[17], "r18" => self.gpr[18], "r19" => self.gpr[19], "r20" => self.gpr[20], "r21" => self.gpr[21], "r22" => self.gpr[22], "r23" => self.gpr[23], "r24" => self.gpr[24], "r25" => self.gpr[25], "r26" => self.gpr[26], "r27" => self.gpr[27], "r28" => self.gpr[28], "r29" => self.gpr[29], "r30" => self.gpr[30], "r31" => self.gpr[31], "cr" => self.cr, "xer" => self.xer, "lr" => self.lr, "ctr" => self.ctr, "mq" => self.mq, "vrsave" => self.vrsave, _ => unreachable!("Invalid ppc register! {}", reg), } } fn set_register(&mut self, reg: &str, val: Self::Register) -> Option<()> { match reg { "srr0" => self.srr0 = val, "srr1" => self.srr1 = val, "r0" => self.gpr[0] = val, "r1" => self.gpr[1] = val, "r2" => self.gpr[2] = val, "r3" => self.gpr[3] = val, "r4" => self.gpr[4] = val, "r5" => self.gpr[5] = val, "r6" => self.gpr[6] = val, "r7" => self.gpr[7] = val, "r8" => self.gpr[8] = val, "r9" => self.gpr[9] = val, "r10" => self.gpr[10] = val, "r11" => self.gpr[11] = val, "r12" => self.gpr[12] = val, "r13" => self.gpr[13] = val, "r14" => self.gpr[14] = val, "r15" => self.gpr[15] = val, "r16" => self.gpr[16] = val, "r17" => self.gpr[17] = val, "r18" => self.gpr[18] = val, "r19" => self.gpr[19] = val, "r20" => self.gpr[20] = val, "r21" => self.gpr[21] = val, "r22" => self.gpr[22] = val, "r23" => self.gpr[23] = val, "r24" => self.gpr[24] = val, "r25" => self.gpr[25] = val, "r26" => self.gpr[26] = val, "r27" => self.gpr[27] = val, "r28" => self.gpr[28] = val, "r29" => self.gpr[29] = val, "r30" => self.gpr[30] = val, "r31" => self.gpr[31] = val, "cr" => self.cr = val, "xer" => self.xer = val, "lr" => self.lr = val, "ctr" => self.ctr = val, "mq" => self.mq = val, "vrsave" => self.vrsave = val, _ => return None, } Some(()) } fn stack_pointer_register_name(&self) -> &'static str { "r1" } fn instruction_pointer_register_name(&self) -> &'static str { "srr0" } } impl CpuContext for md::CONTEXT_PPC64 { type Register = u64; const REGISTERS: &'static [&'static str] = &[ "srr0", "srr1", "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "cr", "xer", "lr", "ctr", "vrsave", ]; fn get_register_always(&self, reg: &str) -> Self::Register { match reg { "srr0" => self.srr0, "srr1" => self.srr1, "r0" => self.gpr[0], "r1" => self.gpr[1], "r2" => self.gpr[2], "r3" => self.gpr[3], "r4" => self.gpr[4], "r5" => self.gpr[5], "r6" => self.gpr[6], "r7" => self.gpr[7], "r8" => self.gpr[8], "r9" => self.gpr[9], "r10" => self.gpr[10], "r11" => self.gpr[11], "r12" => self.gpr[12], "r13" => self.gpr[13], "r14" => self.gpr[14], "r15" => self.gpr[15], "r16" => self.gpr[16], "r17" => self.gpr[17], "r18" => self.gpr[18], "r19" => self.gpr[19], "r20" => self.gpr[20], "r21" => self.gpr[21], "r22" => self.gpr[22], "r23" => self.gpr[23], "r24" => self.gpr[24], "r25" => self.gpr[25], "r26" => self.gpr[26], "r27" => self.gpr[27], "r28" => self.gpr[28], "r29" => self.gpr[29], "r30" => self.gpr[30], "r31" => self.gpr[31], "cr" => self.cr, "xer" => self.xer, "lr" => self.lr, "ctr" => self.ctr, "vrsave" => self.vrsave, _ => unreachable!("Invalid ppc64 register! {}", reg), } } fn set_register(&mut self, reg: &str, val: Self::Register) -> Option<()> { match reg { "srr0" => self.srr0 = val, "srr1" => self.srr1 = val, "r0" => self.gpr[0] = val, "r1" => self.gpr[1] = val, "r2" => self.gpr[2] = val, "r3" => self.gpr[3] = val, "r4" => self.gpr[4] = val, "r5" => self.gpr[5] = val, "r6" => self.gpr[6] = val, "r7" => self.gpr[7] = val, "r8" => self.gpr[8] = val, "r9" => self.gpr[9] = val, "r10" => self.gpr[10] = val, "r11" => self.gpr[11] = val, "r12" => self.gpr[12] = val, "r13" => self.gpr[13] = val, "r14" => self.gpr[14] = val, "r15" => self.gpr[15] = val, "r16" => self.gpr[16] = val, "r17" => self.gpr[17] = val, "r18" => self.gpr[18] = val, "r19" => self.gpr[19] = val, "r20" => self.gpr[20] = val, "r21" => self.gpr[21] = val, "r22" => self.gpr[22] = val, "r23" => self.gpr[23] = val, "r24" => self.gpr[24] = val, "r25" => self.gpr[25] = val, "r26" => self.gpr[26] = val, "r27" => self.gpr[27] = val, "r28" => self.gpr[28] = val, "r29" => self.gpr[29] = val, "r30" => self.gpr[30] = val, "r31" => self.gpr[31] = val, "cr" => self.cr = val, "xer" => self.xer = val, "lr" => self.lr = val, "ctr" => self.ctr = val, "vrsave" => self.vrsave = val, _ => return None, } Some(()) } fn stack_pointer_register_name(&self) -> &'static str { "r1" } fn instruction_pointer_register_name(&self) -> &'static str { "srr0" } } impl CpuContext for md::CONTEXT_MIPS { type Register = u64; const REGISTERS: &'static [&'static str] = &[ "gp", "sp", "fp", "ra", "pc", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", ]; fn get_register_always(&self, reg: &str) -> Self::Register { match reg { "gp" => self.iregs[md::MipsRegisterNumbers::GlobalPointer as usize], "sp" => self.iregs[md::MipsRegisterNumbers::StackPointer as usize], "fp" => self.iregs[md::MipsRegisterNumbers::FramePointer as usize], "ra" => self.iregs[md::MipsRegisterNumbers::ReturnAddress as usize], "pc" => self.epc, "s0" => self.iregs[md::MipsRegisterNumbers::S0 as usize], "s1" => self.iregs[md::MipsRegisterNumbers::S1 as usize], "s2" => self.iregs[md::MipsRegisterNumbers::S2 as usize], "s3" => self.iregs[md::MipsRegisterNumbers::S3 as usize], "s4" => self.iregs[md::MipsRegisterNumbers::S4 as usize], "s5" => self.iregs[md::MipsRegisterNumbers::S5 as usize], "s6" => self.iregs[md::MipsRegisterNumbers::S6 as usize], "s7" => self.iregs[md::MipsRegisterNumbers::S7 as usize], _ => unreachable!("Invalid mips register! {}", reg), } } fn set_register(&mut self, reg: &str, val: Self::Register) -> Option<()> { match reg { "gp" => self.iregs[md::MipsRegisterNumbers::GlobalPointer as usize] = val, "sp" => self.iregs[md::MipsRegisterNumbers::StackPointer as usize] = val, "fp" => self.iregs[md::MipsRegisterNumbers::FramePointer as usize] = val, "ra" => self.iregs[md::MipsRegisterNumbers::ReturnAddress as usize] = val, "pc" => self.epc = val, "s0" => self.iregs[md::MipsRegisterNumbers::S0 as usize] = val, "s1" => self.iregs[md::MipsRegisterNumbers::S1 as usize] = val, "s2" => self.iregs[md::MipsRegisterNumbers::S2 as usize] = val, "s3" => self.iregs[md::MipsRegisterNumbers::S3 as usize] = val, "s4" => self.iregs[md::MipsRegisterNumbers::S4 as usize] = val, "s5" => self.iregs[md::MipsRegisterNumbers::S5 as usize] = val, "s6" => self.iregs[md::MipsRegisterNumbers::S6 as usize] = val, "s7" => self.iregs[md::MipsRegisterNumbers::S7 as usize] = val, _ => return None, } Some(()) } fn stack_pointer_register_name(&self) -> &'static str { "sp" } fn instruction_pointer_register_name(&self) -> &'static str { "pc" } } impl CpuContext for md::CONTEXT_SPARC { type Register = u64; const REGISTERS: &'static [&'static str] = &[ "g_r0", "g_r1", "g_r2", "g_r3", "g_r4", "g_r5", "g_r6", "g_r7", "g_r8", "g_r9", "g_r10", "g_r11", "g_r12", "g_r13", "g_r14", "g_r15", "g_r16", "g_r17", "g_r18", "g_r19", "g_r20", "g_r21", "g_r22", "g_r23", "g_r24", "g_r25", "g_r26", "g_r27", "g_r28", "g_r29", "g_r30", "g_r31", "ccr", "pc", "npc", "y", "asi", "fprs", ]; fn get_register_always(&self, reg: &str) -> Self::Register { match reg { "g_r0" | "g0" => self.g_r[0], "g_r1" | "g1" => self.g_r[1], "g_r2" | "g2" => self.g_r[2], "g_r3" | "g3" => self.g_r[3], "g_r4" | "g4" => self.g_r[4], "g_r5" | "g5" => self.g_r[5], "g_r6" | "g6" => self.g_r[6], "g_r7" | "g7" => self.g_r[7], "g_r8" | "o0" => self.g_r[8], "g_r9" | "o1" => self.g_r[9], "g_r10" | "o2" => self.g_r[10], "g_r11" | "o3" => self.g_r[11], "g_r12" | "o4" => self.g_r[12], "g_r13" | "o5" => self.g_r[13], "g_r14" | "o6" => self.g_r[14], "g_r15" | "o7" => self.g_r[15], "g_r16" | "l0" => self.g_r[16], "g_r17" | "l1" => self.g_r[17], "g_r18" | "l2" => self.g_r[18], "g_r19" | "l3" => self.g_r[19], "g_r20" | "l4" => self.g_r[20], "g_r21" | "l5" => self.g_r[21], "g_r22" | "l6" => self.g_r[22], "g_r23" | "l7" => self.g_r[23], "g_r24" | "i0" => self.g_r[24], "g_r25" | "i1" => self.g_r[25], "g_r26" | "i2" => self.g_r[26], "g_r27" | "i3" => self.g_r[27], "g_r28" | "i4" => self.g_r[28], "g_r29" | "i5" => self.g_r[29], "g_r30" | "i6" => self.g_r[30], "g_r31" | "i7" => self.g_r[31], "ccr" => self.ccr, "pc" => self.pc, "npc" => self.npc, "y" => self.y, "asi" => self.asi, "fprs" => self.fprs, _ => unreachable!("Invalid sparc register! {}", reg), } } fn set_register(&mut self, reg: &str, val: Self::Register) -> Option<()> { match reg { "g_r0" | "g0" => self.g_r[0] = val, "g_r1" | "g1" => self.g_r[1] = val, "g_r2" | "g2" => self.g_r[2] = val, "g_r3" | "g3" => self.g_r[3] = val, "g_r4" | "g4" => self.g_r[4] = val, "g_r5" | "g5" => self.g_r[5] = val, "g_r6" | "g6" => self.g_r[6] = val, "g_r7" | "g7" => self.g_r[7] = val, "g_r8" | "o0" => self.g_r[8] = val, "g_r9" | "o1" => self.g_r[9] = val, "g_r10" | "o2" => self.g_r[10] = val, "g_r11" | "o3" => self.g_r[11] = val, "g_r12" | "o4" => self.g_r[12] = val, "g_r13" | "o5" => self.g_r[13] = val, "g_r14" | "o6" => self.g_r[14] = val, "g_r15" | "o7" => self.g_r[15] = val, "g_r16" | "l0" => self.g_r[16] = val, "g_r17" | "l1" => self.g_r[17] = val, "g_r18" | "l2" => self.g_r[18] = val, "g_r19" | "l3" => self.g_r[19] = val, "g_r20" | "l4" => self.g_r[20] = val, "g_r21" | "l5" => self.g_r[21] = val, "g_r22" | "l6" => self.g_r[22] = val, "g_r23" | "l7" => self.g_r[23] = val, "g_r24" | "i0" => self.g_r[24] = val, "g_r25" | "i1" => self.g_r[25] = val, "g_r26" | "i2" => self.g_r[26] = val, "g_r27" | "i3" => self.g_r[27] = val, "g_r28" | "i4" => self.g_r[28] = val, "g_r29" | "i5" => self.g_r[29] = val, "g_r30" | "i6" => self.g_r[30] = val, "g_r31" | "i7" => self.g_r[31] = val, "ccr" => self.ccr = val, "pc" => self.pc = val, "npc" => self.npc = val, "y" => self.y = val, "asi" => self.asi = val, "fprs" => self.fprs = val, _ => return None, } Some(()) } fn stack_pointer_register_name(&self) -> &'static str { "g_r14" // alias out register o6 } fn instruction_pointer_register_name(&self) -> &'static str { "pc" } } /// Information about which registers are valid in a `MinidumpContext`. #[derive(Clone, Debug, PartialEq, Eq)] pub enum MinidumpContextValidity { // All registers are valid. All, // The registers in this set are valid. Some(HashSet<&'static str>), } /// CPU context such as register states. /// /// MinidumpContext carries a CPU-specific MDRawContext structure, which /// contains CPU context such as register states. Each thread has its /// own context, and the exception record, if present, also has its own /// context. Note that if the exception record is present, the context it /// refers to is probably what the user wants to use for the exception /// thread, instead of that thread's own context. The exception thread's /// context (as opposed to the exception record's context) will contain /// context for the exception handler (which performs minidump generation), /// and not the context that caused the exception (which is probably what the /// user wants). #[derive(Debug, Clone)] pub struct MinidumpContext { /// The raw CPU register state. pub raw: MinidumpRawContext, /// Which registers are valid in `raw`. pub valid: MinidumpContextValidity, } /// Errors encountered while reading a `MinidumpContext`. #[derive(Debug)] pub enum ContextError { /// Failed to read data. ReadFailure, /// Encountered an unknown CPU context. UnknownCpuContext, } //====================================================== // Implementations impl MinidumpContext { /// Return a MinidumpContext given a `MinidumpRawContext`. pub fn from_raw(raw: MinidumpRawContext) -> MinidumpContext { MinidumpContext { raw, valid: MinidumpContextValidity::All, } } /// Read a `MinidumpContext` from `bytes`. pub fn read( bytes: &[u8], endian: scroll::Endian, system_info: &MinidumpSystemInfo, _misc: Option<&MinidumpMiscInfo>, ) -> Result { use md::ProcessorArchitecture::*; let mut offset = 0; // Although every context contains `context_flags` which tell us what kind // ok context we're handling, they aren't all in the same location, so we // need to use SystemInfo to choose what kind of context to parse this as. // We can then use the `context_flags` to validate our parse. // We need to use the raw processor_architecture because system_info.cpu // flattens away some key distinctions for this code. match md::ProcessorArchitecture::from_u16(system_info.raw.processor_architecture) { Some(PROCESSOR_ARCHITECTURE_INTEL) | Some(PROCESSOR_ARCHITECTURE_IA32_ON_WIN64) => { // Not 100% sure IA32_ON_WIN64 is this format, but let's assume so? let ctx: md::CONTEXT_X86 = bytes .gread_with(&mut offset, endian) .or(Err(ContextError::ReadFailure))?; let flags = ContextFlagsCpu::from_flags(ctx.context_flags); if flags == ContextFlagsCpu::CONTEXT_X86 { if ctx.context_flags & md::CONTEXT_HAS_XSTATE != 0 { // FIXME: uses MISC_INFO_5 to parse out extra sections here warn!("Cpu context has extra XSTATE that is being ignored"); } Ok(MinidumpContext::from_raw(MinidumpRawContext::X86(ctx))) } else { Err(ContextError::ReadFailure) } } Some(PROCESSOR_ARCHITECTURE_AMD64) => { let ctx: md::CONTEXT_AMD64 = bytes .gread_with(&mut offset, endian) .or(Err(ContextError::ReadFailure))?; let flags = ContextFlagsCpu::from_flags(ctx.context_flags); if flags == ContextFlagsCpu::CONTEXT_AMD64 { if ctx.context_flags & md::CONTEXT_HAS_XSTATE != 0 { // FIXME: uses MISC_INFO_5 to parse out extra sections here warn!("Cpu context has extra XSTATE that is being ignored"); } Ok(MinidumpContext::from_raw(MinidumpRawContext::Amd64(ctx))) } else { Err(ContextError::ReadFailure) } } Some(PROCESSOR_ARCHITECTURE_PPC) => { let ctx: md::CONTEXT_PPC = bytes .gread_with(&mut offset, endian) .or(Err(ContextError::ReadFailure))?; let flags = ContextFlagsCpu::from_flags(ctx.context_flags); if flags == ContextFlagsCpu::CONTEXT_PPC { Ok(MinidumpContext::from_raw(MinidumpRawContext::Ppc(ctx))) } else { Err(ContextError::ReadFailure) } } Some(PROCESSOR_ARCHITECTURE_PPC64) => { let ctx: md::CONTEXT_PPC64 = bytes .gread_with(&mut offset, endian) .or(Err(ContextError::ReadFailure))?; let flags = ContextFlagsCpu::from_flags(ctx.context_flags as u32); if flags == ContextFlagsCpu::CONTEXT_PPC64 { Ok(MinidumpContext::from_raw(MinidumpRawContext::Ppc64(ctx))) } else { Err(ContextError::ReadFailure) } } Some(PROCESSOR_ARCHITECTURE_SPARC) => { let ctx: md::CONTEXT_SPARC = bytes .gread_with(&mut offset, endian) .or(Err(ContextError::ReadFailure))?; let flags = ContextFlagsCpu::from_flags(ctx.context_flags); if flags == ContextFlagsCpu::CONTEXT_SPARC { Ok(MinidumpContext::from_raw(MinidumpRawContext::Sparc(ctx))) } else { Err(ContextError::ReadFailure) } } Some(PROCESSOR_ARCHITECTURE_ARM) => { let ctx: md::CONTEXT_ARM = bytes .gread_with(&mut offset, endian) .or(Err(ContextError::ReadFailure))?; let flags = ContextFlagsCpu::from_flags(ctx.context_flags); if flags == ContextFlagsCpu::CONTEXT_ARM { Ok(MinidumpContext::from_raw(MinidumpRawContext::Arm(ctx))) } else { Err(ContextError::ReadFailure) } } Some(PROCESSOR_ARCHITECTURE_ARM64) => { let ctx: md::CONTEXT_ARM64 = bytes .gread_with(&mut offset, endian) .or(Err(ContextError::ReadFailure))?; let flags = ContextFlagsCpu::from_flags(ctx.context_flags); if flags == ContextFlagsCpu::CONTEXT_ARM64 { Ok(MinidumpContext::from_raw(MinidumpRawContext::Arm64(ctx))) } else { Err(ContextError::ReadFailure) } } Some(PROCESSOR_ARCHITECTURE_ARM64_OLD) => { let ctx: md::CONTEXT_ARM64_OLD = bytes .gread_with(&mut offset, endian) .or(Err(ContextError::ReadFailure))?; let flags = ContextFlagsCpu::from_flags(ctx.context_flags as u32); if flags == ContextFlagsCpu::CONTEXT_ARM64_OLD { Ok(MinidumpContext::from_raw(MinidumpRawContext::OldArm64(ctx))) } else { Err(ContextError::ReadFailure) } } Some(PROCESSOR_ARCHITECTURE_MIPS) => { let ctx: md::CONTEXT_MIPS = bytes .gread_with(&mut offset, endian) .or(Err(ContextError::ReadFailure))?; let flags = ContextFlagsCpu::from_flags(ctx.context_flags); if flags == ContextFlagsCpu::CONTEXT_MIPS { Ok(MinidumpContext::from_raw(MinidumpRawContext::Mips(ctx))) } else { Err(ContextError::ReadFailure) } } _ => Err(ContextError::UnknownCpuContext), } } pub fn get_instruction_pointer(&self) -> u64 { match self.raw { MinidumpRawContext::Amd64(ref ctx) => ctx.rip, MinidumpRawContext::Arm(ref ctx) => { ctx.iregs[md::ArmRegisterNumbers::ProgramCounter as usize] as u64 } MinidumpRawContext::Arm64(ref ctx) => ctx.pc, MinidumpRawContext::OldArm64(ref ctx) => ctx.pc, MinidumpRawContext::Ppc(ref ctx) => ctx.srr0 as u64, MinidumpRawContext::Ppc64(ref ctx) => ctx.srr0, MinidumpRawContext::Sparc(ref ctx) => ctx.pc, MinidumpRawContext::X86(ref ctx) => ctx.eip as u64, MinidumpRawContext::Mips(ref ctx) => ctx.epc, } } pub fn get_stack_pointer(&self) -> u64 { match self.raw { MinidumpRawContext::Amd64(ref ctx) => ctx.rsp, MinidumpRawContext::Arm(ref ctx) => { ctx.iregs[md::ArmRegisterNumbers::StackPointer as usize] as u64 } MinidumpRawContext::Arm64(ref ctx) => ctx.sp, MinidumpRawContext::OldArm64(ref ctx) => ctx.sp, MinidumpRawContext::Ppc(ref ctx) => { ctx.gpr[md::PpcRegisterNumbers::StackPointer as usize] as u64 } MinidumpRawContext::Ppc64(ref ctx) => { ctx.gpr[md::Ppc64RegisterNumbers::StackPointer as usize] } MinidumpRawContext::Sparc(ref ctx) => { ctx.g_r[md::SparcRegisterNumbers::StackPointer as usize] } MinidumpRawContext::X86(ref ctx) => ctx.esp as u64, MinidumpRawContext::Mips(ref ctx) => { ctx.iregs[md::MipsRegisterNumbers::StackPointer as usize] } } } pub fn get_register_always(&self, reg: &str) -> u64 { match self.raw { MinidumpRawContext::Amd64(ref ctx) => ctx.get_register_always(reg), MinidumpRawContext::Arm(ref ctx) => ctx.get_register_always(reg).into(), MinidumpRawContext::Arm64(ref ctx) => ctx.get_register_always(reg), MinidumpRawContext::OldArm64(ref ctx) => ctx.get_register_always(reg), MinidumpRawContext::Ppc(ref ctx) => ctx.get_register_always(reg).into(), MinidumpRawContext::Ppc64(ref ctx) => ctx.get_register_always(reg), MinidumpRawContext::Sparc(ref ctx) => ctx.get_register_always(reg), MinidumpRawContext::X86(ref ctx) => ctx.get_register_always(reg).into(), MinidumpRawContext::Mips(ref ctx) => ctx.get_register_always(reg), } } pub fn get_register(&self, reg: &str) -> Option { let valid = match &self.raw { MinidumpRawContext::X86(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Ppc(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Ppc64(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Amd64(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Sparc(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Arm(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Arm64(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::OldArm64(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Mips(ctx) => ctx.register_is_valid(reg, &self.valid), }; if valid { Some(self.get_register_always(reg)) } else { None } } pub fn format_register(&self, reg: &str) -> String { match self.raw { MinidumpRawContext::Amd64(ref ctx) => ctx.format_register(reg), MinidumpRawContext::Arm(ref ctx) => ctx.format_register(reg), MinidumpRawContext::Arm64(ref ctx) => ctx.format_register(reg), MinidumpRawContext::OldArm64(ref ctx) => ctx.format_register(reg), MinidumpRawContext::Ppc(ref ctx) => ctx.format_register(reg), MinidumpRawContext::Ppc64(ref ctx) => ctx.format_register(reg), MinidumpRawContext::Sparc(ref ctx) => ctx.format_register(reg), MinidumpRawContext::X86(ref ctx) => ctx.format_register(reg), MinidumpRawContext::Mips(ref ctx) => ctx.format_register(reg), } } pub fn general_purpose_registers(&self) -> &'static [&'static str] { match self.raw { MinidumpRawContext::Amd64(_) => md::CONTEXT_AMD64::REGISTERS, MinidumpRawContext::Arm(_) => md::CONTEXT_ARM::REGISTERS, MinidumpRawContext::Arm64(_) => md::CONTEXT_ARM64::REGISTERS, MinidumpRawContext::OldArm64(_) => md::CONTEXT_ARM64::REGISTERS, MinidumpRawContext::Ppc(_) => md::CONTEXT_PPC::REGISTERS, MinidumpRawContext::Ppc64(_) => md::CONTEXT_PPC64::REGISTERS, MinidumpRawContext::Sparc(_) => md::CONTEXT_SPARC::REGISTERS, MinidumpRawContext::X86(_) => md::CONTEXT_X86::REGISTERS, MinidumpRawContext::Mips(_) => md::CONTEXT_MIPS::REGISTERS, } } pub fn registers(&self) -> impl Iterator + '_ { self.general_purpose_registers() .iter() .map(move |®| (reg, self.get_register_always(reg))) } pub fn valid_registers(&self) -> impl Iterator + '_ { // This is suboptimal in theory, as we could iterate over self.valid just like the original // and faster `CpuRegisters` iterator does. However, this complicates code here, and the // minimal gain in performance hasn't been worth the added complexity. self.registers().filter(move |(reg, _)| match &self.raw { MinidumpRawContext::X86(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Ppc(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Ppc64(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Amd64(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Sparc(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Arm(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Arm64(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::OldArm64(ctx) => ctx.register_is_valid(reg, &self.valid), MinidumpRawContext::Mips(ctx) => ctx.register_is_valid(reg, &self.valid), }) } /// Get the size (in bytes) of general-purpose registers. pub fn register_size(&self) -> usize { fn get(_: &T) -> usize { std::mem::size_of::() } match &self.raw { MinidumpRawContext::X86(ctx) => get(ctx), MinidumpRawContext::Ppc(ctx) => get(ctx), MinidumpRawContext::Ppc64(ctx) => get(ctx), MinidumpRawContext::Amd64(ctx) => get(ctx), MinidumpRawContext::Sparc(ctx) => get(ctx), MinidumpRawContext::Arm(ctx) => get(ctx), MinidumpRawContext::Arm64(ctx) => get(ctx), MinidumpRawContext::OldArm64(ctx) => get(ctx), MinidumpRawContext::Mips(ctx) => get(ctx), } } /// Write a human-readable description of this `MinidumpContext` to `f`. /// /// This is very verbose, it is the format used by `minidump_dump`. pub fn print(&self, f: &mut T) -> io::Result<()> { match self.raw { MinidumpRawContext::X86(ref raw) => { write!( f, r#"CONTEXT_X86 context_flags = {:#x} dr0 = {:#x} dr1 = {:#x} dr2 = {:#x} dr3 = {:#x} dr6 = {:#x} dr7 = {:#x} float_save.control_word = {:#x} float_save.status_word = {:#x} float_save.tag_word = {:#x} float_save.error_offset = {:#x} float_save.error_selector = {:#x} float_save.data_offset = {:#x} float_save.data_selector = {:#x} float_save.register_area[{:2}] = 0x"#, raw.context_flags, raw.dr0, raw.dr1, raw.dr2, raw.dr3, raw.dr6, raw.dr7, raw.float_save.control_word, raw.float_save.status_word, raw.float_save.tag_word, raw.float_save.error_offset, raw.float_save.error_selector, raw.float_save.data_offset, raw.float_save.data_selector, raw.float_save.register_area.len(), )?; write_bytes(f, &raw.float_save.register_area)?; writeln!(f)?; write!( f, r#" float_save.cr0_npx_state = {:#x} gs = {:#x} fs = {:#x} es = {:#x} ds = {:#x} edi = {:#x} esi = {:#x} ebx = {:#x} edx = {:#x} ecx = {:#x} eax = {:#x} ebp = {:#x} eip = {:#x} cs = {:#x} eflags = {:#x} esp = {:#x} ss = {:#x} extended_registers[{:3}] = 0x"#, raw.float_save.cr0_npx_state, raw.gs, raw.fs, raw.es, raw.ds, raw.edi, raw.esi, raw.ebx, raw.edx, raw.ecx, raw.eax, raw.ebp, raw.eip, raw.cs, raw.eflags, raw.esp, raw.ss, raw.extended_registers.len(), )?; write_bytes(f, &raw.extended_registers)?; write!(f, "\n\n")?; } MinidumpRawContext::Ppc(_) => { unimplemented!(); } MinidumpRawContext::Ppc64(_) => { unimplemented!(); } MinidumpRawContext::Amd64(ref raw) => { write!( f, r#"CONTEXT_AMD64 p1_home = {:#x} p2_home = {:#x} p3_home = {:#x} p4_home = {:#x} p5_home = {:#x} p6_home = {:#x} context_flags = {:#x} mx_csr = {:#x} cs = {:#x} ds = {:#x} es = {:#x} fs = {:#x} gs = {:#x} ss = {:#x} eflags = {:#x} dr0 = {:#x} dr1 = {:#x} dr2 = {:#x} dr3 = {:#x} dr6 = {:#x} dr7 = {:#x} rax = {:#x} rcx = {:#x} rdx = {:#x} rbx = {:#x} rsp = {:#x} rbp = {:#x} rsi = {:#x} rdi = {:#x} r8 = {:#x} r9 = {:#x} r10 = {:#x} r11 = {:#x} r12 = {:#x} r13 = {:#x} r14 = {:#x} r15 = {:#x} rip = {:#x} "#, raw.p1_home, raw.p2_home, raw.p3_home, raw.p4_home, raw.p5_home, raw.p6_home, raw.context_flags, raw.mx_csr, raw.cs, raw.ds, raw.es, raw.fs, raw.gs, raw.ss, raw.eflags, raw.dr0, raw.dr1, raw.dr2, raw.dr3, raw.dr6, raw.dr7, raw.rax, raw.rcx, raw.rdx, raw.rbx, raw.rsp, raw.rbp, raw.rsi, raw.rdi, raw.r8, raw.r9, raw.r10, raw.r11, raw.r12, raw.r13, raw.r14, raw.r15, raw.rip, )?; } MinidumpRawContext::Sparc(_) => { unimplemented!(); } MinidumpRawContext::Arm(ref raw) => { write!( f, r#"CONTEXT_ARM context_flags = {:#x} "#, raw.context_flags )?; for (i, reg) in raw.iregs.iter().enumerate() { writeln!(f, " iregs[{i:2}] = {reg:#x}")?; } write!( f, r#" cpsr = {:#x} float_save.fpscr = {:#x} "#, raw.cpsr, raw.float_save.fpscr )?; for (i, reg) in raw.float_save.regs.iter().enumerate() { writeln!(f, " float_save.regs[{i:2}] = {reg:#x}")?; } for (i, reg) in raw.float_save.extra.iter().enumerate() { writeln!(f, " float_save.extra[{i:2}] = {reg:#x}")?; } } MinidumpRawContext::Arm64(ref raw) => { write!( f, r#"CONTEXT_ARM64 context_flags = {:#x} "#, raw.context_flags )?; for (i, reg) in raw.iregs[..29].iter().enumerate() { writeln!(f, " x{i:<2} = {reg:#x}")?; } writeln!(f, " x29 (fp) = {:#x}", raw.iregs[29])?; writeln!(f, " x30 (lr) = {:#x}", raw.iregs[30])?; writeln!(f, " sp = {:#x}", raw.sp)?; writeln!(f, " pc = {:#x}", raw.pc)?; writeln!(f, " cpsr = {:#x}", raw.cpsr)?; writeln!(f, " fpsr = {:#x}", raw.fpsr)?; writeln!(f, " fpcr = {:#x}", raw.fpcr)?; for (i, reg) in raw.float_regs.iter().enumerate() { writeln!(f, " d{i:<2} = {reg:#x}")?; } for (i, reg) in raw.bcr.iter().enumerate() { writeln!(f, " bcr[{i:2}] = {reg:#x}")?; } for (i, reg) in raw.bvr.iter().enumerate() { writeln!(f, " bvr[{i:2}] = {reg:#x}")?; } for (i, reg) in raw.wcr.iter().enumerate() { writeln!(f, " wcr[{i:2}] = {reg:#x}")?; } for (i, reg) in raw.wvr.iter().enumerate() { writeln!(f, " wvr[{i:2}] = {reg:#x}")?; } } MinidumpRawContext::OldArm64(ref raw) => { write!( f, r#"CONTEXT_ARM64_OLD context_flags = {:#x} "#, raw.context_flags )?; for (i, reg) in raw.iregs[..29].iter().enumerate() { writeln!(f, " x{i:<2} = {reg:#x}")?; } writeln!(f, " x29 (fp) = {:#x}", raw.iregs[29])?; writeln!(f, " x30 (lr) = {:#x}", raw.iregs[30])?; writeln!(f, " sp = {:#x}", raw.sp)?; writeln!(f, " pc = {:#x}", raw.pc)?; writeln!(f, " cpsr = {:#x}", raw.cpsr)?; writeln!(f, " fpsr = {:#x}", raw.fpsr)?; writeln!(f, " fpcr = {:#x}", raw.fpcr)?; for (i, reg) in raw.float_regs.iter().enumerate() { writeln!(f, " d{i:<2} = {reg:#x}")?; } } MinidumpRawContext::Mips(ref raw) => { write!( f, r#"CONTEXT_MIPS context_flags = {:#x} "#, raw.context_flags )?; use md::MipsRegisterNumbers; const MIPS_REGS: &[MipsRegisterNumbers] = &[ MipsRegisterNumbers::S0, MipsRegisterNumbers::S1, MipsRegisterNumbers::S2, MipsRegisterNumbers::S3, MipsRegisterNumbers::S4, MipsRegisterNumbers::S5, MipsRegisterNumbers::S6, MipsRegisterNumbers::S7, MipsRegisterNumbers::GlobalPointer, MipsRegisterNumbers::StackPointer, MipsRegisterNumbers::FramePointer, MipsRegisterNumbers::ReturnAddress, ]; for reg in MIPS_REGS { writeln!( f, r#" {} = {:#x}"#, reg.name(), raw.iregs[*reg as usize] )?; } } } Ok(()) } } #[cfg(test)] mod tests { use super::*; #[test] /// Smoke test for the default implementation of `memoize_register`. fn test_memoize_amd64() { let context = md::CONTEXT_AMD64::default(); assert_eq!(context.memoize_register("rip"), Some("rip")); assert_eq!(context.memoize_register("foo"), None); } #[test] /// Test ARM register aliases by example of `fp`. fn test_memoize_arm_alias() { let context = md::CONTEXT_ARM::default(); assert_eq!(context.memoize_register("r11"), Some("fp")); assert_eq!(context.memoize_register("fp"), Some("fp")); assert_eq!(context.memoize_register("foo"), None); } #[test] /// Test ARM register aliases by example of `fp`. fn test_memoize_arm64_alias() { let context = md::CONTEXT_ARM64::default(); assert_eq!(context.memoize_register("x29"), Some("fp")); assert_eq!(context.memoize_register("fp"), Some("fp")); assert_eq!(context.memoize_register("foo"), None); } }