// 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;
usecrate::iostuff::*; usecrate::{MinidumpMiscInfo, MinidumpSystemInfo}; use minidump_common::format as md; use minidump_common::format::ContextFlagsCpu;
/// Generic over the specifics of a CPU context. pubtrait 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 { iflet 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<Self::Register> { ifself.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(&mutself, 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::<Self::Register>() * 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;
}
/// An iterator over registers and values in a [`CpuContext`]. /// /// Returned by [`CpuContext::registers`] and [`CpuContext::valid_registers`]. #[derive(Clone, Debug)] pubstruct CpuRegisters<'a, T: ?Sized> {
regs: CpuRegistersInner<'a>,
context: &'a T,
}
impl<'a, T> Iterator for CpuRegisters<'a, T> where
T: CpuContext,
{ type Item = (&'static str, T::Register);
fn next(&mutself) -> Option<Self::Item> { let reg = match &mutself.regs {
CpuRegistersInner::Slice(iter) => iter.next(),
CpuRegistersInner::Set(iter) => iter.next(),
}?;
/// Information about which registers are valid in a `MinidumpContext`. #[derive(Clone, Debug, PartialEq, Eq)] pubenum 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)] pubstruct 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)] pubenum ContextError { /// Failed to read data.
ReadFailure, /// Encountered an unknown CPU context.
UnknownCpuContext,
}
impl MinidumpContext { /// Return a MinidumpContext given a `MinidumpRawContext`. pubfn from_raw(raw: MinidumpRawContext) -> MinidumpContext {
MinidumpContext {
raw,
valid: MinidumpContextValidity::All,
}
}
/// Read a `MinidumpContext` from `bytes`. pubfn read(
bytes: &[u8],
endian: scroll::Endian,
system_info: &MinidumpSystemInfo,
_misc: Option<&MinidumpMiscInfo>,
) -> Result<MinidumpContext, ContextError> { use md::ProcessorArchitecture::*;
letmut 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))?;
pubfn valid_registers(&self) -> impl Iterator<Item = (&'static str, u64)> + '_ { // 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. pubfn register_size(&self) -> usize { fn get<T: CpuContext>(_: &T) -> usize {
std::mem::size_of::<T::Register>()
}
#[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);
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.35 Sekunden
(vorverarbeitet am 2026-06-20)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.