#![allow(clippy::too_many_arguments)]
use crate::dbgeng::*;
use std::{cell::Cell, rc::Rc};
use windows::{
Win32::{
Foundation::E_POINTER, System::Diagnostics::Debug::EXCEPTION_RECORD64,
},
core::{HRESULT, PCSTR, Ref, implement},
};
use windows_core::PCWSTR;
use windy::{
ACPStr, WStr,
traits::{ToAString, ToWString},
};
fn ds2hr(s: DebugStatus) -> windows::core::Result<()> {
if s == DebugStatus::NoChange {
Ok(())
} else {
Err(windows::core::Error::from_hresult(HRESULT(s as i32)))
}
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone,Eq, PartialEq)]
pub struct DebugEventFlags: u32 {
const Breakpoint = DEBUG_EVENT_BREAKPOINT;
const Exception = DEBUG_EVENT_EXCEPTION;
const CreateThread = DEBUG_EVENT_CREATE_THREAD;
const ExitThread = DEBUG_EVENT_EXIT_THREAD;
const CreateProcess = DEBUG_EVENT_CREATE_PROCESS;
const ExitProcess = DEBUG_EVENT_EXIT_PROCESS;
const LoadModule = DEBUG_EVENT_LOAD_MODULE;
const UnloadModule = DEBUG_EVENT_UNLOAD_MODULE;
const SystemError = DEBUG_EVENT_SYSTEM_ERROR;
const SessionStatus = DEBUG_EVENT_SESSION_STATUS;
const ChangeDebuggeeState = DEBUG_EVENT_CHANGE_DEBUGGEE_STATE;
const ChangeEngineState = DEBUG_EVENT_CHANGE_ENGINE_STATE;
const ChangeSymbolState = DEBUG_EVENT_CHANGE_SYMBOL_STATE;
}
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone,Eq, PartialEq)]
pub struct DebugCdsFlags: u32 {
const All = DEBUG_CDS_ALL;
const Registers = DEBUG_CDS_REGISTERS;
const Data = DEBUG_CDS_DATA;
const Refresh = DEBUG_CDS_REFRESH;
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum DebugCdsArg {
All,
Registers(Option<RegisterIndex>),
Data(DebugDataSpaceFlags),
Refresh(DebugCdsRefreshFlags),
Other { flags: DebugCdsFlags, argument: u64 },
}
impl DebugCdsArg {
pub(crate) fn from_arg(flags: u32, argument: u64) -> Self {
let flags = DebugCdsFlags::from_bits_retain(flags);
match flags {
DebugCdsFlags::All => Self::All,
DebugCdsFlags::Registers => Self::Registers(
(argument != DEBUG_ANY_ID as u64)
.then_some(RegisterIndex::from(argument as u32)),
),
DebugCdsFlags::Data => Self::Data(
DebugDataSpaceFlags::from_bits_retain(argument as u32),
),
DebugCdsFlags::Refresh => Self::Refresh(
DebugCdsRefreshFlags::from_bits_retain(argument as u32),
),
flags => Self::Other { flags, argument },
}
}
pub fn as_flags(&self) -> DebugCdsFlags {
match self {
DebugCdsArg::All => DebugCdsFlags::All,
DebugCdsArg::Registers(_) => DebugCdsFlags::Registers,
DebugCdsArg::Data(_) => DebugCdsFlags::Data,
DebugCdsArg::Refresh(_) => DebugCdsFlags::Refresh,
DebugCdsArg::Other { flags, .. } => *flags,
}
}
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone,Eq, PartialEq)]
pub struct DebugDataSpaceFlags: u32 {
const Virtual = DEBUG_DATA_SPACE_VIRTUAL;
const Physical = DEBUG_DATA_SPACE_PHYSICAL;
const Control = DEBUG_DATA_SPACE_CONTROL;
const Io = DEBUG_DATA_SPACE_IO;
const Msr = DEBUG_DATA_SPACE_MSR;
const BusData = DEBUG_DATA_SPACE_BUS_DATA;
}
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone,Eq, PartialEq)]
pub struct DebugCdsRefreshFlags: u32 {
const Evaluate = DEBUG_CDS_REFRESH_EVALUATE;
const Execute = DEBUG_CDS_REFRESH_EXECUTE;
const ExecuteCommandFile = DEBUG_CDS_REFRESH_EXECUTECOMMANDFILE;
const AddBreakpoint = DEBUG_CDS_REFRESH_ADDBREAKPOINT;
const RemoveBreakpoint = DEBUG_CDS_REFRESH_REMOVEBREAKPOINT;
const WriteVirtual = DEBUG_CDS_REFRESH_WRITEVIRTUAL;
const WriteVirtualUncached = DEBUG_CDS_REFRESH_WRITEVIRTUALUNCACHED;
const WritePhysical = DEBUG_CDS_REFRESH_WRITEPHYSICAL;
const WritePhysical2 = DEBUG_CDS_REFRESH_WRITEPHYSICAL2;
const SetValue = DEBUG_CDS_REFRESH_SETVALUE;
const SetValue2 = DEBUG_CDS_REFRESH_SETVALUE2;
const SetScope = DEBUG_CDS_REFRESH_SETSCOPE;
const SetScopeFrameByIndex = DEBUG_CDS_REFRESH_SETSCOPEFRAMEBYINDEX;
const SetScopeFromJitDebugInfo = DEBUG_CDS_REFRESH_SETSCOPEFROMJITDEBUGINFO;
const SetScopeFromStoredEvent = DEBUG_CDS_REFRESH_SETSCOPEFROMSTOREDEVENT;
const InlineStep = DEBUG_CDS_REFRESH_INLINESTEP;
const InlineStepPseudo = DEBUG_CDS_REFRESH_INLINESTEP_PSEUDO ;
}
}
enum_flags! {
pub enum DebugSessionStatus: u32 {
Active = DEBUG_SESSION_ACTIVE,
EndSessionActiveTerminate = DEBUG_SESSION_END_SESSION_ACTIVE_TERMINATE,
EndSessionActiveDetach = DEBUG_SESSION_END_SESSION_ACTIVE_DETACH,
EndSessionPassive = DEBUG_SESSION_END_SESSION_PASSIVE,
End = DEBUG_SESSION_END,
Reboot = DEBUG_SESSION_REBOOT,
Hibernate = DEBUG_SESSION_HIBERNATE,
Failure = DEBUG_SESSION_FAILURE,
}
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct DebugCesFlags: u32 {
const CurrentThread = DEBUG_CES_CURRENT_THREAD;
const EffectiveProcessor = DEBUG_CES_EFFECTIVE_PROCESSOR;
const Breakpoints = DEBUG_CES_BREAKPOINTS;
const CodeLevel = DEBUG_CES_CODE_LEVEL;
const ExecutionStatus = DEBUG_CES_EXECUTION_STATUS;
const EngineOptions = DEBUG_CES_ENGINE_OPTIONS;
const LogFile = DEBUG_CES_LOG_FILE;
const Radix = DEBUG_CES_RADIX;
const EventFilters = DEBUG_CES_EVENT_FILTERS;
const ProcessOptions = DEBUG_CES_PROCESS_OPTIONS;
const Extensions = DEBUG_CES_EXTENSIONS;
const Systems = DEBUG_CES_SYSTEMS;
const AssemblyOptions = DEBUG_CES_ASSEMBLY_OPTIONS;
const ExpressionSyntax = DEBUG_CES_EXPRESSION_SYNTAX;
const TextReplacements = DEBUG_CES_TEXT_REPLACEMENTS;
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum DebugCesArg {
CurrentThread(Option<EngineThreadId>),
EffectiveProcessor(ProcessorType),
Breakpoints(Option<BreakpointId>),
CodeLevel(CodeLevel),
ExecutionStatus((DebugStatus, bool)),
EngineOptions(EngineOptions),
LogFile(bool),
Radix(u64),
EventFilters(Option<EventFilterIndex>),
ProcessOptions(DebugProcessFlags),
Extensions,
Systems(Option<EngineSystemId>),
AssemblyOptions(DebugAsmOptFlags),
ExpressionSyntax(DebugExprOptions),
TextReplacements,
Other { flags: DebugCesFlags, argument: u64 },
}
impl DebugCesArg {
pub(crate) fn from_arg(flags: u32, argument: u64) -> Self {
let flags = DebugCesFlags::from_bits_retain(flags);
match flags {
DebugCesFlags::CurrentThread => Self::CurrentThread(
(argument != DEBUG_ANY_ID as u64)
.then_some((argument as u32).into()),
),
DebugCesFlags::EffectiveProcessor => {
Self::EffectiveProcessor(ProcessorType::from(argument as u32))
}
DebugCesFlags::Breakpoints => Self::Breakpoints(
(argument != DEBUG_ANY_ID as u64)
.then_some(BreakpointId(argument as u32)),
),
DebugCesFlags::CodeLevel => {
Self::CodeLevel(CodeLevel::try_from(argument as u32).unwrap())
}
DebugCesFlags::ExecutionStatus => {
let mut inside_wait = false;
if argument & DEBUG_STATUS_INSIDE_WAIT != 0 {
inside_wait = true;
}
Self::ExecutionStatus((
DebugStatus::try_from(
(argument & !DEBUG_STATUS_INSIDE_WAIT) as u32,
)
.unwrap(),
inside_wait,
))
}
DebugCesFlags::EngineOptions => Self::EngineOptions(
EngineOptions::from_bits_retain(argument as u32),
),
DebugCesFlags::LogFile => Self::LogFile(argument != 0),
DebugCesFlags::Radix => Self::Radix(argument),
DebugCesFlags::EventFilters => Self::EventFilters(
(argument != DEBUG_ANY_ID as u64)
.then_some(EventFilterIndex(argument as u32)),
),
DebugCesFlags::ProcessOptions => Self::ProcessOptions(
DebugProcessFlags::from_bits_retain(argument as u32),
),
DebugCesFlags::Extensions => Self::Extensions,
DebugCesFlags::Systems => Self::Systems(
(argument != DEBUG_ANY_ID as u64)
.then_some(EngineSystemId(argument as u32)),
),
DebugCesFlags::AssemblyOptions => Self::AssemblyOptions(
DebugAsmOptFlags::from_bits_retain(argument as u32),
),
DebugCesFlags::ExpressionSyntax => Self::ExpressionSyntax(
DebugExprOptions::try_from(argument as u32)
.expect("Invalid expression syntax"),
),
DebugCesFlags::TextReplacements => Self::TextReplacements,
flags => Self::Other { flags, argument },
}
}
pub fn as_flags(&self) -> DebugCesFlags {
match self {
DebugCesArg::CurrentThread(_) => DebugCesFlags::CurrentThread,
DebugCesArg::EffectiveProcessor(_) => {
DebugCesFlags::EffectiveProcessor
}
DebugCesArg::Breakpoints(_) => DebugCesFlags::Breakpoints,
DebugCesArg::CodeLevel(_) => DebugCesFlags::CodeLevel,
DebugCesArg::ExecutionStatus(_) => DebugCesFlags::ExecutionStatus,
DebugCesArg::EngineOptions(_) => DebugCesFlags::EngineOptions,
DebugCesArg::LogFile(_) => DebugCesFlags::LogFile,
DebugCesArg::Radix(_) => DebugCesFlags::Radix,
DebugCesArg::EventFilters(_) => DebugCesFlags::EventFilters,
DebugCesArg::ProcessOptions(_) => DebugCesFlags::ProcessOptions,
DebugCesArg::Extensions => DebugCesFlags::Extensions,
DebugCesArg::Systems(_) => DebugCesFlags::Systems,
DebugCesArg::AssemblyOptions(_) => DebugCesFlags::AssemblyOptions,
DebugCesArg::ExpressionSyntax(_) => DebugCesFlags::ExpressionSyntax,
DebugCesArg::TextReplacements => DebugCesFlags::TextReplacements,
DebugCesArg::Other { flags, .. } => *flags,
}
}
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct DebugCssFlags: u32 {
const Loads = DEBUG_CSS_LOADS;
const Unloads = DEBUG_CSS_UNLOADS;
const Scope = DEBUG_CSS_SCOPE;
const Paths = DEBUG_CSS_PATHS;
const SymbolOptions = DEBUG_CSS_SYMBOL_OPTIONS;
const TypeOptions = DEBUG_CSS_TYPE_OPTIONS;
const CollapseChildren = DEBUG_CSS_COLLAPSE_CHILDREN;
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum DebugCssArg {
Loads(DebuggeeOffset),
Unloads(Option<DebuggeeOffset>),
Scope,
Paths,
SymbolOptions(SymbolOptions),
TypeOptions,
Other { flags: DebugCssFlags, argument: u64 },
}
impl DebugCssArg {
pub(crate) fn from_arg(flags: u32, argument: u64) -> Self {
let flags = DebugCssFlags::from_bits_retain(flags);
match flags {
DebugCssFlags::Loads => Self::Loads(argument),
DebugCssFlags::Unloads => {
Self::Unloads((argument != 0).then_some(argument))
}
DebugCssFlags::Scope => Self::Scope,
DebugCssFlags::Paths => Self::Paths,
DebugCssFlags::SymbolOptions => Self::SymbolOptions(
SymbolOptions::from_bits_retain(argument as u32),
),
DebugCssFlags::TypeOptions => Self::TypeOptions,
flags => Self::Other { flags, argument },
}
}
pub fn as_flags(&self) -> DebugCssFlags {
match self {
DebugCssArg::Loads(_) => DebugCssFlags::Loads,
DebugCssArg::Unloads(_) => DebugCssFlags::Unloads,
DebugCssArg::Scope => DebugCssFlags::Scope,
DebugCssArg::Paths => DebugCssFlags::Paths,
DebugCssArg::SymbolOptions(_) => DebugCssFlags::SymbolOptions,
DebugCssArg::TypeOptions => DebugCssFlags::TypeOptions,
DebugCssArg::Other { flags, .. } => *flags,
}
}
}
#[allow(unused_variables)]
pub trait DebugEventCallbacksHandler {
fn get_interest_mask(&self) -> windows::core::Result<DebugEventFlags> {
Ok(DebugEventFlags::all())
}
fn breakpoint(&self, bp: DebugBreakpointRef) -> DebugStatus {
DebugStatus::NoChange
}
fn exception(
&self,
exception: &EXCEPTION_RECORD64,
first_chance: u32,
) -> DebugStatus {
DebugStatus::NoChange
}
fn create_thread(
&self,
handle: DebuggeeOffset,
data_offset: DebuggeeOffset,
start_offset: DebuggeeOffset,
) -> DebugStatus {
DebugStatus::NoChange
}
fn exit_thread(&self, exit_code: u32) -> DebugStatus {
DebugStatus::NoChange
}
fn create_process(
&self,
image_file_handle: DebuggeeOffset,
handle: DebuggeeOffset,
base_offset: DebuggeeOffset,
module_size: u32,
module_name: Option<String>,
image_name: Option<String>,
checksum: u32,
time_date_stamp: u32,
initial_thread_handle: DebuggeeOffset,
thread_data_offset: DebuggeeOffset,
start_offset: DebuggeeOffset,
) -> DebugStatus {
DebugStatus::NoChange
}
fn exit_process(&self, exit_code: u32) -> DebugStatus {
DebugStatus::NoChange
}
fn load_module(
&self,
image_file_handle: DebuggeeOffset,
base_offset: DebuggeeOffset,
module_size: u32,
module_name: Option<String>,
image_name: Option<String>,
checksum: u32,
time_date_stamp: u32,
) -> DebugStatus {
DebugStatus::NoChange
}
fn unload_module(
&self,
image_base_name: Option<String>,
base_offset: DebuggeeOffset,
) -> DebugStatus {
DebugStatus::NoChange
}
fn system_error(&self, error: u32, level: u32) -> DebugStatus {
DebugStatus::NoChange
}
fn session_status(
&self,
status: DebugSessionStatus,
) -> windows::core::Result<()> {
Ok(())
}
fn change_debuggee_state(
&self,
arg: DebugCdsArg,
) -> windows::core::Result<()> {
Ok(())
}
fn change_engine_state(
&self,
arg: DebugCesArg,
) -> windows::core::Result<()> {
Ok(())
}
fn change_symbol_state(
&self,
arg: DebugCssArg,
) -> windows::core::Result<()> {
Ok(())
}
}
impl_debug_interface!(
DebugEventCallbacks,
DebugEventCallbacksRef,
IDebugEventCallbacks
);
impl From<DebugEventCallbacks> for IDebugEventCallbacks {
fn from(value: DebugEventCallbacks) -> Self { value.0 }
}
impl DebugEventCallbacksHandler for DebugEventCallbacks {
fn get_interest_mask(&self) -> windows::core::Result<DebugEventFlags> {
unsafe {
Ok(DebugEventFlags::from_bits_retain(self.0.GetInterestMask()?))
}
}
fn breakpoint(&self, bp: DebugBreakpointRef) -> DebugStatus {
unsafe {
DebugStatus::try_from(
vcall!(self, Breakpoint, bp.as_ptr()).0 as u32,
)
.expect("Invalid DebugStatus")
}
}
fn exception(
&self,
exception: &EXCEPTION_RECORD64,
first_chance: u32,
) -> DebugStatus {
unsafe {
DebugStatus::try_from(
vcall!(self, Exception, exception, first_chance).0 as u32,
)
.expect("Invalid DebugStatus")
}
}
fn create_thread(
&self,
handle: DebuggeeOffset,
data_offset: DebuggeeOffset,
start_offset: DebuggeeOffset,
) -> DebugStatus {
unsafe {
DebugStatus::try_from(
vcall!(self, CreateThread, handle, data_offset, start_offset,).0
as u32,
)
.expect("Invalid DebugStatus")
}
}
fn exit_thread(&self, exit_code: u32) -> DebugStatus {
unsafe {
DebugStatus::try_from(vcall!(self, ExitThread, exit_code).0 as u32)
.expect("Invalid DebugStatus")
}
}
fn create_process(
&self,
image_file_handle: DebuggeeOffset,
handle: DebuggeeOffset,
base_offset: DebuggeeOffset,
module_size: u32,
module_name: Option<String>,
image_name: Option<String>,
checksum: u32,
time_date_stamp: u32,
initial_thread_handle: DebuggeeOffset,
thread_data_offset: DebuggeeOffset,
start_offset: DebuggeeOffset,
) -> DebugStatus {
unsafe {
let module_name: Option<ACPString> =
module_name.map(|x| x.to_astring_lossy());
let image_name: Option<ACPString> =
image_name.map(|x| x.to_astring_lossy());
DebugStatus::try_from(
vcall!(
self,
CreateProcessA,
image_file_handle,
handle,
base_offset,
module_size,
PCSTR(
module_name
.as_ref()
.map_or(std::ptr::null_mut(), |x| x.as_u8_ptr())
),
PCSTR(
image_name
.as_ref()
.map_or(std::ptr::null_mut(), |x| x.as_u8_ptr())
),
checksum,
time_date_stamp,
initial_thread_handle,
thread_data_offset,
start_offset,
)
.0 as u32,
)
.expect("Invalid DebugStatus")
}
}
fn exit_process(&self, exit_code: u32) -> DebugStatus {
unsafe {
DebugStatus::try_from(vcall!(self, ExitProcess, exit_code).0 as u32)
.expect("Invalid DebugStatus")
}
}
fn load_module(
&self,
image_file_handle: DebuggeeOffset,
base_offset: DebuggeeOffset,
module_size: u32,
module_name: Option<String>,
image_name: Option<String>,
checksum: u32,
time_date_stamp: u32,
) -> DebugStatus {
unsafe {
let module_name: Option<ACPString> =
module_name.map(|x| x.to_astring_lossy());
let image_name: Option<ACPString> =
image_name.map(|x| x.to_astring_lossy());
DebugStatus::try_from(
vcall!(
self,
LoadModule,
image_file_handle,
base_offset,
module_size,
PCSTR(
module_name
.as_ref()
.map_or(std::ptr::null_mut(), |x| x.as_u8_ptr())
),
PCSTR(
image_name
.as_ref()
.map_or(std::ptr::null_mut(), |x| x.as_u8_ptr())
),
checksum,
time_date_stamp
)
.0 as u32,
)
.expect("Invalid DebugStatus")
}
}
fn unload_module(
&self,
image_base_name: Option<String>,
base_offset: DebuggeeOffset,
) -> DebugStatus {
unsafe {
let image_base_name: Option<ACPString> =
image_base_name.map(|x| x.to_astring_lossy());
DebugStatus::try_from(
vcall!(
self,
UnloadModule,
PCSTR(
image_base_name
.as_ref()
.map_or(std::ptr::null_mut(), |x| x.as_u8_ptr())
),
base_offset
)
.0 as u32,
)
.expect("Invalid DebugStatus")
}
}
fn system_error(&self, error: u32, level: u32) -> DebugStatus {
unsafe {
DebugStatus::try_from(
vcall!(self, SystemError, error, level).0 as u32,
)
.expect("Invalid DebugStatus")
}
}
fn session_status(
&self,
status: DebugSessionStatus,
) -> windows::core::Result<()> {
unsafe { self.0.SessionStatus(status as u32) }
}
fn change_debuggee_state(
&self,
arg: DebugCdsArg,
) -> windows::core::Result<()> {
unsafe {
match arg {
DebugCdsArg::All => {
self.0.ChangeDebuggeeState(arg.as_flags().bits(), 0)
}
DebugCdsArg::Registers(x) => self.0.ChangeDebuggeeState(
arg.as_flags().bits(),
x.map_or(DEBUG_ANY_ID, |x| x.0) as u64,
),
DebugCdsArg::Data(x) => self.0.ChangeDebuggeeState(
arg.as_flags().bits(),
x.bits() as u64,
),
DebugCdsArg::Refresh(x) => self.0.ChangeDebuggeeState(
arg.as_flags().bits(),
x.bits() as u64,
),
DebugCdsArg::Other { flags, argument } => {
self.0.ChangeDebuggeeState(flags.bits(), argument)
}
}
}
}
fn change_engine_state(
&self,
arg: DebugCesArg,
) -> windows::core::Result<()> {
unsafe {
match arg {
DebugCesArg::CurrentThread(x) => self.0.ChangeEngineState(
arg.as_flags().bits(),
x.map_or(DEBUG_ANY_ID as u64, |x| x.0 as u64),
),
DebugCesArg::EffectiveProcessor(x) => {
self.0.ChangeEngineState(arg.as_flags().bits(), x as u64)
}
DebugCesArg::Breakpoints(x) => self.0.ChangeEngineState(
arg.as_flags().bits(),
x.map_or(DEBUG_ANY_ID, |x| x.0) as u64,
),
DebugCesArg::CodeLevel(x) => {
self.0.ChangeEngineState(arg.as_flags().bits(), x as u64)
}
DebugCesArg::ExecutionStatus((status, inside_wait)) => {
let mut argument = status as u64;
if inside_wait {
argument |= DEBUG_STATUS_INSIDE_WAIT;
}
self.0.ChangeEngineState(arg.as_flags().bits(), argument)
}
DebugCesArg::EngineOptions(x) => self
.0
.ChangeEngineState(arg.as_flags().bits(), x.bits() as u64),
DebugCesArg::LogFile(x) => self.0.ChangeEngineState(
arg.as_flags().bits(),
if x { 1 } else { 0 },
),
DebugCesArg::Radix(x) => {
self.0.ChangeEngineState(arg.as_flags().bits(), x)
}
DebugCesArg::EventFilters(x) => self.0.ChangeEngineState(
arg.as_flags().bits(),
x.map_or(DEBUG_ANY_ID, |x| x.0) as u64,
),
DebugCesArg::ProcessOptions(x) => self
.0
.ChangeEngineState(arg.as_flags().bits(), x.bits() as u64),
DebugCesArg::Extensions => {
self.0.ChangeEngineState(arg.as_flags().bits(), 0)
}
DebugCesArg::Systems(x) => self.0.ChangeEngineState(
arg.as_flags().bits(),
x.map_or(DEBUG_ANY_ID, |x| x.0) as u64,
),
DebugCesArg::AssemblyOptions(x) => self
.0
.ChangeEngineState(arg.as_flags().bits(), x.bits() as u64),
DebugCesArg::ExpressionSyntax(x) => {
self.0.ChangeEngineState(arg.as_flags().bits(), x as u64)
}
DebugCesArg::TextReplacements => self.0.ChangeEngineState(
arg.as_flags().bits(),
DEBUG_ANY_ID as u64,
),
DebugCesArg::Other { flags, argument } => {
self.0.ChangeEngineState(flags.bits(), argument)
}
}
}
}
fn change_symbol_state(
&self,
arg: DebugCssArg,
) -> windows::core::Result<()> {
unsafe {
match arg {
DebugCssArg::Loads(x) => {
self.0.ChangeSymbolState(arg.as_flags().bits(), x)
}
DebugCssArg::Unloads(x) => self
.0
.ChangeSymbolState(arg.as_flags().bits(), x.unwrap_or(0)),
DebugCssArg::Scope => {
self.0.ChangeSymbolState(arg.as_flags().bits(), 0)
}
DebugCssArg::Paths => {
self.0.ChangeSymbolState(arg.as_flags().bits(), 0)
}
DebugCssArg::SymbolOptions(x) => self
.0
.ChangeSymbolState(arg.as_flags().bits(), x.bits() as u64),
DebugCssArg::TypeOptions => {
self.0.ChangeSymbolState(arg.as_flags().bits(), 0)
}
DebugCssArg::Other { flags, argument } => {
self.0.ChangeSymbolState(flags.bits(), argument)
}
}
}
}
}
#[implement(IDebugEventCallbacks)]
pub struct DebugEventCallbacksAdapter {
pub callbacks: Rc<dyn DebugEventCallbacksHandler>,
poisoned: Cell<bool>,
}
impl DebugEventCallbacksAdapter {
pub fn new(callbacks: Rc<dyn DebugEventCallbacksHandler>) -> Self {
Self {
callbacks,
poisoned: Cell::new(false),
}
}
pub fn as_handler(&self) -> &dyn DebugEventCallbacksHandler {
self.callbacks.as_ref()
}
impl_callbacks_catch_unwind!();
}
impl From<Rc<dyn DebugEventCallbacksHandler>> for DebugEventCallbacksAdapter {
fn from(value: Rc<dyn DebugEventCallbacksHandler>) -> Self {
Self {
callbacks: value,
poisoned: Cell::new(false),
}
}
}
#[allow(non_snake_case)]
impl IDebugEventCallbacks_Impl for DebugEventCallbacksAdapter_Impl {
fn GetInterestMask(&self) -> windows::core::Result<u32> {
self.__catch_unwind(|| Ok(self.callbacks.get_interest_mask()?.bits()))
}
fn Breakpoint(
&self,
bp: Ref<IDebugBreakpoint>,
) -> windows::core::Result<()> {
let bp = bp.as_ref().ok_or(E_POINTER)?;
let bp = unsafe { DebugBreakpointRef::from_raw(bp.as_raw()) }
.ok_or(E_POINTER)?;
self.__catch_unwind(|| ds2hr(self.callbacks.breakpoint(bp)))
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn Exception(
&self,
exception: *const EXCEPTION_RECORD64,
first_chance: u32,
) -> windows::core::Result<()> {
let exception = unsafe { exception.as_ref().ok_or(E_POINTER)? };
self.__catch_unwind(|| {
ds2hr(self.callbacks.exception(exception, first_chance))
})
}
fn CreateThread(
&self,
handle: u64,
data_offset: u64,
start_offset: u64,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
ds2hr(self.callbacks.create_thread(
handle,
data_offset,
start_offset,
))
})
}
fn ExitThread(&self, exit_code: u32) -> windows::core::Result<()> {
self.__catch_unwind(|| ds2hr(self.callbacks.exit_thread(exit_code)))
}
fn CreateProcessA(
&self,
image_file_handle: u64,
handle: u64,
base_offset: u64,
module_size: u32,
module_name: &PCSTR,
image_name: &PCSTR,
checksum: u32,
time_date_stamp: u32,
initial_thread_handle: u64,
thread_data_offset: u64,
start_offset: u64,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
ds2hr(self.callbacks.create_process(
image_file_handle,
handle,
base_offset,
module_size,
(!module_name.is_null()).then(|| unsafe {
ACPStr::from_raw(module_name.0).to_string()
}),
(!image_name.is_null()).then(|| unsafe {
ACPStr::from_raw(image_name.0).to_string()
}),
checksum,
time_date_stamp,
initial_thread_handle,
thread_data_offset,
start_offset,
))
})
}
fn ExitProcess(&self, exit_code: u32) -> windows::core::Result<()> {
self.__catch_unwind(|| ds2hr(self.callbacks.exit_process(exit_code)))
}
fn LoadModule(
&self,
image_file_handle: u64,
base_offset: u64,
module_size: u32,
module_name: &PCSTR,
image_name: &PCSTR,
checksum: u32,
time_date_stamp: u32,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
ds2hr(self.callbacks.load_module(
image_file_handle,
base_offset,
module_size,
(!module_name.is_null()).then(|| unsafe {
ACPStr::from_raw(module_name.0).to_string()
}),
(!image_name.is_null()).then(|| unsafe {
ACPStr::from_raw(image_name.0).to_string()
}),
checksum,
time_date_stamp,
))
})
}
fn UnloadModule(
&self,
image_base_name: &PCSTR,
base_offset: u64,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
ds2hr(self.callbacks.unload_module(
(!image_base_name.is_null()).then(|| unsafe {
ACPStr::from_raw(image_base_name.0).to_string()
}),
base_offset,
))
})
}
fn SystemError(&self, error: u32, level: u32) -> windows::core::Result<()> {
self.__catch_unwind(|| ds2hr(self.callbacks.system_error(error, level)))
}
fn SessionStatus(&self, status: u32) -> windows::core::Result<()> {
self.__catch_unwind(|| {
self.callbacks
.session_status(DebugSessionStatus::try_from(status).unwrap())
})
}
fn ChangeDebuggeeState(
&self,
flags: u32,
argument: u64,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
self.callbacks
.change_debuggee_state(DebugCdsArg::from_arg(flags, argument))
})
}
fn ChangeEngineState(
&self,
flags: u32,
argument: u64,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
self.callbacks
.change_engine_state(DebugCesArg::from_arg(flags, argument))
})
}
fn ChangeSymbolState(
&self,
flags: u32,
argument: u64,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
self.callbacks
.change_symbol_state(DebugCssArg::from_arg(flags, argument))
})
}
}
impl_debug_interface!(
DebugEventCallbacksWide,
DebugEventCallbacksWideRef,
IDebugEventCallbacksWide
);
impl From<DebugEventCallbacksWide> for IDebugEventCallbacksWide {
fn from(value: DebugEventCallbacksWide) -> Self { value.0 }
}
impl DebugEventCallbacksHandler for DebugEventCallbacksWide {
fn get_interest_mask(&self) -> windows::core::Result<DebugEventFlags> {
unsafe {
Ok(DebugEventFlags::from_bits_retain(self.0.GetInterestMask()?))
}
}
fn breakpoint(&self, bp: DebugBreakpointRef) -> DebugStatus {
unsafe {
DebugStatus::try_from(
vcall!(self, Breakpoint, bp.as_ptr()).0 as u32,
)
.expect("Invalid DebugStatus")
}
}
fn exception(
&self,
exception: &EXCEPTION_RECORD64,
first_chance: u32,
) -> DebugStatus {
unsafe {
DebugStatus::try_from(
vcall!(self, Exception, exception, first_chance).0 as u32,
)
.expect("Invalid DebugStatus")
}
}
fn create_thread(
&self,
handle: DebuggeeOffset,
data_offset: DebuggeeOffset,
start_offset: DebuggeeOffset,
) -> DebugStatus {
unsafe {
DebugStatus::try_from(
vcall!(self, CreateThread, handle, data_offset, start_offset,).0
as u32,
)
.expect("Invalid DebugStatus")
}
}
fn exit_thread(&self, exit_code: u32) -> DebugStatus {
unsafe {
DebugStatus::try_from(vcall!(self, ExitThread, exit_code).0 as u32)
.expect("Invalid DebugStatus")
}
}
fn create_process(
&self,
image_file_handle: DebuggeeOffset,
handle: DebuggeeOffset,
base_offset: DebuggeeOffset,
module_size: u32,
module_name: Option<String>,
image_name: Option<String>,
checksum: u32,
time_date_stamp: u32,
initial_thread_handle: DebuggeeOffset,
thread_data_offset: DebuggeeOffset,
start_offset: DebuggeeOffset,
) -> DebugStatus {
let module_name = module_name.map(|x| x.to_wstring());
let image_name = image_name.map(|x| x.to_wstring());
unsafe {
DebugStatus::try_from(
vcall!(
self,
CreateProcessA,
image_file_handle,
handle,
base_offset,
module_size,
PCWSTR(
module_name
.as_ref()
.map_or(std::ptr::null(), |x| x.as_ptr())
),
PCWSTR(
image_name
.as_ref()
.map_or(std::ptr::null(), |x| x.as_ptr())
),
checksum,
time_date_stamp,
initial_thread_handle,
thread_data_offset,
start_offset,
)
.0 as u32,
)
.expect("Invalid DebugStatus")
}
}
fn exit_process(&self, exit_code: u32) -> DebugStatus {
unsafe {
DebugStatus::try_from(vcall!(self, ExitProcess, exit_code).0 as u32)
.expect("Invalid DebugStatus")
}
}
fn load_module(
&self,
image_file_handle: DebuggeeOffset,
base_offset: DebuggeeOffset,
module_size: u32,
module_name: Option<String>,
image_name: Option<String>,
checksum: u32,
time_date_stamp: u32,
) -> DebugStatus {
let module_name = module_name.map(|x| x.to_wstring());
let image_name = image_name.map(|x| x.to_wstring());
unsafe {
DebugStatus::try_from(
vcall!(
self,
LoadModule,
image_file_handle,
base_offset,
module_size,
PCWSTR(
module_name
.as_ref()
.map_or(std::ptr::null_mut(), |x| x.as_ptr())
),
PCWSTR(
image_name
.as_ref()
.map_or(std::ptr::null_mut(), |x| x.as_ptr())
),
checksum,
time_date_stamp
)
.0 as u32,
)
.expect("Invalid DebugStatus")
}
}
fn unload_module(
&self,
image_base_name: Option<String>,
base_offset: DebuggeeOffset,
) -> DebugStatus {
let image_base_name = image_base_name.map(|x| x.to_wstring());
unsafe {
DebugStatus::try_from(
vcall!(
self,
UnloadModule,
PCWSTR(
image_base_name
.as_ref()
.map_or(std::ptr::null(), |x| x.as_ptr())
),
base_offset
)
.0 as u32,
)
.expect("Invalid DebugStatus")
}
}
fn system_error(&self, error: u32, level: u32) -> DebugStatus {
unsafe {
DebugStatus::try_from(
vcall!(self, SystemError, error, level).0 as u32,
)
.expect("Invalid DebugStatus")
}
}
fn session_status(
&self,
status: DebugSessionStatus,
) -> windows::core::Result<()> {
unsafe { self.0.SessionStatus(status as u32) }
}
fn change_debuggee_state(
&self,
arg: DebugCdsArg,
) -> windows::core::Result<()> {
unsafe {
match arg {
DebugCdsArg::All => {
self.0.ChangeDebuggeeState(arg.as_flags().bits(), 0)
}
DebugCdsArg::Registers(x) => self.0.ChangeDebuggeeState(
arg.as_flags().bits(),
x.map_or(DEBUG_ANY_ID, |x| x.0) as u64,
),
DebugCdsArg::Data(x) => self.0.ChangeDebuggeeState(
arg.as_flags().bits(),
x.bits() as u64,
),
DebugCdsArg::Refresh(x) => self.0.ChangeDebuggeeState(
arg.as_flags().bits(),
x.bits() as u64,
),
DebugCdsArg::Other { flags, argument } => {
self.0.ChangeDebuggeeState(flags.bits(), argument)
}
}
}
}
fn change_engine_state(
&self,
arg: DebugCesArg,
) -> windows::core::Result<()> {
unsafe {
match arg {
DebugCesArg::CurrentThread(x) => self.0.ChangeEngineState(
arg.as_flags().bits(),
x.map_or(DEBUG_ANY_ID as u64, |x| x.0 as u64),
),
DebugCesArg::EffectiveProcessor(x) => {
self.0.ChangeEngineState(arg.as_flags().bits(), x as u64)
}
DebugCesArg::Breakpoints(x) => self.0.ChangeEngineState(
arg.as_flags().bits(),
x.map_or(DEBUG_ANY_ID, |x| x.0) as u64,
),
DebugCesArg::CodeLevel(x) => {
self.0.ChangeEngineState(arg.as_flags().bits(), x as u64)
}
DebugCesArg::ExecutionStatus((status, inside_wait)) => {
let mut argument = status as u64;
if inside_wait {
argument |= DEBUG_STATUS_INSIDE_WAIT;
}
self.0.ChangeEngineState(arg.as_flags().bits(), argument)
}
DebugCesArg::EngineOptions(x) => self
.0
.ChangeEngineState(arg.as_flags().bits(), x.bits() as u64),
DebugCesArg::LogFile(x) => self.0.ChangeEngineState(
arg.as_flags().bits(),
if x { 1 } else { 0 },
),
DebugCesArg::Radix(x) => {
self.0.ChangeEngineState(arg.as_flags().bits(), x)
}
DebugCesArg::EventFilters(x) => self.0.ChangeEngineState(
arg.as_flags().bits(),
x.map_or(DEBUG_ANY_ID, |x| x.0) as u64,
),
DebugCesArg::ProcessOptions(x) => self
.0
.ChangeEngineState(arg.as_flags().bits(), x.bits() as u64),
DebugCesArg::Systems(x) => self.0.ChangeEngineState(
arg.as_flags().bits(),
x.map_or(DEBUG_ANY_ID, |x| x.0) as u64,
),
DebugCesArg::Extensions => {
self.0.ChangeEngineState(arg.as_flags().bits(), 0)
}
DebugCesArg::AssemblyOptions(x) => self
.0
.ChangeEngineState(arg.as_flags().bits(), x.bits() as u64),
DebugCesArg::ExpressionSyntax(x) => {
self.0.ChangeEngineState(arg.as_flags().bits(), x as u64)
}
DebugCesArg::TextReplacements => self.0.ChangeEngineState(
arg.as_flags().bits(),
DEBUG_ANY_ID as u64,
),
DebugCesArg::Other { flags, argument } => {
self.0.ChangeEngineState(flags.bits(), argument)
}
}
}
}
fn change_symbol_state(
&self,
arg: DebugCssArg,
) -> windows::core::Result<()> {
unsafe {
match arg {
DebugCssArg::Loads(x) => {
self.0.ChangeSymbolState(arg.as_flags().bits(), x)
}
DebugCssArg::Unloads(x) => self
.0
.ChangeSymbolState(arg.as_flags().bits(), x.unwrap_or(0)),
DebugCssArg::Scope => {
self.0.ChangeSymbolState(arg.as_flags().bits(), 0)
}
DebugCssArg::Paths => {
self.0.ChangeSymbolState(arg.as_flags().bits(), 0)
}
DebugCssArg::SymbolOptions(x) => self
.0
.ChangeSymbolState(arg.as_flags().bits(), x.bits() as u64),
DebugCssArg::TypeOptions => {
self.0.ChangeSymbolState(arg.as_flags().bits(), 0)
}
DebugCssArg::Other { flags, argument } => {
self.0.ChangeSymbolState(flags.bits(), argument)
}
}
}
}
}
#[implement(IDebugEventCallbacksWide)]
pub struct DebugEventCallbacksWideAdapter {
pub callbacks: Rc<dyn DebugEventCallbacksHandler>,
poisoned: Cell<bool>,
}
impl DebugEventCallbacksWideAdapter {
pub fn new(callbacks: Rc<dyn DebugEventCallbacksHandler>) -> Self {
Self {
callbacks,
poisoned: Cell::new(false),
}
}
pub fn as_handler(&self) -> &dyn DebugEventCallbacksHandler {
self.callbacks.as_ref()
}
pub fn into_callbacks(self) -> DebugEventCallbacksWide {
let interface: IDebugEventCallbacksWide = self.into();
interface.into()
}
impl_callbacks_catch_unwind!();
}
impl From<Rc<dyn DebugEventCallbacksHandler>>
for DebugEventCallbacksWideAdapter
{
fn from(value: Rc<dyn DebugEventCallbacksHandler>) -> Self {
Self {
callbacks: value,
poisoned: Cell::new(false),
}
}
}
#[allow(non_snake_case)]
impl IDebugEventCallbacksWide_Impl for DebugEventCallbacksWideAdapter_Impl {
fn GetInterestMask(&self) -> windows::core::Result<u32> {
self.__catch_unwind(|| Ok(self.callbacks.get_interest_mask()?.bits()))
}
fn Breakpoint(
&self,
bp: Ref<IDebugBreakpoint2>,
) -> windows::core::Result<()> {
let bp = bp.as_ref().ok_or(E_POINTER)?;
let bp = unsafe { DebugBreakpointRef::from_raw(bp.as_raw()) }
.ok_or(E_POINTER)?;
self.__catch_unwind(|| ds2hr(self.callbacks.breakpoint(bp)))
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn Exception(
&self,
exception: *const EXCEPTION_RECORD64,
first_chance: u32,
) -> windows::core::Result<()> {
let exception = unsafe { exception.as_ref().ok_or(E_POINTER)? };
self.__catch_unwind(|| {
ds2hr(self.callbacks.exception(exception, first_chance))
})
}
fn CreateThread(
&self,
handle: u64,
data_offset: u64,
start_offset: u64,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
ds2hr(self.callbacks.create_thread(
handle,
data_offset,
start_offset,
))
})
}
fn ExitThread(&self, exit_code: u32) -> windows::core::Result<()> {
self.__catch_unwind(|| ds2hr(self.callbacks.exit_thread(exit_code)))
}
fn CreateProcessA(
&self,
image_file_handle: u64,
handle: u64,
base_offset: u64,
module_size: u32,
module_name: &PCWSTR,
image_name: &PCWSTR,
checksum: u32,
time_date_stamp: u32,
initial_thread_handle: u64,
thread_data_offset: u64,
start_offset: u64,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
ds2hr(self.callbacks.create_process(
image_file_handle,
handle,
base_offset,
module_size,
(!module_name.is_null()).then(|| unsafe {
WStr::from_raw(module_name.0).to_string()
}),
(!image_name.is_null()).then(|| unsafe {
WStr::from_raw(image_name.0).to_string()
}),
checksum,
time_date_stamp,
initial_thread_handle,
thread_data_offset,
start_offset,
))
})
}
fn ExitProcess(&self, exit_code: u32) -> windows::core::Result<()> {
self.__catch_unwind(|| ds2hr(self.callbacks.exit_process(exit_code)))
}
fn LoadModule(
&self,
image_file_handle: u64,
base_offset: u64,
module_size: u32,
module_name: &PCWSTR,
image_name: &PCWSTR,
checksum: u32,
time_date_stamp: u32,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
ds2hr(self.callbacks.load_module(
image_file_handle,
base_offset,
module_size,
(!module_name.is_null()).then(|| unsafe {
WStr::from_raw(module_name.0).to_string()
}),
(!image_name.is_null()).then(|| unsafe {
WStr::from_raw(image_name.0).to_string()
}),
checksum,
time_date_stamp,
))
})
}
fn UnloadModule(
&self,
image_base_name: &PCWSTR,
base_offset: u64,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
ds2hr(self.callbacks.unload_module(
(!image_base_name.is_null()).then(|| unsafe {
WStr::from_raw(image_base_name.0).to_string()
}),
base_offset,
))
})
}
fn SystemError(&self, error: u32, level: u32) -> windows::core::Result<()> {
self.__catch_unwind(|| ds2hr(self.callbacks.system_error(error, level)))
}
fn SessionStatus(&self, status: u32) -> windows::core::Result<()> {
self.__catch_unwind(|| {
self.callbacks
.session_status(DebugSessionStatus::try_from(status).unwrap())
})
}
fn ChangeDebuggeeState(
&self,
flags: u32,
argument: u64,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
self.callbacks
.change_debuggee_state(DebugCdsArg::from_arg(flags, argument))
})
}
fn ChangeEngineState(
&self,
flags: u32,
argument: u64,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
self.callbacks
.change_engine_state(DebugCesArg::from_arg(flags, argument))
})
}
fn ChangeSymbolState(
&self,
flags: u32,
argument: u64,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
self.callbacks
.change_symbol_state(DebugCssArg::from_arg(flags, argument))
})
}
}