#![allow(non_upper_case_globals)]
use crate::{
AnsiStrArg, DebuggeeOffset, TypeIdSelector, WdControl, WdRegisters,
WdResult, WdSymbolGroup, WdSymbols, WideStrArg,
dbgeng::DebugClassFlags,
enum_flags,
error::WdErrorKind,
impl_id,
object::traits::context::Context,
ttd::{
cursor::CursorView,
raw::{
bindings::{ICursorView, IReplayEngineView},
interfaces::TtdTargetInterface,
},
replay_engine::ReplayEngineView,
},
};
use bitflags::bitflags;
use std::{ffi::c_void, fmt, mem::MaybeUninit};
use windows::Win32::{
Foundation::E_POINTER,
System::{
Diagnostics::Debug::{Extensions::*, *},
SystemInformation::*,
Variant::*,
},
};
use windows_core::{GUID, IUnknown, PCSTR};
use windy::{ACPString, CP_ACP};
use windy_macros::acpstr;
bitflags::bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct DebugExecutionFlags :u32{
const Echo = DEBUG_EXECUTE_ECHO;
const NotLogged = DEBUG_EXECUTE_NOT_LOGGED;
const NoRepeat = DEBUG_EXECUTE_NO_REPEAT;
const UserTyped = DEBUG_EXECUTE_USER_TYPED;
const UserClicked = DEBUG_EXECUTE_USER_CLICKED;
const Extension = DEBUG_EXECUTE_EXTENSION;
const Internal = DEBUG_EXECUTE_INTERNAL;
const Script = DEBUG_EXECUTE_SCRIPT;
const Toolbar = DEBUG_EXECUTE_TOOLBAR;
const Menu = DEBUG_EXECUTE_MENU;
const Hotkey = DEBUG_EXECUTE_HOTKEY;
const Event = DEBUG_EXECUTE_EVENT;
}
}
bitflags::bitflags! {
pub struct DebugDisasmFlags: u32 {
const EffectiveAddress = DEBUG_DISASM_EFFECTIVE_ADDRESS;
const MatchingSymbols = DEBUG_DISASM_MATCHING_SYMBOLS;
const SourceLineNumber = DEBUG_DISASM_SOURCE_LINE_NUMBER;
const SourceFileName = DEBUG_DISASM_SOURCE_FILE_NAME;
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum DebuggeeType {
Uninitialized,
Kernel(DebugKernelFlags),
UserWindows(DebugUserWindowsFlags),
ImageFile(u32),
}
impl DebuggeeType {
pub fn class(self) -> DebugClassFlags {
match self {
Self::Uninitialized => DebugClassFlags::Uninitialized,
Self::Kernel(_) => DebugClassFlags::Kernel,
Self::UserWindows(_) => DebugClassFlags::UserWindows,
Self::ImageFile(_) => DebugClassFlags::ImageFile,
}
}
pub fn is_dump_file(self) -> bool {
matches!(
self,
Self::Kernel(
DebugKernelFlags::SmallDump
| DebugKernelFlags::Dump
| DebugKernelFlags::FullDump
) | Self::UserWindows(
DebugUserWindowsFlags::SmallDump
| DebugUserWindowsFlags::Dump
| DebugUserWindowsFlags::DumpWindowsCe
)
)
}
pub fn from_raw(class: u32, qualifier: u32) -> Self {
match DebugClassFlags::try_from(class).expect("Invalid class") {
DebugClassFlags::Uninitialized => {
assert_eq!(qualifier, 0, "uninitialized target has qualifier");
DebuggeeType::Uninitialized
}
DebugClassFlags::Kernel => DebuggeeType::Kernel(
DebugKernelFlags::try_from(qualifier)
.expect("Invalid kernel qualifier"),
),
DebugClassFlags::UserWindows => DebuggeeType::UserWindows(
DebugUserWindowsFlags::try_from(qualifier)
.expect("Invalid user-mode qualifier"),
),
DebugClassFlags::ImageFile => DebuggeeType::ImageFile(qualifier),
}
}
}
enum_flags! {
pub enum DebugKernelFlags: u32 {
Connection = DEBUG_KERNEL_CONNECTION,
Local = DEBUG_KERNEL_LOCAL,
ExdiDriver = DEBUG_KERNEL_EXDI_DRIVER,
SmallDump = DEBUG_KERNEL_SMALL_DUMP,
Dump = DEBUG_KERNEL_DUMP,
FullDump = DEBUG_KERNEL_FULL_DUMP,
}
}
enum_flags! {
pub enum DebugUserWindowsFlags: u32 {
Process = DEBUG_USER_WINDOWS_PROCESS,
ProcessServer = DEBUG_USER_WINDOWS_PROCESS_SERVER,
Idna = DEBUG_USER_WINDOWS_IDNA,
Rept = DEBUG_USER_WINDOWS_REPT,
SmallDump = DEBUG_USER_WINDOWS_SMALL_DUMP,
Dump = DEBUG_USER_WINDOWS_DUMP,
DumpWindowsCe = DEBUG_USER_WINDOWS_DUMP_WINDOWS_CE,
LinuxUnknown1 = 0xc47,
LinuxUnknown2 = 0xc48,
LinuxUnknown3 = 0xc49,
}
}
bitflags! {
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct DebugProcessFlags: u32 {
const DetachOnExit = DEBUG_PROCESS_DETACH_ON_EXIT;
const OnlyThisProcess = DEBUG_PROCESS_ONLY_THIS_PROCESS;
}
}
enum_flags! {
pub enum DebugExtPVTypeFlags: u32 {
Value = DEBUG_EXT_PVTYPE_IS_VALUE,
Pointer = DEBUG_EXT_PVTYPE_IS_POINTER,
}
}
enum_flags! {
pub enum DebugBreakpointTypes: u32 {
Code = DEBUG_BREAKPOINT_CODE,
Data = DEBUG_BREAKPOINT_DATA,
Time = DEBUG_BREAKPOINT_TIME,
Inline = DEBUG_BREAKPOINT_INLINE,
}
}
enum_flags! {
pub enum SleLevel: u32 {
NoError = 0,
ErrorA = 1,
MinorError = 2,
Warning = 3,
}
}
impl From<RIP_INFO_TYPE> for SleLevel {
fn from(value: RIP_INFO_TYPE) -> Self {
value.0.try_into().expect("Invalid SleLevel")
}
}
bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct DebugBreakpointFlags: u32 {
const GoOnly = DEBUG_BREAKPOINT_GO_ONLY;
const Deferred = DEBUG_BREAKPOINT_DEFERRED;
const Enabled = DEBUG_BREAKPOINT_ENABLED;
const AdderOnly = DEBUG_BREAKPOINT_ADDER_ONLY;
const OneShot = DEBUG_BREAKPOINT_ONE_SHOT;
}
}
bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct DebugBreakAccessTypes: u32 {
const Read = DEBUG_BREAK_READ;
const Write = DEBUG_BREAK_WRITE;
const Execute = DEBUG_BREAK_EXECUTE;
const Io = DEBUG_BREAK_IO;
}
}
impl_id!(BreakpointId: u32);
impl_id!(ProcessId: u32);
impl_id!(ThreadId: u32);
impl_id!(EngineSystemId: u32);
impl_id!(
EngineProcessId: u32);
impl_id!(
EngineThreadId: u32);
impl EngineThreadId {
#[inline]
pub fn is_any_id(self) -> bool { self.0 == DEBUG_ANY_ID }
#[inline]
pub fn any_id() -> Self { Self(DEBUG_ANY_ID) }
#[inline]
pub fn get(self) -> Option<Self> { (!self.is_any_id()).then_some(self) }
}
impl_id!(SymbolGroupEntryIndex: u32);
impl SymbolGroupEntryIndex {
#[inline]
pub fn is_any_id(self) -> bool { self.0 == DEBUG_ANY_ID }
#[inline]
pub fn any_id() -> Self { Self(DEBUG_ANY_ID) }
#[inline]
pub fn get(self) -> Option<Self> { (!self.is_any_id()).then_some(self) }
#[inline]
pub(crate) fn require_specific(self) -> WdResult<Self> {
self.get()
.ok_or_else(|| WdErrorKind::SpecificIdRequired.into_error())
}
pub fn get_name(
self,
symbols: impl AsRef<WdSymbolGroup>,
) -> WdResult<String> {
symbols.as_ref().get_symbol_name(self)
}
pub fn get_offset(
self,
symbols: impl AsRef<WdSymbolGroup>,
) -> WdResult<Option<DebuggeeOffset>> {
symbols.as_ref().get_symbol_offset(self)
}
pub fn get_register(
self,
symbols: impl AsRef<WdSymbolGroup>,
) -> WdResult<Option<RegisterIndex>> {
symbols.as_ref().get_symbol_register(self)
}
pub fn get_size(
self,
symbols: impl AsRef<WdSymbolGroup>,
) -> WdResult<Option<u32>> {
symbols.as_ref().get_symbol_size(self)
}
pub fn get_type_name(
self,
symbols: impl AsRef<WdSymbolGroup>,
) -> WdResult<String> {
symbols.as_ref().get_symbol_type_name(self)
}
pub fn get_value_text(
self,
symbols: impl AsRef<WdSymbolGroup>,
) -> WdResult<String> {
symbols.as_ref().get_symbol_value_text(self)
}
pub fn remove(self, symbols: impl AsRef<WdSymbolGroup>) -> WdResult<()> {
symbols.as_ref().remove_symbol(self)
}
pub fn write_value<'a>(
self,
symbols: impl AsRef<WdSymbolGroup>,
value: impl Into<WideStrArg<'a>>,
) -> WdResult<()> {
symbols.as_ref().write_symbol(self, value)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ProcessorType {
I386,
Amd64,
Arm,
Arm64,
Ia64,
Ebc,
}
impl ProcessorType {
pub fn is_64bit(&self) -> bool {
matches!(*self, Self::Ia64 | Self::Amd64 | Self::Arm64)
}
pub fn is_32bit(&self) -> bool { matches!(*self, Self::I386 | Self::Arm) }
}
impl From<u16> for ProcessorType {
fn from(value: u16) -> Self { Self::from(IMAGE_FILE_MACHINE(value)) }
}
impl From<u32> for ProcessorType {
fn from(value: u32) -> Self { Self::from(IMAGE_FILE_MACHINE(value as u16)) }
}
impl From<IMAGE_FILE_MACHINE> for ProcessorType {
fn from(value: IMAGE_FILE_MACHINE) -> Self {
match value {
IMAGE_FILE_MACHINE_I386 => Self::I386,
IMAGE_FILE_MACHINE_AMD64 => Self::Amd64,
IMAGE_FILE_MACHINE_ARM => Self::Arm,
IMAGE_FILE_MACHINE_ARM64 => Self::Arm64,
IMAGE_FILE_MACHINE_IA64 => Self::Ia64,
IMAGE_FILE_MACHINE_EBC => Self::Ebc,
_ => panic!("Invalid CodeLevel"),
}
}
}
impl From<ProcessorType> for u16 {
fn from(val: ProcessorType) -> Self {
match val {
ProcessorType::I386 => IMAGE_FILE_MACHINE_I386.0,
ProcessorType::Amd64 => IMAGE_FILE_MACHINE_AMD64.0,
ProcessorType::Arm => IMAGE_FILE_MACHINE_ARM.0,
ProcessorType::Arm64 => IMAGE_FILE_MACHINE_ARM64.0,
ProcessorType::Ia64 => IMAGE_FILE_MACHINE_IA64.0,
ProcessorType::Ebc => IMAGE_FILE_MACHINE_EBC.0,
}
}
}
impl From<ProcessorType> for u32 {
fn from(val: ProcessorType) -> Self {
match val {
ProcessorType::I386 => IMAGE_FILE_MACHINE_I386.0 as u32,
ProcessorType::Amd64 => IMAGE_FILE_MACHINE_AMD64.0 as u32,
ProcessorType::Arm => IMAGE_FILE_MACHINE_ARM.0 as u32,
ProcessorType::Arm64 => IMAGE_FILE_MACHINE_ARM64.0 as u32,
ProcessorType::Ia64 => IMAGE_FILE_MACHINE_IA64.0 as u32,
ProcessorType::Ebc => IMAGE_FILE_MACHINE_EBC.0 as u32,
}
}
}
impl From<ProcessorType> for IMAGE_FILE_MACHINE {
fn from(val: ProcessorType) -> Self {
match val {
ProcessorType::I386 => IMAGE_FILE_MACHINE_I386,
ProcessorType::Amd64 => IMAGE_FILE_MACHINE_AMD64,
ProcessorType::Arm => IMAGE_FILE_MACHINE_ARM,
ProcessorType::Arm64 => IMAGE_FILE_MACHINE_ARM64,
ProcessorType::Ia64 => IMAGE_FILE_MACHINE_IA64,
ProcessorType::Ebc => IMAGE_FILE_MACHINE_EBC,
}
}
}
enum_flags! {
pub enum CodeLevel: u32 {
Source = DEBUG_LEVEL_SOURCE,
Assembly = DEBUG_LEVEL_ASSEMBLY,
}
}
enum_flags! {
pub enum DebugRadix: u32 {
Octal = 8,
Decimal = 10,
Hexadecimal = 16,
}
}
bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct EngineOptions: u32 {
const IgnoreDbghelpVersion = DEBUG_ENGOPT_IGNORE_DBGHELP_VERSION;
const IgnoreExtensionVersions = DEBUG_ENGOPT_IGNORE_EXTENSION_VERSIONS;
const AllowNetworkPaths = DEBUG_ENGOPT_ALLOW_NETWORK_PATHS;
const DisallowNetworkPaths = DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS;
const NetworkPaths = DEBUG_ENGOPT_ALLOW_NETWORK_PATHS | DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS;
const IgnoreLoaderExceptions = DEBUG_ENGOPT_IGNORE_LOADER_EXCEPTIONS;
const InitialBreak = DEBUG_ENGOPT_INITIAL_BREAK;
const InitialModuleBreak = DEBUG_ENGOPT_INITIAL_MODULE_BREAK;
const FinalBreak = DEBUG_ENGOPT_FINAL_BREAK;
const NoExecuteRepeat = DEBUG_ENGOPT_NO_EXECUTE_REPEAT;
const FailIncompleteInformation = DEBUG_ENGOPT_FAIL_INCOMPLETE_INFORMATION;
const AllowReadOnlyBreakpoints = DEBUG_ENGOPT_ALLOW_READ_ONLY_BREAKPOINTS;
const SynchronizeBreakpoints = DEBUG_ENGOPT_SYNCHRONIZE_BREAKPOINTS;
const DisallowShellCommands = DEBUG_ENGOPT_DISALLOW_SHELL_COMMANDS;
const KdQuietMode = DEBUG_ENGOPT_KD_QUIET_MODE;
const DisableManagedSupport = DEBUG_ENGOPT_DISABLE_MANAGED_SUPPORT;
const DisableModuleSymbolLoad = DEBUG_ENGOPT_DISABLE_MODULE_SYMBOL_LOAD;
const DisableExecutionCommands = DEBUG_ENGOPT_DISABLE_EXECUTION_COMMANDS;
const DisallowImageFileMapping = DEBUG_ENGOPT_DISALLOW_IMAGE_FILE_MAPPING;
const PreferDml = DEBUG_ENGOPT_PREFER_DML;
const Disablesqm = DEBUG_ENGOPT_DISABLESQM;
}
}
bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct SymbolOptions: u32 {
const CaseInsensitive = SYMOPT_CASE_INSENSITIVE;
const Undname = SYMOPT_UNDNAME;
const DeferredLoads = SYMOPT_DEFERRED_LOADS;
const NoCpp = SYMOPT_NO_CPP;
const LoadLines = SYMOPT_LOAD_LINES;
const OmapFindNearest = SYMOPT_OMAP_FIND_NEAREST;
const LoadAnything = SYMOPT_LOAD_ANYTHING;
const IgnoreCvrec = SYMOPT_IGNORE_CVREC;
const NoUnqualifiedLoads = SYMOPT_NO_UNQUALIFIED_LOADS;
const FailCriticalErrors = SYMOPT_FAIL_CRITICAL_ERRORS;
const ExactSymbols = SYMOPT_EXACT_SYMBOLS;
const AllowAbsoluteSymbols = SYMOPT_ALLOW_ABSOLUTE_SYMBOLS;
const IgnoreNtSympath = SYMOPT_IGNORE_NT_SYMPATH;
const Include32bitModules = SYMOPT_INCLUDE_32BIT_MODULES;
const PublicsOnly = SYMOPT_PUBLICS_ONLY;
const NoPublics = SYMOPT_NO_PUBLICS;
const AutoPublics = SYMOPT_AUTO_PUBLICS;
const NoImageSearch = SYMOPT_NO_IMAGE_SEARCH;
const Secure = SYMOPT_SECURE;
const NoPrompts = SYMOPT_NO_PROMPTS;
const Debug = SYMOPT_DEBUG;
}
}
enum_flags! {
pub enum DebugOutctlTarget: u32 {
ThisClient = DEBUG_OUTCTL_THIS_CLIENT,
AllClients = DEBUG_OUTCTL_ALL_CLIENTS,
AllOtherClients = DEBUG_OUTCTL_ALL_OTHER_CLIENTS,
Ignore = DEBUG_OUTCTL_IGNORE,
LogOnly = DEBUG_OUTCTL_LOG_ONLY,
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
pub struct DebugOutctlOptions(u32);
impl DebugOutctlOptions {
pub const NotLogged: Self = Self(DEBUG_OUTCTL_NOT_LOGGED);
pub const OverrideMask: Self = Self(DEBUG_OUTCTL_OVERRIDE_MASK);
pub const Dml: Self = Self(DEBUG_OUTCTL_DML);
pub const fn empty() -> Self { Self(0) }
pub const fn bits(self) -> u32 { self.0 }
}
impl std::ops::BitOr for DebugOutctlOptions {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output { Self(self.0 | rhs.0) }
}
impl std::ops::BitOrAssign for DebugOutctlOptions {
fn bitor_assign(&mut self, rhs: Self) { self.0 |= rhs.0; }
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct DebugOutctlFlags(u32);
impl DebugOutctlFlags {
pub const ThisClient: Self = Self(DEBUG_OUTCTL_THIS_CLIENT);
pub const AllClients: Self = Self(DEBUG_OUTCTL_ALL_CLIENTS);
pub const AllOtherClients: Self = Self(DEBUG_OUTCTL_ALL_OTHER_CLIENTS);
pub const Ignore: Self = Self(DEBUG_OUTCTL_IGNORE);
pub const LogOnly: Self = Self(DEBUG_OUTCTL_LOG_ONLY);
pub const AmbientDml: Self = Self(DEBUG_OUTCTL_AMBIENT_DML);
pub const AmbientText: Self = Self(DEBUG_OUTCTL_AMBIENT_TEXT);
pub const Ambient: Self = Self(DEBUG_OUTCTL_AMBIENT);
pub const fn new(
target: DebugOutctlTarget,
options: DebugOutctlOptions,
) -> Self {
Self(target as u32 | options.bits())
}
pub const fn bits(self) -> u32 { self.0 }
}
bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct DebugOutputFlags: u32 {
const Normal = DEBUG_OUTPUT_NORMAL;
const Error = DEBUG_OUTPUT_ERROR;
const Warning = DEBUG_OUTPUT_WARNING;
const Verbose = DEBUG_OUTPUT_VERBOSE;
const Prompt = DEBUG_OUTPUT_PROMPT;
const PromptRegisters = DEBUG_OUTPUT_PROMPT_REGISTERS;
const ExtensionWarning = DEBUG_OUTPUT_EXTENSION_WARNING;
const Debuggee = DEBUG_OUTPUT_DEBUGGEE;
const DebuggeePrompt = DEBUG_OUTPUT_DEBUGGEE_PROMPT;
const Symbols = DEBUG_OUTPUT_SYMBOLS;
const Status = DEBUG_OUTPUT_STATUS;
}
}
#[repr(transparent)]
#[derive(Default, Copy, Clone)]
pub struct DebugValue(pub(crate) DEBUG_VALUE);
impl std::fmt::Debug for DebugValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
match self.r#type() {
DebugValueType::Invalid => f.write_str("(INVALID)"),
DebugValueType::Int8 => {
write!(f, "{} (INT8)", self.as_u8())
}
DebugValueType::Int16 => {
write!(f, "{} (INT16)", self.as_u16())
}
DebugValueType::Int32 => {
write!(f, "{} (INT32)", self.as_u32())
}
DebugValueType::Int64 => {
write!(f, "{} (INT64)", self.as_u64())
}
DebugValueType::Float32 => {
write!(f, "{:e} (FLOAT32)", self.as_f32())
}
DebugValueType::Float64 => {
write!(f, "{:e} (FLOAT64)", self.as_f64())
}
DebugValueType::Float80 => {
write!(f, "{:?} (FLOAT80)", self.as_f80())
}
DebugValueType::Float82 => {
write!(f, "{:?} (FLOAT82)", self.as_f82())
}
DebugValueType::Float128 => {
write!(f, "{:?} (FLOAT128)", self.as_f128())
}
DebugValueType::Vector64 => {
write!(f, "{:?} (VECTOR64)", self.as_vi8_64())
}
DebugValueType::Vector128 => {
write!(f, "{:?} (VECTOR128)", self.as_vi8_128())
}
}
}
}
fn check_debug_value(
value: &DebugValue,
expected: DebugValueType,
) -> WdResult<()> {
let actual = value.r#type();
(actual == expected).ok_or_else(|| {
WdErrorKind::DebugValueTypeMismatch { actual, expected }.into_error()
})
}
macro_rules! impl_debug_value_as {
(
$as_func:ident,
$try_as_func:ident,
$coerced_as_func:ident,
$ret_ty:ty,
$dv_type:ident,
$value:ident =>
$get:expr
) => {
pub fn $try_as_func(&self) -> WdResult<$ret_ty> {
check_debug_value(self, DebugValueType::$dv_type)?;
let $value = self;
Ok($get)
}
pub fn $as_func(&self) -> $ret_ty {
self.$try_as_func().expect("Invalid DebugValue conversion")
}
pub fn $coerced_as_func(
&self,
control: impl AsRef<WdControl>,
) -> WdResult<$ret_ty> {
if self.r#type() == DebugValueType::$dv_type {
let $value = self;
Ok($get)
} else {
let coerced = control
.as_ref()
.coerce_value(self, DebugValueType::$dv_type)?;
let $value = &coerced;
Ok($get)
}
}
};
}
impl DebugValue {
#[inline(always)]
pub fn as_raw(&self) -> &DEBUG_VALUE { &self.0 }
#[inline(always)]
pub fn as_raw_mut(&mut self) -> &mut DEBUG_VALUE { &mut self.0 }
#[inline(always)]
pub fn as_ptr(&self) -> *const DEBUG_VALUE { &self.0 as *const _ }
#[inline(always)]
pub fn as_mut_ptr(&mut self) -> *mut DEBUG_VALUE { &mut self.0 as *mut _ }
#[inline(always)]
pub fn is_invalid(&self) -> bool {
self.r#type() == DebugValueType::Invalid
}
impl_debug_value_as!(as_u8, try_as_u8, coerced_as_u8, u8, Int8, v => unsafe {v.0.Anonymous.I8});
impl_debug_value_as!(as_i8, try_as_i8, coerced_as_i8, i8, Int8, v => unsafe {v.0.Anonymous.I8 as i8});
impl_debug_value_as!(as_u16, try_as_u16, coerced_as_u16, u16, Int16, v => unsafe {v.0.Anonymous.I16});
impl_debug_value_as!(as_i16, try_as_i16, coerced_as_i16, i16, Int16, v => unsafe {v.0.Anonymous.I16 as i16});
impl_debug_value_as!(as_u32, try_as_u32, coerced_as_u32, u32, Int32, v => unsafe {v.0.Anonymous.I32});
impl_debug_value_as!(as_i32, try_as_i32, coerced_as_i32, i32, Int32, v => unsafe {v.0.Anonymous.I32 as i32});
impl_debug_value_as!(as_u64, try_as_u64, coerced_as_u64, u64, Int64, v => unsafe {
((v.0.Anonymous.I64Parts32.HighPart as u64) << 32) | v.0.Anonymous.I64Parts32.LowPart as u64
});
impl_debug_value_as!(as_i64, try_as_i64, coerced_as_i64, i64, Int64, v => unsafe {
(((v.0.Anonymous.I64Parts32.HighPart as u64) << 32) | v.0.Anonymous.I64Parts32.LowPart as u64) as i64
});
impl_debug_value_as!(as_f32, try_as_f32, coerced_as_f32, f32, Float32, v => unsafe {v.0.Anonymous.F32});
impl_debug_value_as!(as_f64, try_as_f64, coerced_as_f64, f64, Float64, v => unsafe {v.0.Anonymous.F64});
impl_debug_value_as!(as_f80, try_as_f80, coerced_as_f80, [u8;10], Float80, v => unsafe {v.0.Anonymous.F80Bytes});
impl_debug_value_as!(as_f82, try_as_f82, coerced_as_f82, [u8;11], Float82, v => unsafe {v.0.Anonymous.F82Bytes});
impl_debug_value_as!(as_f128, try_as_f128, coerced_as_f128, [u8;16], Float128, v => unsafe {v.0.Anonymous.F128Bytes});
impl_debug_value_as!(as_vi8_64, try_as_vi8_64, coerced_as_vi8_64, [u8;8], Vector64, v => unsafe {v.0.Anonymous.VI8[..8].try_into().unwrap()});
impl_debug_value_as!(as_vi16_64, try_as_vi16_64, coerced_as_vi16_64, [u16;4], Vector64, v => unsafe {v.0.Anonymous.VI16[..4].try_into().unwrap()});
impl_debug_value_as!(as_vi32_64, try_as_vi32_64, coerced_as_vi32_64, [u32;2], Vector64, v => unsafe {v.0.Anonymous.VI32[..2].try_into().unwrap()});
impl_debug_value_as!(as_vi64_64, try_as_vi64_64, coerced_as_vi64_64, [u64;1], Vector64, v => unsafe {v.0.Anonymous.VI64[..1].try_into().unwrap()});
impl_debug_value_as!(as_vf32_64, try_as_vf32_64, coerced_as_vf32_64, [f32;2], Vector64, v => unsafe {v.0.Anonymous.VF32[..2].try_into().unwrap()});
impl_debug_value_as!(as_vf64_64, try_as_vf64_64, coerced_as_vf64_64, [f64;1], Vector64, v => unsafe {v.0.Anonymous.VF64[..1].try_into().unwrap()});
impl_debug_value_as!(as_vi8_128, try_as_vi8_128, coerced_as_vi8_128, [u8;16], Vector128, v => unsafe {v.0.Anonymous.VI8});
impl_debug_value_as!(as_vi16_128, try_as_vi16_128, coerced_as_vi16_128, [u16;8], Vector128, v => unsafe {v.0.Anonymous.VI16});
impl_debug_value_as!(as_vi32_128, try_as_vi32_128, coerced_as_vi32_128, [u32;4], Vector128, v => unsafe {v.0.Anonymous.VI32});
impl_debug_value_as!(as_vi64_128, try_as_vi64_128, coerced_as_vi64_128, [u64;2], Vector128, v => unsafe {v.0.Anonymous.VI64});
impl_debug_value_as!(as_vf32_128, try_as_vf32_128, coerced_as_vf32_128, [f32;4], Vector128, v => unsafe {v.0.Anonymous.VF32});
impl_debug_value_as!(as_vf64_128, try_as_vf64_128, coerced_as_vf64_128, [f64;2], Vector128, v => unsafe {v.0.Anonymous.VF64});
pub fn try_as_usize(&self) -> WdResult<usize> {
#[cfg(target_pointer_width = "64")]
{
Ok(self.try_as_u64()? as usize)
}
#[cfg(target_pointer_width = "32")]
{
Ok(self.try_as_u32()? as usize)
}
}
pub fn as_usize(&self) -> usize {
self.try_as_usize().expect("Invalid DebugValue conversion")
}
pub fn coerced_as_usize(
&self,
control: impl AsRef<WdControl>,
) -> WdResult<usize> {
#[cfg(target_pointer_width = "64")]
{
Ok(self.coerced_as_u64(control)? as usize)
}
#[cfg(target_pointer_width = "32")]
{
Ok(self.coerced_as_u32(control)? as usize)
}
}
pub fn try_as_isize(&self) -> WdResult<isize> {
#[cfg(target_pointer_width = "64")]
{
Ok(self.try_as_i64()? as isize)
}
#[cfg(target_pointer_width = "32")]
{
Ok(self.try_as_i32()? as isize)
}
}
pub fn as_isize(&self) -> isize {
self.try_as_isize().expect("Invalid DebugValue conversion")
}
pub fn coerced_as_isize(
&self,
control: impl AsRef<WdControl>,
) -> WdResult<isize> {
#[cfg(target_pointer_width = "64")]
{
Ok(self.coerced_as_i64(control)? as isize)
}
#[cfg(target_pointer_width = "32")]
{
Ok(self.coerced_as_i32(control)? as isize)
}
}
#[inline(always)]
pub fn as_raw_bytes(&self) -> &[u8; 24] {
unsafe { &self.0.Anonymous.RawBytes }
}
#[inline(always)]
pub fn tail_of_raw_bytes(&self) -> u32 { self.0.TailOfRawBytes }
pub fn r#type(&self) -> DebugValueType {
DebugValueType::try_from(self.0.Type).expect("Invalid type")
}
pub fn from_f128_bytes(value: [u8; 16]) -> Self {
let mut x = DEBUG_VALUE::default();
x.Anonymous.F128Bytes = value;
x.Type = DebugValueType::Float128 as u32;
Self(x)
}
}
macro_rules! debug_value_from_impl {
($ty: ty $(as $ty2: ty)?, $m: ident, $dvt: ident) => {
impl From<$ty> for DebugValue {
fn from(value: $ty) -> Self {
let mut x = DEBUG_VALUE::default();
x.Anonymous.$m = value $(as $ty2)?;
x.Type = DebugValueType::$dvt as u32;
Self(x)
}
}
};
(@half [$ty: ty; $n:expr], $m: ident, $dvt: ident) => {
impl From<[$ty; $n]> for DebugValue {
fn from(value: [$ty; $n]) -> Self {
let mut x = DEBUG_VALUE::default();
unsafe{ x.Anonymous.$m[..$n].clone_from_slice(&value); }
x.Type = DebugValueType::$dvt as u32;
Self(x)
}
}
};
}
debug_value_from_impl!(u8, I8, Int8);
debug_value_from_impl!(i8 as u8, I8, Int8);
debug_value_from_impl!(u16, I16, Int16);
debug_value_from_impl!(i16 as u16, I16, Int16);
debug_value_from_impl!(u32, I32, Int32);
debug_value_from_impl!(i32 as u32, I32, Int32);
debug_value_from_impl!(f32, F32, Float32);
debug_value_from_impl!(f64, F64, Float64);
debug_value_from_impl!(@half [u8; 8], VI8, Vector64);
debug_value_from_impl!(@half [u16; 4], VI16, Vector64);
debug_value_from_impl!(@half [u32; 2], VI32, Vector64);
debug_value_from_impl!(@half [u64; 1], VI64, Vector64);
debug_value_from_impl!(@half [f32; 2], VF32, Vector64);
debug_value_from_impl!(@half [f64; 1], VF64, Vector64);
debug_value_from_impl!([u8; 16], VI8, Vector128);
debug_value_from_impl!([u16; 8], VI16, Vector128);
debug_value_from_impl!([u32; 4], VI32, Vector128);
debug_value_from_impl!([u64; 2], VI64, Vector128);
debug_value_from_impl!([f32; 4], VF32, Vector128);
debug_value_from_impl!([f64; 2], VF64, Vector128);
debug_value_from_impl!([u8; 10], F80Bytes, Float80);
debug_value_from_impl!([u8; 11], F82Bytes, Float82);
debug_value_from_impl!([u8; 24], RawBytes, Vector128);
impl From<u64> for DebugValue {
fn from(value: u64) -> Self {
let mut x = DEBUG_VALUE::default();
x.Anonymous.I64Parts32.HighPart = (value >> 32) as u32;
x.Anonymous.I64Parts32.LowPart = value as u32;
x.Type = DebugValueType::Int64 as u32;
Self(x)
}
}
impl From<i64> for DebugValue {
fn from(value: i64) -> Self {
let mut x = DEBUG_VALUE::default();
x.Anonymous.I64Parts32.HighPart = (value >> 32) as u32;
x.Anonymous.I64Parts32.LowPart = value as u32;
x.Type = DebugValueType::Int64 as u32;
Self(x)
}
}
pub(crate) mod sealed {
pub trait Sealed {}
}
pub trait DebugValueT: sealed::Sealed + Sized {
const DEBUG_VALUE_TYPE: DebugValueType;
fn try_as_debug_value(value: &DebugValue) -> WdResult<Self>;
fn coerced_debug_value(
value: &DebugValue,
control: impl AsRef<WdControl>,
) -> WdResult<Self>;
}
macro_rules! impl_coerced_register_value {
($dvt:ident, $ty:ty => $try_as_func:ident, $coerced_func:ident) => {
impl sealed::Sealed for $ty {}
impl DebugValueT for $ty {
const DEBUG_VALUE_TYPE: DebugValueType = DebugValueType::$dvt;
#[inline]
fn try_as_debug_value(value: &DebugValue) -> WdResult<Self> {
value.$try_as_func()
}
#[inline]
fn coerced_debug_value(
value: &DebugValue,
control: impl AsRef<WdControl>,
) -> WdResult<Self> {
value.$coerced_func(control)
}
}
};
}
impl_coerced_register_value!(Int8, u8 => try_as_u8, coerced_as_u8);
impl_coerced_register_value!(Int8, i8 => try_as_i8, coerced_as_i8);
impl_coerced_register_value!(Int16, u16 => try_as_u16, coerced_as_u16);
impl_coerced_register_value!(Int16, i16 => try_as_i16, coerced_as_i16);
impl_coerced_register_value!(Int32, u32 => try_as_u32, coerced_as_u32);
impl_coerced_register_value!(Int32, i32 => try_as_i32, coerced_as_i32);
impl_coerced_register_value!(Int64, u64 => try_as_u64, coerced_as_u64);
impl_coerced_register_value!(Int64, i64 => try_as_i64, coerced_as_i64);
impl_coerced_register_value!(Float32, f32 => try_as_f32, coerced_as_f32);
impl_coerced_register_value!(Float64, f64 => try_as_f64, coerced_as_f64);
impl_coerced_register_value!(Float80, [u8; 10] => try_as_f80, coerced_as_f80);
impl_coerced_register_value!(Float82, [u8; 11] => try_as_f82, coerced_as_f82);
impl_coerced_register_value!(Vector64, [u8; 8] => try_as_vi8_64, coerced_as_vi8_64);
impl_coerced_register_value!(Vector64, [u16; 4] => try_as_vi16_64, coerced_as_vi16_64);
impl_coerced_register_value!(Vector64, [u32; 2] => try_as_vi32_64, coerced_as_vi32_64);
impl_coerced_register_value!(Vector64, [u64; 1] => try_as_vi64_64, coerced_as_vi64_64);
impl_coerced_register_value!(Vector128, [u8; 16] => try_as_vi8_128, coerced_as_vi8_128);
impl_coerced_register_value!(Vector128, [u16; 8] => try_as_vi16_128, coerced_as_vi16_128);
impl_coerced_register_value!(Vector128, [u32; 4] => try_as_vi32_128, coerced_as_vi32_128);
impl_coerced_register_value!(Vector128, [u64; 2] => try_as_vi64_128, coerced_as_vi64_128);
impl_coerced_register_value!(Float64, [f32; 2] => try_as_vf32_64, coerced_as_vf32_64);
impl_coerced_register_value!(Float64, [f64; 1] => try_as_vf64_64, coerced_as_vf64_64);
impl_coerced_register_value!(Float128, [f32; 4] => try_as_vf32_128, coerced_as_vf32_128);
impl_coerced_register_value!(Float128, [f64; 2] => try_as_vf64_128, coerced_as_vf64_128);
impl sealed::Sealed for usize {}
impl DebugValueT for usize {
#[cfg(target_pointer_width = "64")]
const DEBUG_VALUE_TYPE: DebugValueType = DebugValueType::Int64;
#[cfg(target_pointer_width = "32")]
const DEBUG_VALUE_TYPE: DebugValueType = DebugValueType::Int32;
#[inline]
fn try_as_debug_value(value: &DebugValue) -> WdResult<Self> {
value.try_as_usize()
}
#[inline]
fn coerced_debug_value(
value: &DebugValue,
control: impl AsRef<WdControl>,
) -> WdResult<Self> {
value.coerced_as_usize(control)
}
}
impl sealed::Sealed for isize {}
impl DebugValueT for isize {
#[cfg(target_pointer_width = "64")]
const DEBUG_VALUE_TYPE: DebugValueType = DebugValueType::Int64;
#[cfg(target_pointer_width = "32")]
const DEBUG_VALUE_TYPE: DebugValueType = DebugValueType::Int32;
#[inline]
fn try_as_debug_value(value: &DebugValue) -> WdResult<Self> {
value.try_as_isize()
}
#[inline]
fn coerced_debug_value(
value: &DebugValue,
control: impl AsRef<WdControl>,
) -> WdResult<Self> {
value.coerced_as_isize(control)
}
}
enum_flags! {
pub enum DebugValueType: u32 {
Invalid = DEBUG_VALUE_INVALID,
Int8 = DEBUG_VALUE_INT8,
Int16 = DEBUG_VALUE_INT16,
Int32 = DEBUG_VALUE_INT32,
Int64 = DEBUG_VALUE_INT64,
Float32 = DEBUG_VALUE_FLOAT32,
Float64 = DEBUG_VALUE_FLOAT64,
Float80 = DEBUG_VALUE_FLOAT80,
Float82 = DEBUG_VALUE_FLOAT82,
Float128 = DEBUG_VALUE_FLOAT128,
Vector64 = DEBUG_VALUE_VECTOR64,
Vector128 = DEBUG_VALUE_VECTOR128,
}
}
enum_flags! {
pub enum BusDataType: u32 {
ConfigurationSpaceUndefined = 0,
Cmos = 1,
EisaConfiguration = 2,
Pos = 3,
CbusConfiguration = 4,
PCIConfiguration = 5,
VMEConfiguration = 6,
NuBusConfiguration = 7,
PCMCIAConfiguration = 8,
MPIConfiguration = 9,
MPSAConfiguration = 10,
PNPISAConfiguration = 11,
SgiInternalConfiguration = 12,
MaximumBusDataType = 13,
}
}
enum_flags! {
pub enum DebugDataType: u32 {
KpcrOffset = DEBUG_DATA_KPCR_OFFSET,
KpcrbOffset = DEBUG_DATA_KPRCB_OFFSET,
KthreadOffset = DEBUG_DATA_KTHREAD_OFFSET,
BaseTranslationVirtualOffset = DEBUG_DATA_BASE_TRANSLATION_VIRTUAL_OFFSET,
ProcessorIndetification = DEBUG_DATA_PROCESSOR_IDENTIFICATION,
ProcessorSpeed = DEBUG_DATA_PROCESSOR_SPEED,
}
}
enum_flags! {
pub enum DebugPhysicalFlags: u32 {
Default = DEBUG_PHYSICAL_DEFAULT,
Cached = DEBUG_PHYSICAL_CACHED,
Uncached = DEBUG_PHYSICAL_UNCACHED,
WriteCombined = DEBUG_PHYSICAL_WRITE_COMBINED,
}
}
bitflags! {
pub struct DebugRegistersFlags: u32 {
const Default = DEBUG_REGISTERS_DEFAULT;
const Int32 = DEBUG_REGISTERS_INT32;
const Int64 = DEBUG_REGISTERS_INT64;
const Float = DEBUG_REGISTERS_FLOAT;
const All = DEBUG_REGISTERS_ALL;
}
}
enum_flags! {
pub enum DebugRegSrc: u32 {
Debuggee = DEBUG_REGSRC_DEBUGGEE,
Explicit = DEBUG_REGSRC_EXPLICIT,
Frame = DEBUG_REGSRC_FRAME,
}
}
bitflags! {
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct DebugRegisterFlags: u32 {
const SubRegister = DEBUG_REGISTER_SUB_REGISTER;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct DebugAsmOptFlags: u32 {
const Default = DEBUG_ASMOPT_DEFAULT;
const Verbose = DEBUG_ASMOPT_VERBOSE;
const NoCodeBytes = DEBUG_ASMOPT_NO_CODE_BYTES;
const IgnoreOutputWidth = DEBUG_ASMOPT_IGNORE_OUTPUT_WIDTH;
const SourceLineNumber = DEBUG_ASMOPT_SOURCE_LINE_NUMBER;
}
}
enum_flags! {
pub enum DebugExprOptions: u32 {
Masm = DEBUG_EXPR_MASM,
Cplusplus = DEBUG_EXPR_CPLUSPLUS,
}
}
#[repr(transparent)]
#[derive(Debug, Default, Clone)]
pub struct DebugRegisterDescription(pub(crate) DEBUG_REGISTER_DESCRIPTION);
impl DebugRegisterDescription {
pub fn as_raw(&self) -> &DEBUG_REGISTER_DESCRIPTION { &self.0 }
pub fn as_raw_mut(&mut self) -> &mut DEBUG_REGISTER_DESCRIPTION {
&mut self.0
}
pub fn r#type(&self) -> DebugValueType {
DebugValueType::try_from(self.0.Type).expect("Invalid type")
}
pub fn flags(&self) -> DebugRegisterFlags {
DebugRegisterFlags::from_bits(self.0.Flags).unwrap_or_else(|| {
panic!("Invalid DebugRegisterFlags: {}", self.0.Flags)
})
}
pub fn is_sub_register(&self) -> bool {
self.flags().contains(DebugRegisterFlags::SubRegister)
}
pub fn sub_reg_master(&self) -> Option<u32> {
if self.is_sub_register() {
Some(self.0.SubregMaster)
} else {
None
}
}
pub fn sub_reg_length(&self) -> Option<u32> {
if self.is_sub_register() {
Some(self.0.SubregLength)
} else {
None
}
}
pub fn sub_reg_mask(&self) -> Option<u64> {
if self.is_sub_register() {
Some(self.0.SubregMask)
} else {
None
}
}
pub fn sub_reg_shift(&self) -> Option<u32> {
if self.is_sub_register() {
Some(self.0.SubregShift)
} else {
None
}
}
pub fn reserved0(&self) -> u32 { self.0.Reserved0 }
}
pub(crate) const DEBUG_INVALID_OFFSET: u64 = u64::MAX;
#[repr(transparent)]
#[derive(Default, Debug, Clone)]
pub struct DebugBreakpointParameters(pub DEBUG_BREAKPOINT_PARAMETERS);
impl DebugBreakpointParameters {
pub fn offset(&self) -> Option<DebuggeeOffset> {
(self.0.Offset != DEBUG_INVALID_OFFSET).then_some(self.0.Offset)
}
pub fn is_valid(&self) -> bool { self.0.Id != DEBUG_ANY_ID }
pub fn id(&self) -> Option<BreakpointId> {
self.is_valid().then(|| self.0.Id.into())
}
pub fn break_type(&self) -> DebugBreakpointTypes {
self.0.BreakType.try_into().expect("Invalid break type")
}
pub fn proc_type(&self) -> ProcessorType { self.0.ProcType.into() }
pub fn flags(&self) -> DebugBreakpointFlags {
DebugBreakpointFlags::from_bits_retain(self.0.Flags)
}
pub fn pass_count(&self) -> u32 { self.0.PassCount }
pub fn current_pass_count(&self) -> u32 { self.0.CurrentPassCount }
pub fn match_thread(&self) -> Option<EngineThreadId> {
EngineThreadId(self.0.MatchThread).get()
}
pub fn command_size(&self) -> Option<u32> {
(self.0.CommandSize != 0).then_some(self.0.CommandSize)
}
pub fn offset_expression_size(&self) -> Option<u32> {
(self.0.OffsetExpressionSize != 0)
.then_some(self.0.OffsetExpressionSize)
}
}
impl From<DEBUG_BREAKPOINT_PARAMETERS> for DebugBreakpointParameters {
fn from(value: DEBUG_BREAKPOINT_PARAMETERS) -> Self { Self(value) }
}
impl_id!(SymbolId: u64);
impl_id!(ModuleIndex: u32);
impl ModuleIndex {
pub fn any_id() -> Self { ModuleIndex(DEBUG_ANY_ID) }
pub fn is_any_id(&self) -> bool { self.0 == DEBUG_ANY_ID }
pub fn get(self) -> Option<Self> {
(self.0 != DEBUG_ANY_ID).then_some(self)
}
pub fn from_name<'a>(
symbols: impl AsRef<WdSymbols>,
name: impl Into<WideStrArg<'a>>,
) -> WdResult<Self> {
Ok(symbols.as_ref().get_module_by_module_name(name, 0)?.0)
}
pub fn from_name2<'a>(
symbols: impl AsRef<WdSymbols>,
name: impl Into<WideStrArg<'a>>,
flags: DebugGetModFlags,
) -> WdResult<Self> {
Ok(symbols
.as_ref()
.get_module_by_module_name2(name, 0, flags)?
.0)
}
pub fn from_offset(
symbols: impl AsRef<WdSymbols>,
offset: DebuggeeOffset,
) -> WdResult<Self> {
Ok(symbols.as_ref().get_module_by_offset(offset, 0)?.0)
}
pub fn from_offset2(
symbols: impl AsRef<WdSymbols>,
offset: DebuggeeOffset,
flags: DebugGetModFlags,
) -> WdResult<Self> {
Ok(symbols.as_ref().get_module_by_offset2(offset, 0, flags)?.0)
}
pub fn get_offset(
self,
symbols: impl AsRef<WdSymbols>,
) -> WdResult<DebuggeeOffset> {
symbols.as_ref().get_module_by_index(self)
}
pub fn is_loaded(self, symbols: impl AsRef<WdSymbols>) -> WdResult<bool> {
match symbols.as_ref().get_module_by_index(self) {
Ok(_) => Ok(true),
Err(e) if e.kind() == WdErrorKind::ModuleNotFound => Ok(false),
Err(e) => Err(e),
}
}
pub fn get_module_name(
self,
symbols: impl AsRef<WdSymbols>,
) -> WdResult<String> {
symbols
.as_ref()
.get_module_name_string(DebugModNameFlags::Module, self)
}
pub fn get_image_name(
self,
symbols: impl AsRef<WdSymbols>,
) -> WdResult<String> {
Ok(symbols
.as_ref()
.get_module_name_string(DebugModNameFlags::Image, self)?
.to_string())
}
pub fn get_loaded_image_name(
self,
symbols: impl AsRef<WdSymbols>,
) -> WdResult<String> {
symbols
.as_ref()
.get_module_name_string(DebugModNameFlags::LoadedImage, self)
}
pub fn get_mapped_image_name(
self,
symbols: impl AsRef<WdSymbols>,
) -> WdResult<String> {
symbols
.as_ref()
.get_module_name_string(DebugModNameFlags::MappedImage, self)
}
pub fn get_symbol_file_name(
self,
symbols: impl AsRef<WdSymbols>,
) -> WdResult<String> {
symbols
.as_ref()
.get_module_name_string(DebugModNameFlags::SymbolFile, self)
}
}
#[repr(transparent)]
#[derive(Default, Debug, Copy, Clone, PartialEq)]
pub struct DebugModuleAndId(pub DEBUG_MODULE_AND_ID);
impl Eq for DebugModuleAndId {}
impl From<DEBUG_MODULE_AND_ID> for DebugModuleAndId {
fn from(value: DEBUG_MODULE_AND_ID) -> Self { Self(value) }
}
impl From<DebugModuleAndId> for DEBUG_MODULE_AND_ID {
fn from(value: DebugModuleAndId) -> Self { value.0 }
}
impl DebugModuleAndId {
pub fn from_module_and_id(module_base: DebuggeeOffset, id: u64) -> Self {
Self(DEBUG_MODULE_AND_ID {
ModuleBase: module_base,
Id: id,
})
}
pub fn module_base(&self) -> DebuggeeOffset { self.0.ModuleBase }
pub fn id(&self) -> SymbolId { SymbolId(self.0.Id) }
}
impl_id!(ManagedSymbolToken: u32);
enum_flags! {
pub enum SymTagEnum: u32{
Null=0,
Exe=1,
Compiland=2,
CompilandDetails=3,
CompilandEnv=4,
Function=5,
Block=6,
Data=7,
Annotation=8,
Label=9,
PublicSymbol=10,
UDT=11,
Enum=12,
FunctionType=13,
PointerType=14,
ArrayType=15,
BaseType=16,
Typedef=17,
BaseClass=18,
Friend=19,
FunctionArgType=20,
FuncDebugStart=21,
FuncDebugEnd=22,
UsingNamespace=23,
VTableShape=24,
VTable=25,
Custom=26,
Thunk=27,
CustomType=28,
ManagedType=29,
Dimension=30,
CallSite=31,
InlineSite=32,
BaseInterface=33,
VectorType=34,
MatrixType=35,
HLSLType=36,
Caller=37,
Callee=38,
Export=39,
HeapAllocationSite=40,
CoffGroup=41,
Inlinee=42,
TaggedUnionCase=43,
}
}
enum_flags! {
pub enum DebugModNameFlags: u32{
Image = DEBUG_MODNAME_IMAGE,
Module = DEBUG_MODNAME_MODULE,
LoadedImage = DEBUG_MODNAME_LOADED_IMAGE,
SymbolFile = DEBUG_MODNAME_SYMBOL_FILE,
MappedImage = DEBUG_MODNAME_MAPPED_IMAGE,
}
}
#[repr(transparent)]
#[derive(Debug, Clone)]
pub struct DebugSymbolEntry(pub(crate) DEBUG_SYMBOL_ENTRY);
impl DebugSymbolEntry {
pub fn as_raw(&self) -> &DEBUG_SYMBOL_ENTRY { &self.0 }
pub fn module_base(&self) -> DebuggeeOffset { self.0.ModuleBase }
pub fn offset(&self) -> DebuggeeOffset { self.0.Offset }
pub fn id(&self) -> Option<SymbolId> {
(self.0.Id != DEBUG_INVALID_OFFSET).then_some(self.0.Id.into())
}
pub fn arg64(&self) -> Option<u64> {
(self.0.Arg64 != 0).then_some(self.0.Arg64)
}
pub fn size(&self) -> u32 { self.0.Size }
pub fn flags(&self) -> u32 { self.0.Flags }
pub fn type_id(&self) -> SymbolTypeId {
SymbolTypeId {
module: self.module_base(),
id: self.0.TypeId,
}
}
pub fn name_size(&self) -> Option<u32> {
(self.0.NameSize != 0).then_some(self.0.NameSize)
}
pub fn token(&self) -> Option<ManagedSymbolToken> {
(self.0.Token != 0).then_some(self.0.Token.into())
}
pub fn tag(&self) -> Option<SymTagEnum> {
(self.0.Tag != 0).then_some(self.0.Tag.try_into().unwrap())
}
pub fn arg32(&self) -> Option<u32> {
(self.0.Arg32 != 0).then_some(self.0.Arg32)
}
pub fn reserved(&self) -> u32 { self.0.Reserved }
}
impl From<DEBUG_SYMBOL_ENTRY> for DebugSymbolEntry {
fn from(value: DEBUG_SYMBOL_ENTRY) -> Self { Self(value) }
}
enum_flags! {
pub enum DebugInterruptFlag: u32 {
Active = DEBUG_INTERRUPT_ACTIVE,
Passive = DEBUG_INTERRUPT_PASSIVE,
Exit = DEBUG_INTERRUPT_EXIT,
}
}
enum_flags! {
pub enum DebugStatus: u32 {
NoDebuggee = DEBUG_STATUS_NO_DEBUGGEE,
OutOfSync = DEBUG_STATUS_OUT_OF_SYNC,
WaitInput = DEBUG_STATUS_WAIT_INPUT,
Timeout = DEBUG_STATUS_TIMEOUT,
Break = DEBUG_STATUS_BREAK,
StepInto = DEBUG_STATUS_STEP_INTO,
StepBranch = DEBUG_STATUS_STEP_BRANCH,
StepOver = DEBUG_STATUS_STEP_OVER,
GoNotHandled = DEBUG_STATUS_GO_NOT_HANDLED,
GoHandled = DEBUG_STATUS_GO_HANDLED,
Go = DEBUG_STATUS_GO,
IgnoreEvent = DEBUG_STATUS_IGNORE_EVENT,
RestartRequested = DEBUG_STATUS_RESTART_REQUESTED,
NoChange = DEBUG_STATUS_NO_CHANGE,
}
}
enum_flags! {
pub enum DebugExecutionStatus: u32 {
StepInto = DEBUG_STATUS_STEP_INTO,
StepBranch = DEBUG_STATUS_STEP_BRANCH,
StepOver = DEBUG_STATUS_STEP_OVER,
GoNotHandled = DEBUG_STATUS_GO_NOT_HANDLED,
GoHandled = DEBUG_STATUS_GO_HANDLED,
Go = DEBUG_STATUS_GO,
}
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct DebugOutCbFlags: u32 {
const Text = DEBUG_OUTCB_TEXT;
const Dml = DEBUG_OUTCB_DML;
const ExplicitFlush = DEBUG_OUTCB_EXPLICIT_FLUSH;
}
}
impl_id!(EventFilterIndex: u32);
enum_flags! {
pub enum InterfaceType: u32 {
InterfaceTypeUndefined = 0,
Internal = 1,
Isa = 3,
Eisa = 4,
MicroChannel = 5,
TurboChannel = 6,
PCIBus = 7,
VMEBus = 8,
NuBus = 9,
PCMCIABus = 10,
CBus = 11,
MPIBus = 12,
MPSABus = 13,
ProcessorInternal = 14,
InternalPowerBus = 15,
PNPISABus = 16,
PNPBus = 17,
Vmcs = 18,
ACPIBus = 19,
MaximumInterfaceType = 20,
}
}
bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct DebugSymbolFlags: u32 {
const Expanded = DEBUG_SYMBOL_EXPANDED;
const ReadOnly = DEBUG_SYMBOL_READ_ONLY;
const IsArray = DEBUG_SYMBOL_IS_ARRAY;
const IsFloat = DEBUG_SYMBOL_IS_FLOAT;
const IsArgument = DEBUG_SYMBOL_IS_ARGUMENT;
const IsLocal = DEBUG_SYMBOL_IS_LOCAL;
}
}
#[repr(transparent)]
#[derive(Default, Debug, Clone)]
pub struct DebugSymbolParameters(DEBUG_SYMBOL_PARAMETERS);
impl DebugSymbolParameters {
pub fn as_raw(&self) -> &DEBUG_SYMBOL_PARAMETERS { &self.0 }
pub fn as_mut_raw(&mut self) -> &mut DEBUG_SYMBOL_PARAMETERS { &mut self.0 }
pub fn module(&self) -> DebuggeeOffset { self.0.Module }
pub fn type_id(&self) -> SymbolTypeId {
SymbolTypeId {
module: self.module(),
id: self.0.TypeId,
}
}
pub fn parent_symbol(&self) -> Option<SymbolGroupEntryIndex> {
(self.0.ParentSymbol != DEBUG_ANY_ID)
.then_some(self.0.ParentSymbol.into())
}
pub fn sub_elements(&self) -> u32 { self.0.SubElements }
pub fn flags(&self) -> DebugSymbolFlags {
DebugSymbolFlags::from_bits_retain(self.0.Flags)
}
pub fn reserved(&self) -> u64 { self.0.Reserved }
}
impl From<DEBUG_SYMBOL_PARAMETERS> for DebugSymbolParameters {
fn from(value: DEBUG_SYMBOL_PARAMETERS) -> Self { Self(value) }
}
impl From<DebugSymbolParameters> for DEBUG_SYMBOL_PARAMETERS {
fn from(value: DebugSymbolParameters) -> Self { value.0 }
}
bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct DebugGetModFlags: u32 {
const NoLoadedModules = DEBUG_GETMOD_NO_LOADED_MODULES;
const NoUnloadedModules = DEBUG_GETMOD_NO_UNLOADED_MODULES;
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct SymbolMatchHandle(pub(crate) u64);
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct SymbolTypeId {
pub(crate) module: DebuggeeOffset,
pub(crate) id: u32,
}
impl SymbolTypeId {
pub fn new(module: DebuggeeOffset, id: u32) -> Self { Self { module, id } }
pub fn module(&self) -> Option<DebuggeeOffset> {
(self.module != 0).then_some(self.module)
}
pub fn type_id(&self) -> Option<u32> { (self.id != 0).then_some(self.id) }
pub fn from_name<'a>(
symbols: impl AsRef<WdSymbols>,
module: DebuggeeOffset,
name: impl Into<WideStrArg<'a>>,
) -> WdResult<Self> {
let name = name.into().to_string();
symbols
.as_ref()
.get_type_id(TypeIdSelector::TypeName(module, &name))
}
pub fn get_name(self, symbols: impl AsRef<WdSymbols>) -> WdResult<String> {
symbols.as_ref().get_type_name(self)
}
pub fn get_size(self, symbols: impl AsRef<WdSymbols>) -> WdResult<u32> {
symbols.as_ref().get_type_size(self)
}
pub fn get_field_offset<'a>(
self,
symbols: impl AsRef<WdSymbols>,
field: impl Into<WideStrArg<'a>>,
) -> WdResult<DebuggeeOffset> {
Ok(symbols.as_ref().get_field_offset(self, field)? as u64)
}
pub fn get_field_name(
self,
symbols: impl AsRef<WdSymbols>,
field_index: u32,
) -> WdResult<String> {
Ok(symbols
.as_ref()
.get_field_name(self, field_index)?
.to_string())
}
pub fn get_constant_name(
self,
symbols: impl AsRef<WdSymbols>,
value: u64,
) -> WdResult<String> {
Ok(symbols.as_ref().get_constant_name(self, value)?.to_string())
}
}
impl_id!(ScopeFrameIndex: u32);
impl_id!(EventHandle: u64);
impl_id!(PlmPackageServer: u64);
impl_id!(RegisterIndex: u32);
macro_rules! impl_ri_rw {
($rfunc:ident, $wfunc:ident, $ty:ty) => {
pub fn $rfunc(
self,
registers: impl AsRef<WdRegisters>,
) -> WdResult<$ty> {
registers.as_ref().$rfunc(self)
}
pub fn $wfunc(
self,
registers: impl AsRef<WdRegisters>,
value: $ty,
) -> WdResult<()> {
registers.as_ref().$wfunc(self, value)
}
};
}
impl RegisterIndex {
pub fn from_name<'a>(
registers: impl AsRef<WdRegisters>,
name: impl Into<WideStrArg<'a>>,
) -> WdResult<Self> {
registers.as_ref().get_index_by_name(name)
}
pub fn get_name(
self,
registers: impl AsRef<WdRegisters>,
) -> WdResult<String> {
registers.as_ref().get_name_by_index(self)
}
pub fn get_description(
self,
registers: impl AsRef<WdRegisters>,
) -> WdResult<DebugRegisterDescription> {
registers.as_ref().get_description(self)
}
impl_ri_rw!(read_pointer, write_pointer, DebuggeeOffset);
}
impl_id!(PseudoRegisterIndex: u32);
impl PseudoRegisterIndex {
pub fn from_name<'a>(
registers: impl AsRef<WdRegisters>,
name: impl Into<WideStrArg<'a>>,
) -> WdResult<Self> {
registers.as_ref().get_pseudo_index_by_name(name)
}
pub fn get_name(
self,
registers: impl AsRef<WdRegisters>,
) -> WdResult<String> {
registers.as_ref().get_pseudo_name_by_index(self)
}
pub fn get_type(
self,
registers: impl AsRef<WdRegisters>,
) -> WdResult<SymbolTypeId> {
registers.as_ref().get_pseudo_register_type(self)
}
}
bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct VarType: u16 {
const Empty = VT_EMPTY.0;
const Null = VT_NULL.0;
const I2 = VT_I2.0;
const I4 = VT_I4.0;
const R4 = VT_R4.0;
const R8 = VT_R8.0;
const CY = VT_CY.0;
const Date = VT_DATE.0;
const BStr = VT_BSTR.0;
const Dispatch = VT_DISPATCH.0;
const VtError = VT_ERROR.0;
const Bool = VT_BOOL.0;
const Variant = VT_VARIANT.0;
const Unknown = VT_UNKNOWN.0;
const Decimal = VT_DECIMAL.0;
const I1 = VT_I1.0;
const UI1 = VT_UI1.0;
const UI2 = VT_UI2.0;
const UI4 = VT_UI4.0;
const I8 = VT_I8.0;
const UI8 = VT_UI8.0;
const Int = VT_INT.0;
const UInt = VT_UINT.0;
const Void = VT_VOID.0;
const HResult = VT_HRESULT.0;
const Ptr = VT_PTR.0;
const SafeArray = VT_SAFEARRAY.0;
const CArray = VT_CARRAY.0;
const UserDefined = VT_USERDEFINED.0;
const LPStr = VT_LPSTR.0;
const LPWStr = VT_LPWSTR.0;
const Record = VT_RECORD.0;
const IntPtr = VT_INT_PTR.0;
const UIntPtr = VT_UINT_PTR.0;
const FileType = VT_FILETIME.0;
const Blob = VT_BLOB.0;
const Stream = VT_STREAM.0;
const Storage = VT_STORAGE.0;
const StreamedObject = VT_STREAMED_OBJECT.0;
const StoredObject = VT_STORED_OBJECT.0;
const BlobObject = VT_BLOB_OBJECT.0;
const CF = VT_CF.0;
const CLSID = VT_CLSID.0;
const VersionStream = VT_VERSIONED_STREAM.0;
const BStrBlob = VT_BSTR_BLOB.0;
const Vector = VT_VECTOR.0;
const Array = VT_ARRAY.0;
const ByRef = VT_BYREF.0;
const Reserved = VT_RESERVED.0;
const Illegel = VT_ILLEGAL.0;
const IllegalMasked = VT_ILLEGALMASKED.0;
const TypeMask = VT_TYPEMASK.0;
}
}
impl From<VARENUM> for VarType {
fn from(value: VARENUM) -> Self { Self::from_bits_truncate(value.0) }
}
impl From<VarType> for VARENUM {
fn from(value: VarType) -> Self { VARENUM(value.bits()) }
}
#[derive(Clone)]
pub struct Variant(pub VARIANT);
#[inline(always)]
fn check_variant(variant: &Variant, expected: VarType) -> WdResult<()> {
let actual = variant.vt();
(actual != expected).ok_or_else(|| {
WdErrorKind::VariantTypeMismatch { expected, actual }.into_error()
})
}
macro_rules! impl_variant_as {
($func_name:ident, $try_func_name:ident, $({$c:tt})? $ret_type:ty, $debug_value_type:expr, $elem:ident $(as $new_ty:ty)?) => {
pub fn $try_func_name(&self) -> WdResult< $($c)? $ret_type> {
check_variant(self, $debug_value_type)?;
Ok(unsafe{$($c)? self.0.Anonymous.Anonymous.Anonymous.$elem $(as $new_ty)?})
}
impl_variant_as!(@a $func_name, $try_func_name, $({$c})? $ret_type);
};
(@a $func_name:ident, $try_func_name:ident, $({$c:tt})? $ret_type:ty)=>{
#[doc = concat!("Gets the value as `",stringify!($ty),"`. Panics if the type does not match.")]
pub fn $func_name(&self) -> $($c)? $ret_type {
self.$try_func_name().expect("Invalid VarType conversion")
}
};
}
macro_rules! impl_variant_from {
($ty:ty) => {
impl From<$ty> for Variant {
fn from(value: $ty) -> Self { Self(value.into()) }
}
};
}
impl Variant {
pub fn vt(&self) -> VarType { self.0.vt().into() }
impl_variant_as!(as_i8, try_as_i8, i8, VarType::I1, cVal);
impl_variant_as!(as_u8, try_as_u8, u8, VarType::UI1, bVal);
impl_variant_as!(as_i16, try_as_i16, i16, VarType::I2, iVal);
impl_variant_as!(as_u16, try_as_u16, u16, VarType::UI2, uiVal);
impl_variant_as!(as_i32, try_as_i32, i32, VarType::I4, lVal);
impl_variant_as!(as_u32, try_as_u32, u32, VarType::UI4, ulVal);
impl_variant_as!(as_i64, try_as_i64, i64, VarType::I8, llVal);
impl_variant_as!(as_u64, try_as_u64, u64, VarType::UI8, ullVal);
impl_variant_as!(as_f32, try_as_f32, f32, VarType::R4, fltVal);
impl_variant_as!(as_f64, try_as_f64, f64, VarType::R8, dblVal);
pub fn try_as_bool(&self) -> WdResult<bool> {
check_variant(self, VarType::Bool)?;
Ok(unsafe { self.0.Anonymous.Anonymous.Anonymous.boolVal.as_bool() })
}
pub fn as_bool(&self) -> bool { self.try_as_bool().unwrap() }
pub fn try_as_string(&self) -> WdResult<String> {
check_variant(self, VarType::BStr)?;
Ok(unsafe { self.0.Anonymous.Anonymous.Anonymous.bstrVal.to_string() })
}
pub fn as_string(&self) -> String { self.try_as_string().unwrap() }
}
impl AsRef<VARIANT> for Variant {
fn as_ref(&self) -> &VARIANT { &self.0 }
}
impl From<VARIANT> for Variant {
fn from(value: VARIANT) -> Self { Self(value) }
}
impl From<IUnknown> for Variant {
fn from(value: IUnknown) -> Self { Self(value.into()) }
}
impl From<Variant> for VARIANT {
fn from(value: Variant) -> Self { value.0 }
}
impl_variant_from!(bool);
impl_variant_from!(u8);
impl_variant_from!(i8);
impl_variant_from!(u16);
impl_variant_from!(i16);
impl_variant_from!(u32);
impl_variant_from!(i32);
impl_variant_from!(i64);
impl_variant_from!(u64);
impl_variant_from!(&str);
impl From<String> for Variant {
fn from(value: String) -> Self { Self(value.as_str().into()) }
}
pub struct ProcessHandle(u64);
impl ProcessHandle {
pub(crate) fn new(handle: u64) -> Self { Self(handle) }
pub fn as_raw_handle(&self) -> u64 { self.0 }
}
pub struct ThreadHandle(u64);
impl ThreadHandle {
pub(crate) fn new(handle: u64) -> Self { Self(handle) }
pub fn as_raw_handle(&self) -> u64 { self.0 }
}
#[repr(transparent)]
#[derive(Debug, Clone)]
pub struct DebugStackFrame(pub DEBUG_STACK_FRAME);
impl DebugStackFrame {
pub fn instruction_offset(&self) -> DebuggeeOffset {
self.0.InstructionOffset
}
pub fn return_offset(&self) -> DebuggeeOffset { self.0.ReturnOffset }
pub fn frame_offset(&self) -> DebuggeeOffset { self.0.FrameOffset }
pub fn stack_offset(&self) -> DebuggeeOffset { self.0.StackOffset }
pub fn func_table_entry(&self) -> DebuggeeOffset { self.0.FuncTableEntry }
pub fn params(&self) -> &[u64; 4] { &self.0.Params }
pub fn reserved(&self) -> &[u64; 6] { &self.0.Reserved }
pub fn r#virtual(&self) -> bool { self.0.Virtual.as_bool() }
pub fn frame_number(&self) -> u32 { self.0.FrameNumber }
}
#[repr(transparent)]
#[derive(Debug, Clone)]
pub struct DebugStackFrameEx(pub DEBUG_STACK_FRAME_EX);
impl DebugStackFrameEx {
pub fn instruction_offset(&self) -> DebuggeeOffset {
self.0.InstructionOffset
}
pub fn return_offset(&self) -> DebuggeeOffset { self.0.ReturnOffset }
pub fn frame_offset(&self) -> DebuggeeOffset { self.0.FrameOffset }
pub fn stack_offset(&self) -> DebuggeeOffset { self.0.StackOffset }
pub fn func_table_entry(&self) -> DebuggeeOffset { self.0.FuncTableEntry }
pub fn params(&self) -> &[u64; 4] { &self.0.Params }
pub fn reserved(&self) -> &[u64; 6] { &self.0.Reserved }
pub fn r#virtual(&self) -> bool { self.0.Virtual.as_bool() }
pub fn frame_number(&self) -> u32 { self.0.FrameNumber }
pub fn inline_frame_context(&self) -> u32 { self.0.InlineFrameContext }
pub fn frame_machine(&self) -> u32 { self.0.Reserved1 }
}
#[repr(transparent)]
pub struct WindbgExtensionApis64(WINDBG_EXTENSION_APIS64);
impl Default for WindbgExtensionApis64 {
fn default() -> Self {
let mut ret = Self(WINDBG_EXTENSION_APIS64::default());
ret.0.nSize = size_of::<WINDBG_EXTENSION_APIS64>() as u32;
ret
}
}
impl WindbgExtensionApis64 {
pub fn output<'a>(
&self,
s: impl Into<AnsiStrArg<'a, CP_ACP>>,
) -> WdResult<()> {
let Some(f) = self.0.lpOutputRoutine else {
return Err(E_POINTER.into());
};
type Func = unsafe extern "C" fn(PCSTR, PCSTR);
unsafe {
let f: Func = std::mem::transmute(f);
f(
PCSTR(acpstr!("%s").as_u8_ptr()),
PCSTR(s.into().into_ansi()?.as_u8_ptr()),
);
}
Ok(())
}
pub fn dprintf<'a>(
&self,
s: impl Into<AnsiStrArg<'a, CP_ACP>>,
) -> WdResult<()> {
self.output(s)
}
pub fn get_expression<'a>(
&self,
expression: impl Into<AnsiStrArg<'a, CP_ACP>>,
) -> WdResult<u64> {
let Some(f) = self.0.lpGetExpressionRoutine else {
return Err(E_POINTER.into());
};
let res =
unsafe { f(PCSTR(expression.into().into_ansi()?.as_u8_ptr())) };
Ok(res)
}
pub fn get_symbol(
&self,
offset: DebuggeeOffset,
) -> WdResult<(String, u64)> {
let Some(f) = self.0.lpGetSymbolRoutine else {
return Err(E_POINTER.into());
};
let mut buf = Vec::with_capacity(256);
let mut displacement = 0;
unsafe {
f(offset, PCSTR(buf.as_mut_ptr()), &mut displacement);
Ok((
ACPString::clone_from_raw(buf.as_ptr()).to_string(),
displacement,
))
}
}
pub fn disasm(
&self,
mut offset: DebuggeeOffset,
show_effective_address: bool,
) -> WdResult<(String, u64)> {
let Some(f) = self.0.lpDisasmRoutine else {
return Err(E_POINTER.into());
};
let mut buf = Vec::with_capacity(2000);
unsafe {
let res = f(
&mut offset,
PCSTR(buf.as_mut_ptr()),
show_effective_address as u32,
);
if res == 0 {
return Err(WdErrorKind::InvalidArgument.into_error());
}
Ok((ACPString::clone_from_raw(buf.as_ptr()).to_string(), offset))
}
}
pub fn check_control_c(&self) -> WdResult<bool> {
let Some(f) = self.0.lpCheckControlCRoutine else {
return Err(E_POINTER.into());
};
unsafe { Ok(f() != 0) }
}
pub fn read_process_memory(
&self,
offset: DebuggeeOffset,
buf: &mut [u8],
) -> WdResult<usize> {
let Some(f) = self.0.lpReadProcessMemoryRoutine else {
return Err(E_POINTER.into());
};
let mut bytes_read = 0;
let res = unsafe {
f(
offset,
buf.as_mut_ptr() as *mut c_void,
buf.len().try_into()?,
&mut bytes_read,
)
};
if res == 0 {
return Err(WdErrorKind::ReadMemoryError {
address: offset,
bytes_read: bytes_read as usize,
}
.into_error());
}
Ok(bytes_read as usize)
}
pub fn write_process_memory(
&self,
offset: DebuggeeOffset,
buf: &[u8],
) -> WdResult<usize> {
let Some(f) = self.0.lpWriteProcessMemoryRoutine else {
return Err(E_POINTER.into());
};
let mut bytes_written = 0;
let res = unsafe {
f(
offset,
buf.as_ptr() as *mut c_void,
buf.len().try_into()?,
&mut bytes_written,
)
};
if res == 0 {
return Err(WdErrorKind::WriteMemoryError {
address: offset,
bytes_write: bytes_written as usize,
}
.into_error());
}
Ok(bytes_written as usize)
}
pub fn get_thread_context<C: Context>(
&self,
processor: u32,
) -> WdResult<C> {
let Some(f) = self.0.lpGetThreadContextRoutine else {
return Err(E_POINTER.into());
};
let size_of_context = size_of::<C>() as u32;
let mut context = MaybeUninit::<C>::uninit();
let res = unsafe {
f(processor, context.as_mut_ptr() as *mut _, size_of_context)
};
if res == 0 {
return Err(WdErrorKind::InvalidArgument.into_error());
}
unsafe { Ok(context.assume_init()) }
}
pub fn set_thread_context<C: Context>(
&self,
processor: u32,
context: &mut C,
) -> WdResult<()> {
let Some(f) = self.0.lpSetThreadContextRoutine else {
return Err(E_POINTER.into());
};
let size_of_context = size_of::<C>() as u32;
let res = unsafe {
f(processor, context as *mut C as *mut _, size_of_context)
};
if res == 0 {
return Err(WdErrorKind::InvalidArgument.into_error());
}
Ok(())
}
pub(crate) unsafe fn ioctl(
&self,
ioctl_type: u16,
data: *mut c_void,
size: u32,
) -> WdResult<u32> {
let Some(f) = self.0.lpIoctlRoutine else {
return Err(E_POINTER.into());
};
let res = unsafe { f(ioctl_type, data, size) };
Ok(res)
}
pub fn stack_trace(
&self,
frame_pointer: DebuggeeOffset,
stack_pointer: DebuggeeOffset,
program_counter: DebuggeeOffset,
frames: u32,
) -> WdResult<Vec<EXTSTACKTRACE64>> {
let f = self.0.lpStackTraceRoutine.ok_or(E_POINTER)?;
let mut stack_frames = Vec::with_capacity(frames as usize);
let res = unsafe {
f(
frame_pointer,
stack_pointer,
program_counter,
stack_frames.as_mut_ptr(),
frames,
)
};
if res == 0 {
return Err(WdErrorKind::InvalidArgument.into_error());
}
assert!(res <= frames);
unsafe {
stack_frames.set_len(res as usize);
}
Ok(stack_frames)
}
pub fn is_ptr64(&self) -> WdResult<bool> {
let mut is_ptr64 = false;
let mut ret = 0u32;
let res = unsafe {
self.ioctl(
IG_IS_PTR64 as u16,
&mut ret as *mut u32 as *mut _,
size_of_val(&ret) as u32,
)?
};
if res != 0 {
is_ptr64 = ret != 0;
}
Ok(is_ptr64)
}
pub(crate) unsafe fn query_target_interface<T: TtdTargetInterface>(
&self,
) -> WdResult<T> {
#[repr(C)]
struct S {
iid: *const GUID,
interface: *mut c_void,
}
let iid = T::IID;
let mut s = S {
iid: &iid,
interface: std::ptr::null_mut(),
};
unsafe {
let res = self.ioctl(
IG_QUERY_TARGET_INTERFACE as u16,
&mut s as *mut _ as *mut _,
size_of::<S>() as u32,
)?;
if res == 0 || s.interface.is_null() {
return Err(WdErrorKind::InterfaceNotSupported.into_error());
}
Ok(T::from_target_raw(s.interface))
}
}
pub unsafe fn get_cursor_view(&self) -> WdResult<CursorView> {
Ok(unsafe { self.query_target_interface::<ICursorView>()? }.into())
}
pub unsafe fn get_replay_engine_view(&self) -> WdResult<ReplayEngineView> {
Ok(
unsafe { self.query_target_interface::<IReplayEngineView>()? }
.into(),
)
}
}