#![cfg(windows)]
use crate::{
data::*,
dbgeng::DebugDataSpaces,
error::{DbgModelError, WdError},
};
use std::{
backtrace::Backtrace,
borrow::Cow,
cell::RefCell,
ffi::c_void,
fmt,
fmt::{Formatter, Write},
hash::{Hash, Hasher},
marker::PhantomData,
mem::MaybeUninit,
num::TryFromIntError,
panic, slice,
str::FromStr,
sync::Once,
};
use windows::{
Win32::{
Foundation::{S_FALSE, S_OK},
System::Diagnostics::Debug::Extensions::*,
},
core::{HRESULT, IUnknown, Interface, imp::E_INVALIDARG},
};
use windy::{
ACPStr, ACPString, AStr, AString, CP_UTF8, ConvertResult, WStr, WString,
traits::{ToAString, ToWString},
};
mod macros;
pub use ::zerocopy;
#[doc(hidden)]
pub use windows as __windows;
pub mod strings {
pub use windy::{ACPStr, ACPString, AStr, AString, WStr, WString};
pub use windy_macros::{acpstr, acpstring, wstr, wstring};
}
use zerocopy::{FromBytes, Immutable, IntoBytes};
pub mod address;
pub mod data;
pub mod dbgeng;
pub mod dbgmodel;
pub mod dml;
pub mod error;
pub mod expr;
pub mod extsfns;
pub mod object;
pub mod ttd;
pub mod util;
pub mod wd;
use crate::{
dbgeng::{
DebugClassFlags, DebugClient, DebugControl, DebugRegisters,
DebugSymbols, DebugSystemObjects,
},
dml::DML_COLOR_SRCPAIR,
error::WdErrorKind,
};
pub use wd::*;
pub type WdResult<T> = Result<T, WdError>;
pub type DbgModelResult<T> = Result<T, DbgModelError>;
static INSTALL_PANIC_HOOK: Once = Once::new();
macro_rules! __catch_unwind {
($e:expr) => {
match std::panic::catch_unwind(|| $e) {
Ok(x) => x,
Err(_) => return $crate::__windows::Win32::Foundation::E_UNEXPECTED,
}
};
(@res $e:expr) => {
std::panic::catch_unwind(|| $e)
.map_err(|_| $crate::__windows::Win32::Foundation::E_UNEXPECTED)
};
}
thread_local! {
static PANIC_DEBUG_CONTROL: RefCell<Option<DebugControl>> = const { RefCell::new(None) };
}
fn init_panic_control(client: &WdClient) {
PANIC_DEBUG_CONTROL.with(|slot| {
if let Ok(control) = DebugControl::from_interface(client.as_interface())
{
*slot.borrow_mut() = Some(control);
}
});
}
#[doc(hidden)]
fn set_panic_hook() {
INSTALL_PANIC_HOOK.call_once(|| {
let prev_hook = panic::take_hook();
panic::set_hook(Box::new(move |info| {
let _ = PANIC_DEBUG_CONTROL.try_with(|slot| {
if let Some(control) = slot.borrow().as_ref() {
let mut out_str = String::with_capacity(8192);
write!(out_str, r#"<col fg="{DML_COLOR_SRCPAIR}">"#)
.unwrap();
if let Some(loc) = info.location() {
writeln!(
out_str,
"panicked at {}:{}:{}",
loc.file(),
loc.line(),
loc.column()
)
.unwrap();
} else {
out_str += "panicked at unknown code\n";
}
if let Some(s) = info.payload_as_str() {
out_str += s;
out_str.push('\n');
}
let bt = Backtrace::force_capture();
write!(out_str, "backtrace:\n {bt}").unwrap();
out_str += "</col>";
let out_str = out_str.to_wstring();
let _ = control.controlled_output_wide(
DebugOutctlFlags::new(
DebugOutctlTarget::ThisClient,
DebugOutctlOptions::Dml,
),
DebugOutputFlags::Error,
out_str,
);
}
});
prev_hook(info);
}));
});
}
#[doc(hidden)]
pub unsafe fn __callback_register_command(
client: *mut c_void,
args: *const u8,
) -> Result<(WdClient, String), HRESULT> {
set_panic_hook();
let interface =
unsafe { IUnknown::from_raw_borrowed(&client) }.ok_or(E_INVALIDARG)?;
let client =
WdClient::from_interface(interface).map_err(|e| e.hresult())?;
let args = if args.is_null() {
String::new()
} else {
unsafe { ACPStr::from_raw(args).to_string() }
};
init_panic_control(&client);
Ok((client, args))
}
pub fn tokenize_args(command_name: &str, args: &str) -> Option<Vec<String>> {
let args = args.trim();
let mut ret = Vec::with_capacity(16);
ret.push(format!("!{}", command_name));
if args.is_empty() {
return Some(ret);
}
let mut s = String::with_capacity(32);
let mut in_dq = false;
let mut before_dq = false;
for ch in args.chars() {
match ch {
'"' => {
if in_dq {
if before_dq {
s.push('"');
before_dq = false;
} else {
before_dq = true;
}
} else {
in_dq = true;
}
}
' ' => {
if in_dq {
if before_dq {
ret.push(s.clone());
s.clear();
in_dq = false;
} else {
s.push(' ');
}
} else if !s.is_empty() {
ret.push(s.clone());
s.clear();
}
before_dq = false;
}
x => {
if before_dq {
return None;
}
s.push(x);
before_dq = false;
}
}
}
if !in_dq && before_dq {
return None;
}
if in_dq && !before_dq {
return None;
}
ret.push(s);
Some(ret)
}
#[test]
fn test_tokenize_args() {
fn vec<const N: usize>(v: [&str; N]) -> Vec<String> {
v.map(str::to_owned).to_vec()
}
assert_eq!(tokenize_args("cmd", r#" "#).unwrap(), vec(["!cmd"]));
assert_eq!(tokenize_args("cmd", r#""""#).unwrap(), vec(["!cmd", ""]));
assert_eq!(tokenize_args("cmd", r#"a"#).unwrap(), vec(["!cmd", "a"]));
assert_eq!(tokenize_args("cmd", r#""a""#).unwrap(), vec(["!cmd", "a"]));
assert_eq!(
tokenize_args("cmd", r#"aaa bbb ccc"#).unwrap(),
vec(["!cmd", "aaa", "bbb", "ccc"])
);
assert_eq!(
tokenize_args("cmd", r#"aaa "bbb" ccc"#).unwrap(),
vec(["!cmd", "aaa", "bbb", "ccc"])
);
assert_eq!(
tokenize_args("cmd", r#"aaa "b""bb" ccc"#).unwrap(),
vec(["!cmd", "aaa", "b\"bb", "ccc"])
);
assert_eq!(
tokenize_args("cmd", r#""aaa" "b""bb" ccc"#).unwrap(),
vec(["!cmd", "aaa", "b\"bb", "ccc"])
);
assert_eq!(tokenize_args("cmd", r#""#).unwrap(), vec(["!cmd"]));
assert_eq!(
tokenize_args("cmd", r#"aaa"#).unwrap(),
vec(["!cmd", "aaa"])
);
assert_eq!(
tokenize_args("cmd", r#" a "" b "#).unwrap(),
vec(["!cmd", "a", "", "b"])
);
assert!(tokenize_args("cmd", r#"""#).is_none());
assert!(tokenize_args("cmd", r#"aaa "bb"b" ccc"#).is_none());
}
#[macro_export]
macro_rules! register_command {
($command_name:ident, $func_name:path$(,)?) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
unsafe extern "system" fn $command_name(
Client: *mut ::core::ffi::c_void,
Args: *const u8,
) -> $crate::__windows::core::HRESULT {
let (client, args) = unsafe {
match $crate::__callback_register_command(Client, Args) {
Ok(x) => x,
Err(e) => return e,
}
};
$crate::__inner_register_command!(@handle $command_name, client, $func_name(&client, args))
}
};
($command_name:ident, $func_name:path, $args_type:ty$(,)?) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
unsafe extern "system" fn $command_name(
Client: *mut ::core::ffi::c_void,
Args: *const u8,
) -> $crate::__windows::core::HRESULT {
let (client, args) = unsafe {
match $crate::__callback_register_command(Client, Args) {
Ok(x) => x,
Err(e) => return e,
}
};
let tokens = $crate::__inner_register_command!(@tokenize $command_name, client, $crate::tokenize_args(stringify!($command_name), &args));
let args: $args_type = $crate::__inner_register_command!(@clap client, tokens);
$crate::__inner_register_command!(@handle $command_name, client, $func_name(&client, args))
}
};
(
$command_name:ident,
$func_name:path,
$args_type:ty,
$tokenize_func:path$(,)?
) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
unsafe extern "system" fn $command_name(
Client: *mut ::core::ffi::c_void,
Args: *const u8,
) -> $crate::__windows::core::HRESULT {
let (client, args) = unsafe {
match $crate::__callback_register_command(Client, Args) {
Ok(x) => x,
Err(e) => return e,
}
};
let tokens = $crate::__inner_register_command!(@tokenize $command_name, client, $tokenize_func(stringify!($command_name), &args));
let args: $args_type = $crate::__inner_register_command!(@clap client, tokens);
$crate::__inner_register_command!(@handle $command_name, client, $func_name(&client, args))
}
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __inner_register_command {
(@tokenize $command_name:ident, $client:expr, $e:expr$(,)?) => {
match $e {
Some(tokens) => tokens,
None => {
let control = match $crate::WdControl::from_interface(
$client.as_interface(),
) {
Ok(x) => x,
Err(e) => return e.hresult(),
};
if let Err(e) = control.out(format!(
"!{} returns error: Failed to tokenize args.\n",
stringify!($command_name)
)) {
return e.hresult();
}
return $crate::__windows::Win32::Foundation::E_INVALIDARG;
}
}
};
(@clap $client:expr, $tokens: expr$(,)?) => {
match clap::Parser::try_parse_from($tokens) {
Ok(args) => args,
Err(e) => {
let control = match $crate::WdControl::from_interface(
$client.as_interface(),
) {
Ok(x) => x,
Err(e) => return e.hresult(),
};
if let Err(e) = control.out(format!("{e}\n")) {
return e.hresult();
}
return $crate::__windows::Win32::Foundation::E_INVALIDARG;
}
}
};
(@handle $command_name:ident, $client:expr, $e:expr$(,)?) => {{
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { $e })) {
Ok(Ok(_)) => $crate::__windows::Win32::Foundation::S_OK,
Ok(Err(e)) => {
let control = match $crate::WdControl::from_interface(
$client.as_interface(),
) {
Ok(x) => x,
Err(e) => return e.hresult(),
};
if let Err(e) = control.out(format!(
"!{} returns error: {e}\n",
stringify!($command_name)
)) {
return e.hresult();
}
e.hresult()
}
Err(_) => $crate::__windows::Win32::Foundation::E_UNEXPECTED,
}
}};
}
pub fn debug_extension_version(major_ver: u16, minor_ver: u16) -> u32 {
(major_ver as u32) << 16 | (minor_ver as u32)
}
bitflags::bitflags! {
pub struct ExtInitFlags: u32 {
const Default = 0;
const HasCommandHelp = DEBUG_EXTINIT_HAS_COMMAND_HELP;
}
}
#[doc(hidden)]
#[inline(always)]
pub fn __callback_debug_extension_can_unload(
f: impl FnOnce() -> WdResult<bool> + panic::UnwindSafe,
) -> Result<bool, HRESULT> {
set_panic_hook();
Ok(__catch_unwind!(@res f())??)
}
#[macro_export]
macro_rules! debug_extension_can_unload {
($func_name:path $(,)?) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
extern "system" fn DebugExtensionCanUnload()
-> $crate::__windows::core::HRESULT {
match $crate::__callback_debug_extension_can_unload($func_name) {
Ok(true) => $crate::__windows::Win32::Foundation::S_OK,
Ok(false) => $crate::__windows::Win32::Foundation::S_FALSE,
Err(e) => e,
}
}
};
}
#[doc(hidden)]
#[inline(always)]
pub fn __callback_debug_extension_unload(f: impl FnOnce() + panic::UnwindSafe) {
set_panic_hook();
let _ = __catch_unwind!(@res f());
}
#[macro_export]
macro_rules! debug_extension_unload {
($func_name:path $(,)?) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
extern "system" fn DebugExtensionUnload() {
$crate::__callback_debug_extension_unload($func_name);
}
};
}
#[doc(hidden)]
#[inline(always)]
pub fn __callback_debug_extension_uninitialize(
f: impl FnOnce() + panic::UnwindSafe,
) {
set_panic_hook();
let _ = __catch_unwind!(@res f());
}
#[macro_export]
macro_rules! debug_extension_uninitialize {
($func_name:path $(,)?) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
extern "system" fn DebugExtensionUninitialize() {
$crate::__callback_debug_extension_uninitialize($func_name);
}
};
}
enum_flags! {
pub enum NotifyFlags: u32 {
Active = DEBUG_NOTIFY_SESSION_ACTIVE,
Inactive = DEBUG_NOTIFY_SESSION_INACTIVE,
Accessible = DEBUG_NOTIFY_SESSION_ACCESSIBLE,
Inaccessible = DEBUG_NOTIFY_SESSION_INACCESSIBLE,
}
}
#[doc(hidden)]
#[inline(always)]
pub fn __callback_debug_extension_notify(
notify: u32,
argument: u64,
f: impl FnOnce(NotifyFlags, u64) + panic::UnwindSafe,
) {
set_panic_hook();
let _ = __catch_unwind!(@res {
let notify = NotifyFlags::try_from(notify)
.unwrap_or_else(|e| panic!("Invalid notify flag: {e}"));
f(notify, argument)
});
}
#[macro_export]
macro_rules! debug_extension_notify {
($notify_func:path $(,)?) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
extern "system" fn DebugExtensionNotify(notify: u32, argument: u64) {
$crate::__callback_debug_extension_notify(
notify,
argument,
$notify_func,
);
}
};
}
#[doc(hidden)]
#[inline(always)]
pub unsafe fn __callback_debug_extension_initialize(
version: *mut u32,
flags: *mut u32,
major_ver: u16,
minor_ver: u16,
new_flags: ExtInitFlags,
f: impl FnOnce(&mut u32, &mut u32) -> WdResult<()> + panic::UnwindSafe,
) -> Result<(), HRESULT> {
set_panic_hook();
if version.is_null() || flags.is_null() {
return Err(E_INVALIDARG);
}
unsafe {
*version = debug_extension_version(major_ver, minor_ver);
*flags = new_flags.bits();
Ok(__catch_unwind!(@res f(&mut *version, &mut *flags))??)
}
}
#[macro_export]
macro_rules! debug_extension_initialize {
($major_ver:expr, $minor_ver:expr $(,)?) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
unsafe extern "system" fn DebugExtensionInitialize(
Version: *mut u32,
Flags: *mut u32,
) -> $crate::__windows::core::HRESULT {
match $crate::__callback_debug_extension_initialize(
Version,
Flags,
$major_ver,
$minor_ver,
$crate::ExtInitFlags::Default,
|_, _| Ok(()),
) {
Ok(()) => $crate::__windows::Win32::Foundation::S_OK,
Err(e) => e,
}
}
};
($major_ver:expr, $minor_ver:expr, $flags:expr $(,)?) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
unsafe extern "system" fn DebugExtensionInitialize(
Version: *mut u32,
Flags: *mut u32,
) -> $crate::__windows::core::HRESULT {
match $crate::__callback_debug_extension_initialize(
Version,
Flags,
$major_ver,
$minor_ver,
$flags,
|_, _| Ok(()),
) {
Ok(()) => $crate::__windows::Win32::Foundation::S_OK,
Err(e) => e,
}
}
};
($major_ver:expr, $minor_ver:expr, $flags:expr, $func:path $(,)?) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
unsafe extern "system" fn DebugExtensionInitialize(
Version: *mut u32,
Flags: *mut u32,
) -> $crate::__windows::core::HRESULT {
match $crate::__callback_debug_extension_initialize(
Version, Flags, $major_ver, $minor_ver, $flags, $func,
) {
Ok(()) => $crate::__windows::Win32::Foundation::S_OK,
Err(e) => e,
}
}
};
}
#[doc(hidden)]
#[inline(always)]
pub unsafe fn __callback_efn_analyze(
client: *mut c_void,
call_phase: u32,
analysis: *mut c_void,
f: impl FnOnce(
&WdClient,
FaExtensionPluginPhase,
&WdFailureAnalysis,
) -> WdResult<bool>
+ panic::UnwindSafe,
) -> Result<bool, HRESULT> {
set_panic_hook();
let client =
unsafe { IUnknown::from_raw_borrowed(&client) }.ok_or(E_INVALIDARG)?;
let analysis = unsafe { IUnknown::from_raw_borrowed(&analysis) }
.ok_or(E_INVALIDARG)?;
let client = WdClient::from_interface(client)?;
let analysis = WdFailureAnalysis::from_interface(analysis)?;
let call_phase = FaExtensionPluginPhase::from_bits_truncate(call_phase);
init_panic_control(&client);
Ok(__catch_unwind!(@res f(&client, call_phase, &analysis))??)
}
#[macro_export]
macro_rules! efn_analyze {
($func_name:path $(,)?) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
unsafe extern "system" fn _EFN_Analyze(
Client: *mut std::ffi::c_void,
CallPhase: u32,
pAnalysis: *mut std::ffi::c_void,
) -> $crate::__windows::core::HRESULT {
unsafe {
match $crate::__callback_efn_analyze(
Client, CallPhase, pAnalysis, $func_name,
) {
Ok(true) => $crate::__windows::Win32::Foundation::S_OK,
Ok(false) => $crate::__windows::Win32::Foundation::S_FALSE,
Err(e) => e,
}
}
}
};
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct FaExtensionPluginPhase: u32 {
const Initialization = 1;
const StackAnalysis = 2;
const PreBucketing = 4;
const PostBucketing = 8;
}
}
enum_flags! {
pub enum KnownStructFlags: u32 {
GetNames = DEBUG_KNOWN_STRUCT_GET_NAMES,
GetSingleLineOutput = DEBUG_KNOWN_STRUCT_GET_SINGLE_LINE_OUTPUT,
SuppressTypeName = DEBUG_KNOWN_STRUCT_SUPPRESS_TYPE_NAME,
}
}
pub(crate) fn utf8s_to_acp_array(
strings: &[String],
buf: &mut [u8],
required_size: &mut u32,
) -> WdResult<bool> {
if strings.is_empty() {
*required_size = 2;
if buf.len() < 2 {
return Ok(false);
}
buf[..2].fill(0);
return Ok(true);
}
let mut astrings = Vec::with_capacity(strings.len());
let mut bytes_len = 1;
for string in strings {
let string = ACPString::from_utf8_lossy(string);
bytes_len += string.as_bytes_with_nul().len();
astrings.push(string);
}
*required_size = bytes_len.try_into()?;
if bytes_len > buf.len() {
return Ok(false);
}
let mut cur = buf;
for name in &astrings {
let bytes = name.as_bytes_with_nul();
cur[..bytes.len()].copy_from_slice(bytes);
cur = &mut cur[bytes.len()..];
}
cur[0] = b'\0';
Ok(true)
}
pub(crate) fn utf8s_to_wide_array(
strings: &[String],
buf: &mut [u16],
required_size: &mut u32,
) -> WdResult<bool> {
if strings.is_empty() {
*required_size = 2;
if buf.len() < 2 {
return Ok(false);
}
buf[..2].fill(0);
return Ok(true);
}
let mut wstrings = Vec::with_capacity(strings.len());
let mut bytes_len = 1;
for string in strings {
let string = WString::from_utf8_lossy(string);
bytes_len += string.as_bytes_with_nul().len();
wstrings.push(string);
}
*required_size = bytes_len.try_into()?;
if bytes_len > buf.len() {
return Ok(false);
}
let mut cur = buf;
for name in &wstrings {
let bytes = name.as_bytes_with_nul();
cur[..bytes.len()].copy_from_slice(bytes);
cur = &mut cur[bytes.len()..];
}
cur[0] = 0;
Ok(true)
}
#[test]
fn test_utf8s_to_acp() {
let mut required_size = 0;
assert!(!utf8s_to_acp_array(&[], &mut [0; 1], &mut required_size).unwrap());
assert_eq!(required_size, 2);
let mut buf = [2; 2];
required_size = 0;
assert!(utf8s_to_acp_array(&[], &mut buf, &mut required_size).unwrap());
assert_eq!(required_size, 2);
assert_eq!(buf, [0, 0]);
assert!(
!utf8s_to_acp_array(
&["aa".to_string(), "bbb".to_string()],
&mut buf,
&mut required_size
)
.unwrap()
);
assert_eq!(required_size, 8);
required_size = 0;
let mut buf = [0; 8];
assert!(
utf8s_to_acp_array(
&["aa".to_string(), "bbb".to_string()],
&mut buf,
&mut required_size
)
.unwrap()
);
assert_eq!(required_size, 8);
assert_eq!(&buf, b"aa\0bbb\0\0");
}
#[inline(always)]
fn handle_post_known_struct_get_names(
buf: &mut [u8],
buffer_chars: &mut u32,
names: Vec<String>,
) -> WdResult<bool> {
utf8s_to_acp_array(&names, buf, buffer_chars)
}
#[inline(always)]
fn handle_post_known_struct_get_single_line_output(
buf: &mut [u8],
buffer_chars: &mut u32,
representation: String,
) -> WdResult<bool> {
let s = ACPString::from_utf8_lossy(&representation);
let s_len = s.as_bytes_with_nul().len();
*buffer_chars = s_len.try_into()?;
if s_len > buf.len() {
return Ok(false);
}
let b = s.as_bytes_with_nul();
buf[..b.len()].copy_from_slice(s.as_bytes_with_nul());
Ok(true)
}
#[doc(hidden)]
#[inline(always)]
#[allow(clippy::too_many_arguments)]
pub unsafe fn __callback_known_struct_output(
flags: u32,
offset: u64,
type_name: *const u8,
buffer: *mut u8,
buffer_chars: *mut u32,
get_names_func: impl FnOnce() -> WdResult<Vec<String>> + panic::UnwindSafe,
suppress_type_name_func: impl FnOnce(String) -> WdResult<bool>
+ panic::UnwindSafe,
get_single_line_output_func: impl FnOnce(
DebuggeeOffset,
String,
) -> WdResult<String>
+ panic::UnwindSafe,
) -> Result<bool, HRESULT> {
set_panic_hook();
let flags = KnownStructFlags::try_from(flags)?;
match flags {
KnownStructFlags::GetNames => {
if buffer.is_null() || buffer_chars.is_null() {
return Err(E_INVALIDARG);
}
unsafe {
let buf =
slice::from_raw_parts_mut(buffer, *buffer_chars as usize);
Ok(handle_post_known_struct_get_names(
buf,
&mut *buffer_chars,
__catch_unwind!(@res get_names_func())??,
)?)
}
}
KnownStructFlags::SuppressTypeName => {
if type_name.is_null() {
return Err(E_INVALIDARG);
}
let type_name = unsafe { ACPStr::from_raw(type_name).to_string() };
Ok(__catch_unwind!(
@res suppress_type_name_func(type_name)
)??)
}
KnownStructFlags::GetSingleLineOutput => {
if buffer.is_null() || type_name.is_null() || buffer_chars.is_null()
{
return Err(E_INVALIDARG);
}
unsafe {
let type_name = ACPStr::from_raw(type_name).to_string();
let representation = __catch_unwind!(@res get_single_line_output_func(
offset, type_name
))??;
let buf =
slice::from_raw_parts_mut(buffer, *buffer_chars as usize);
Ok(handle_post_known_struct_get_single_line_output(
buf,
&mut *buffer_chars,
representation,
)?)
}
}
}
}
#[macro_export]
macro_rules! known_struct_output {
(
$get_names_func:path,
$suppress_type_name_func:path,
$get_single_line_output_func:path $(,)?
) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
unsafe extern "system" fn KnownStructOutput(
Flags: u32,
Offset: u64,
TypeName: *const u8,
Buffer: *mut u8,
BufferChars: *mut u32,
) -> $crate::__windows::core::HRESULT {
unsafe {
match $crate::__callback_known_struct_output(
Flags,
Offset,
TypeName,
Buffer,
BufferChars,
$get_names_func,
$suppress_type_name_func,
$get_single_line_output_func,
) {
Ok(true) => $crate::__windows::Win32::Foundation::S_OK,
Ok(false) => $crate::__windows::Win32::Foundation::S_FALSE,
Err(e) => e,
}
}
}
};
}
#[doc(hidden)]
#[inline(always)]
#[allow(clippy::too_many_arguments)]
pub unsafe fn __callback_known_struct_output_ex(
client: *mut c_void,
flags: u32,
offset: DebuggeeOffset,
type_name: *const u8,
buffer: *mut u8,
buffer_chars: *mut u32,
get_names_func: impl FnOnce(&WdClient) -> WdResult<Vec<String>>
+ panic::UnwindSafe,
suppress_type_name_func: impl FnOnce(&WdClient, String) -> WdResult<bool>
+ panic::UnwindSafe,
get_single_line_output_func: impl FnOnce(
&WdClient,
DebuggeeOffset,
String,
) -> WdResult<String>
+ panic::UnwindSafe,
) -> Result<bool, HRESULT> {
set_panic_hook();
let interface =
unsafe { IUnknown::from_raw_borrowed(&client) }.ok_or(E_INVALIDARG)?;
let client =
WdClient::from_interface(interface).map_err(|e| e.hresult())?;
let flags = KnownStructFlags::try_from(flags)?;
init_panic_control(&client);
match flags {
KnownStructFlags::GetNames => {
if buffer.is_null() || buffer_chars.is_null() {
return Err(E_INVALIDARG);
}
unsafe {
let buf =
slice::from_raw_parts_mut(buffer, *buffer_chars as usize);
Ok(handle_post_known_struct_get_names(
buf,
&mut *buffer_chars,
__catch_unwind!(@res get_names_func(&client))??,
)?)
}
}
KnownStructFlags::SuppressTypeName => {
if type_name.is_null() {
return Err(E_INVALIDARG);
}
let type_name = unsafe { ACPStr::from_raw(type_name).to_string() };
Ok(__catch_unwind!(
@res suppress_type_name_func(&client, type_name)
)??)
}
KnownStructFlags::GetSingleLineOutput => {
if buffer.is_null() || type_name.is_null() || buffer_chars.is_null()
{
return Err(E_INVALIDARG);
}
unsafe {
let type_name = ACPStr::from_raw(type_name).to_string();
let representation = __catch_unwind!(@res get_single_line_output_func(
&client, offset, type_name
))??;
let buf =
slice::from_raw_parts_mut(buffer, *buffer_chars as usize);
Ok(handle_post_known_struct_get_single_line_output(
buf,
&mut *buffer_chars,
representation,
)?)
}
}
}
}
#[macro_export]
macro_rules! known_struct_output_ex {
(
$get_names_func:path,
$suppress_type_name_func:path,
$get_single_line_output_func:path $(,)?
) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
unsafe extern "system" fn KnownStructOutputEx(
Client: *mut ::core::ffi::c_void,
Flags: u32,
Offset: u64,
TypeName: *const u8,
Buffer: *mut u8,
BufferChars: *mut u32,
) -> $crate::__windows::core::HRESULT {
unsafe {
match $crate::__callback_known_struct_output_ex(
Client,
Flags,
Offset,
TypeName,
Buffer,
BufferChars,
$get_names_func,
$suppress_type_name_func,
$get_single_line_output_func,
) {
Ok(true) => $crate::__windows::Win32::Foundation::S_OK,
Ok(false) => $crate::__windows::Win32::Foundation::S_FALSE,
Err(e) => e,
}
}
}
};
}
#[doc(hidden)]
pub unsafe fn __callback_debug_extension_query_value_names(
client: *mut c_void,
flags: u32,
buffer: *mut u16,
buffer_chars: u32,
buffer_needed: *mut u32,
f: impl FnOnce(&WdClient, u32) -> WdResult<Vec<String>> + panic::UnwindSafe,
) -> Result<bool, HRESULT> {
set_panic_hook();
let interface =
unsafe { IUnknown::from_raw_borrowed(&client) }.ok_or(E_INVALIDARG)?;
let client =
WdClient::from_interface(interface).map_err(|e| e.hresult())?;
init_panic_control(&client);
if buffer.is_null() || buffer_needed.is_null() {
return Err(E_INVALIDARG);
}
unsafe {
let names = __catch_unwind!(@res f(&client,flags))??;
let buf = slice::from_raw_parts_mut(buffer, buffer_chars as usize);
Ok(utf8s_to_wide_array(&names, buf, &mut *buffer_needed)?)
}
}
#[macro_export]
macro_rules! debug_extension_query_value_names {
($func:path $(,)?) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
unsafe extern "system" fn DebugExtensionQueryValueNames(
Client: *mut ::core::ffi::c_void,
Flags: u32,
Buffer: *mut u16,
BufferChars: u32,
BufferNeeded: *mut u32,
) -> $crate::__windows::core::HRESULT {
unsafe {
match $crate::__callback_debug_extension_query_value_names(
Client,
Flags,
Buffer,
BufferChars,
BufferNeeded,
$func,
) {
Ok(true) => $crate::__windows::Win32::Foundation::S_OK,
Ok(false) => $crate::__windows::Win32::Foundation::S_FALSE,
Err(e) => e,
}
}
}
};
}
#[derive(Debug, Clone, Copy)]
pub enum ProvidedValue {
Value { value: u64 },
Pointer { value: u64, type_id: SymbolTypeId },
}
impl ProvidedValue {
pub fn is_value(&self) -> bool {
matches!(self, &ProvidedValue::Value { .. })
}
pub fn is_pointer(&self) -> bool {
matches!(self, &ProvidedValue::Pointer { .. })
}
pub fn value(&self) -> u64 {
match self {
ProvidedValue::Value { value } => *value,
ProvidedValue::Pointer { value, .. } => *value,
}
}
pub fn type_id(&self) -> Option<SymbolTypeId> {
match self {
ProvidedValue::Value { .. } => None,
ProvidedValue::Pointer { type_id, .. } => Some(*type_id),
}
}
}
#[doc(hidden)]
#[allow(clippy::too_many_arguments)]
pub unsafe fn __callback_debug_extension_provide_value(
client: *mut c_void,
flags: u32,
name: *const u16,
out_value: *mut u64,
out_type_mod_base: *mut u64,
out_type_id: *mut u32,
out_type_flags: *mut u32,
f: impl FnOnce(&WdClient, u32, String) -> WdResult<ProvidedValue>
+ panic::UnwindSafe,
) -> Result<(), HRESULT> {
set_panic_hook();
if name.is_null()
|| out_value.is_null()
|| out_type_mod_base.is_null()
|| out_type_id.is_null()
|| out_type_flags.is_null()
{
return Err(E_INVALIDARG);
}
let interface =
unsafe { IUnknown::from_raw_borrowed(&client) }.ok_or(E_INVALIDARG)?;
let client =
WdClient::from_interface(interface).map_err(|e| e.hresult())?;
let name = unsafe { WStr::from_raw(name).to_string() };
init_panic_control(&client);
unsafe {
match __catch_unwind!(@res f(&client, flags, name))?? {
ProvidedValue::Value { value } => {
*out_value = value;
*out_type_flags = DebugExtPVTypeFlags::Value as u32;
}
ProvidedValue::Pointer { value, type_id } => {
*out_value = value;
*out_type_mod_base = type_id.module;
*out_type_id = type_id.id;
*out_type_flags = DebugExtPVTypeFlags::Pointer as u32;
}
}
Ok(())
}
}
#[macro_export]
macro_rules! debug_extension_provide_value {
($func:path $(,)?) => {
#[doc(hidden)]
#[unsafe(no_mangle)]
unsafe extern "system" fn DebugExtensionProvideValue(
Client: *mut ::core::ffi::c_void,
Flags: u32,
Name: *const u16,
Value: *mut u64,
TypeModBase: *mut u64,
TypeId: *mut u32,
TypeFlags: *mut u32,
) -> $crate::__windows::core::HRESULT {
unsafe {
match $crate::__callback_debug_extension_provide_value(
Client,
Flags,
Name,
Value,
TypeModBase,
TypeId,
TypeFlags,
$func,
) {
Ok(()) => $crate::__windows::Win32::Foundation::S_OK,
Err(e) => e,
}
}
}
};
}
pub type DebuggeeOffset = u64;
#[repr(C)]
#[derive(
Default,
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
Hash,
FromBytes,
IntoBytes,
Immutable,
)]
pub struct VoidType;
pub trait TPtr<T>:
Default
+ Copy
+ Clone
+ Eq
+ PartialEq
+ PartialEq<DebuggeeOffset>
+ Hash
+ fmt::Debug
+ fmt::Display
+ fmt::LowerHex
+ fmt::UpperHex
+ fmt::Pointer
+ From<DebuggeeOffset>
+ Into<DebuggeeOffset>
+ FromBytes
+ Immutable
{
fn offset(self) -> DebuggeeOffset;
fn set_offset(&mut self, offset: DebuggeeOffset);
fn try_set_offset(
&mut self,
offset: DebuggeeOffset,
) -> Result<(), TryFromIntError>;
fn pointer_width() -> PointerWidth;
fn pointer_size() -> u64 { Self::pointer_width().size() }
fn is_32bit() -> bool { Self::pointer_width() == PointerWidth::Ptr32 }
fn is_64bit() -> bool { Self::pointer_width() == PointerWidth::Ptr64 }
fn is_null(self) -> bool { self.offset() == 0 }
fn try_from_offset(offset: DebuggeeOffset)
-> Result<Self, TryFromIntError>;
fn cast<P: TPtr<U>, U>(self) -> P { self.offset().into() }
fn read(&self, ds: impl AsRef<WdDataSpaces>, memory: &Memory) -> WdResult<T>
where
T: FromBytes,
{
ds.as_ref().read_object(memory, self.offset())
}
fn read_tobject(
&self,
ds: impl AsRef<WdDataSpaces>,
memory: &Memory,
) -> WdResult<TObject<Self, T>>
where
T: FromBytes,
{
Ok(TObject::new(*self, self.read(ds, memory)?))
}
fn read_objects(
&self,
ds: impl AsRef<WdDataSpaces>,
memory: &Memory,
size: usize,
) -> WdResult<Vec<T>>
where
T: FromBytes,
{
ds.as_ref().read_objects(memory, self.offset(), size)
}
fn write(
&self,
ds: impl AsRef<WdDataSpaces>,
memory: &Memory,
value: &T,
) -> WdResult<()>
where
T: IntoBytes + Immutable,
{
ds.as_ref().write_object(memory, self.offset(), value)
}
fn write_objects(
&self,
ds: impl AsRef<WdDataSpaces>,
memory: &Memory,
values: &[T],
) -> WdResult<usize>
where
T: IntoBytes + Immutable,
{
let ds = ds.as_ref();
memory.write_objects(ds, self.offset(), values)
}
}
#[repr(transparent)]
#[derive(FromBytes, IntoBytes, Immutable)]
pub struct TPtr64<T>(pub u64, PhantomData<fn() -> T>);
impl<T> Default for TPtr64<T> {
fn default() -> Self { Self(u64::default(), PhantomData) }
}
impl<T> Clone for TPtr64<T> {
fn clone(&self) -> Self { *self }
}
impl<T> Copy for TPtr64<T> {}
impl<T> Eq for TPtr64<T> {}
impl<T> PartialEq for TPtr64<T> {
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
impl<T> PartialEq<DebuggeeOffset> for TPtr64<T> {
fn eq(&self, other: &DebuggeeOffset) -> bool { self.0 == *other }
}
impl<T> Hash for TPtr64<T> {
fn hash<H: Hasher>(&self, state: &mut H) { state.write_u64(self.0); }
}
impl<T> fmt::Debug for TPtr64<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl<T> fmt::Display for TPtr64<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl<T> fmt::LowerHex for TPtr64<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(&self.0, f)
}
}
impl<T> fmt::UpperHex for TPtr64<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::UpperHex::fmt(&self.0, f)
}
}
impl<T> fmt::Pointer for TPtr64<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let digits = format!("{:016x}", self.0);
f.pad_integral(true, "0x", &digits)
}
}
impl<T> TPtr<T> for TPtr64<T> {
fn offset(self) -> DebuggeeOffset { self.0 }
fn set_offset(&mut self, offset: DebuggeeOffset) { self.0 = offset; }
fn try_set_offset(
&mut self,
offset: DebuggeeOffset,
) -> Result<(), TryFromIntError> {
self.0 = offset;
Ok(())
}
fn pointer_width() -> PointerWidth { PointerWidth::Ptr64 }
fn try_from_offset(
offset: DebuggeeOffset,
) -> Result<Self, TryFromIntError> {
Ok(Self(offset, PhantomData))
}
}
impl<T> From<DebuggeeOffset> for TPtr64<T> {
fn from(value: DebuggeeOffset) -> Self { Self(value, PhantomData) }
}
impl<T> From<TPtr64<T>> for u64 {
fn from(value: TPtr64<T>) -> Self { value.offset() }
}
#[repr(transparent)]
#[derive(FromBytes, IntoBytes, Immutable)]
pub struct TPtr32<T>(pub u32, PhantomData<fn() -> T>);
impl<T> Default for TPtr32<T> {
fn default() -> Self { Self(u32::default(), PhantomData) }
}
impl<T> Clone for TPtr32<T> {
fn clone(&self) -> Self { *self }
}
impl<T> Copy for TPtr32<T> {}
impl<T> Eq for TPtr32<T> {}
impl<T> PartialEq for TPtr32<T> {
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
impl<T> PartialEq<DebuggeeOffset> for TPtr32<T> {
fn eq(&self, other: &DebuggeeOffset) -> bool { self.0 as u64 == *other }
}
impl<T> Hash for TPtr32<T> {
fn hash<H: Hasher>(&self, state: &mut H) { state.write_u32(self.0); }
}
impl<T> fmt::Debug for TPtr32<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl<T> fmt::Display for TPtr32<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl<T> fmt::LowerHex for TPtr32<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(&self.0, f)
}
}
impl<T> fmt::UpperHex for TPtr32<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::UpperHex::fmt(&self.0, f)
}
}
impl<T> fmt::Pointer for TPtr32<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let digits = format!("{:08x}", self.0);
f.pad_integral(true, "0x", &digits)
}
}
impl<T> TPtr<T> for TPtr32<T> {
fn offset(self) -> DebuggeeOffset { self.0 as DebuggeeOffset }
fn set_offset(&mut self, offset: DebuggeeOffset) { self.0 = offset as u32; }
fn try_set_offset(
&mut self,
offset: DebuggeeOffset,
) -> Result<(), TryFromIntError> {
self.0 = u32::try_from(offset)?;
Ok(())
}
fn pointer_width() -> PointerWidth { PointerWidth::Ptr32 }
fn try_from_offset(
offset: DebuggeeOffset,
) -> Result<Self, TryFromIntError> {
Ok(Self(u32::try_from(offset)?, PhantomData))
}
}
impl<T> From<DebuggeeOffset> for TPtr32<T> {
fn from(value: DebuggeeOffset) -> Self { Self(value as u32, PhantomData) }
}
impl<T> From<u32> for TPtr32<T> {
fn from(value: u32) -> Self { Self(value, PhantomData) }
}
impl<T> From<TPtr32<T>> for u64 {
fn from(value: TPtr32<T>) -> Self { value.offset() }
}
pub struct TObject<P: TPtr<T>, T> {
ptr: P,
object: T,
}
impl<P: TPtr<T>, T> TObject<P, T> {
#[inline]
pub fn new(ptr: P, object: T) -> Self { Self { ptr, object } }
pub fn read_pointer(
ptr: P,
ds: impl AsRef<WdDataSpaces>,
memory: &Memory,
) -> WdResult<Self>
where
T: FromBytes,
{
let object = ptr.read(ds, memory)?;
Ok(Self::new(ptr, object))
}
pub fn cast<P1: TPtr<T1>, T1>(&self, object: T1) -> TObject<P1, T1> {
TObject::<P1, T1>::new(self.ptr.cast(), object)
}
pub fn ptr(&self) -> P { self.ptr }
pub fn object(&self) -> &T { &self.object }
pub fn object_mut(&mut self) -> &mut T { &mut self.object }
pub fn write_object(
&self,
ds: impl AsRef<WdDataSpaces>,
memory: &Memory,
) -> WdResult<()>
where
T: IntoBytes + Immutable,
{
self.ptr().write(ds, memory, self.object())
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum PointerWidth {
Ptr32,
Ptr64,
}
impl PointerWidth {
#[inline]
pub fn size(self) -> u64 {
match self {
Self::Ptr32 => 4,
Self::Ptr64 => 8,
}
}
#[inline]
pub fn is_64bit(self) -> bool { self == Self::Ptr64 }
#[inline]
pub fn is_32bit(self) -> bool { self == Self::Ptr32 }
}
pub struct TargetInformation {
pointer_width: PointerWidth,
actual_processor_type: ProcessorType,
effective_processor_type: ProcessorType,
debuggee_type: DebuggeeType,
page_size: u64,
}
impl TargetInformation {
pub fn new(control: impl AsRef<WdControl>) -> WdResult<Self> {
let control = control.as_ref();
let pointer_width = if control.is_pointer_64bit()? {
PointerWidth::Ptr64
} else {
PointerWidth::Ptr32
};
let actual_processor_type =
control.get_processor_type(TargetTypeSelector::Actual)?;
let effective_processor_type =
control.get_processor_type(TargetTypeSelector::Effective)?;
let debuggee_type = control.get_debuggee_type()?;
let page_size = control.get_page_size()?;
Ok(Self {
pointer_width,
actual_processor_type,
effective_processor_type,
debuggee_type,
page_size,
})
}
#[inline]
pub fn pointer_width(&self) -> PointerWidth { self.pointer_width }
#[inline]
pub fn actual_processor_type(&self) -> ProcessorType {
self.actual_processor_type
}
#[inline]
pub fn effective_processor_type(&self) -> ProcessorType {
self.effective_processor_type
}
#[inline]
pub fn debuggee_type(&self) -> DebuggeeType { self.debuggee_type }
#[inline]
pub fn debug_class_flags(&self) -> DebugClassFlags {
self.debuggee_type.class()
}
#[inline]
pub fn debug_kernel_flags(&self) -> Option<DebugKernelFlags> {
match self.debuggee_type {
DebuggeeType::Kernel(qualifier) => Some(qualifier),
_ => None,
}
}
#[inline]
pub fn debug_user_windows_flags(&self) -> Option<DebugUserWindowsFlags> {
match self.debuggee_type {
DebuggeeType::UserWindows(qualifier) => Some(qualifier),
_ => None,
}
}
#[inline]
pub fn page_size(&self) -> DebuggeeOffset { self.page_size }
#[inline]
pub fn ptr_size(&self) -> u64 { self.pointer_width.size() }
#[inline]
pub fn is_32bit(&self) -> bool { self.pointer_width.is_32bit() }
#[inline]
pub fn is_64bit(&self) -> bool { self.pointer_width.is_64bit() }
}
pub struct TargetContext {
client: WdClient,
control: WdControl,
data_spaces: WdDataSpaces,
registers: WdRegisters,
symbols: WdSymbols,
system_objects: WdSystemObjects,
memory: Memory,
information: TargetInformation,
}
impl TargetContext {
pub fn new(client: &WdClient, memory: Memory) -> WdResult<Self> {
let control = WdControl::from_interface(client.as_interface())?;
let data_spaces = WdDataSpaces::from_interface(client.as_interface())?;
let registers = WdRegisters::from_interface(client.as_interface())?;
let symbols = WdSymbols::from_interface(client.as_interface())?;
let system_objects =
WdSystemObjects::from_interface(client.as_interface())?;
let information = TargetInformation::new(&control)?;
Ok(Self {
client: client.clone(),
control,
data_spaces,
registers,
symbols,
system_objects,
memory,
information,
})
}
pub fn update_target_information(&mut self) -> WdResult<()> {
self.information = TargetInformation::new(self.control())?;
Ok(())
}
pub fn update_memory(&mut self, memory: Memory) { self.memory = memory; }
#[inline]
pub fn client(&self) -> &WdClient { &self.client }
#[inline]
pub fn control(&self) -> &WdControl { &self.control }
#[inline]
pub fn data_spaces(&self) -> &WdDataSpaces { &self.data_spaces }
#[inline]
pub fn registers(&self) -> &WdRegisters { &self.registers }
#[inline]
pub fn symbols(&self) -> &WdSymbols { &self.symbols }
#[inline]
pub fn system_objects(&self) -> &WdSystemObjects { &self.system_objects }
pub fn target_information(&self) -> &TargetInformation { &self.information }
#[inline]
pub fn memory(&self) -> &Memory { &self.memory }
#[inline]
pub fn pointer_width(&self) -> PointerWidth {
self.target_information().pointer_width()
}
#[inline]
pub fn actual_processor_type(&self) -> ProcessorType {
self.target_information().actual_processor_type()
}
#[inline]
pub fn effective_processor_type(&self) -> ProcessorType {
self.target_information().effective_processor_type()
}
#[inline]
pub fn debug_class_flags(&self) -> DebugClassFlags {
self.target_information().debug_class_flags()
}
#[inline]
pub fn debuggee_type(&self) -> DebuggeeType {
self.target_information().debuggee_type()
}
#[inline]
pub fn debug_kernel_flags(&self) -> Option<DebugKernelFlags> {
self.target_information().debug_kernel_flags()
}
#[inline]
pub fn debug_user_windows_flags(&self) -> Option<DebugUserWindowsFlags> {
self.target_information().debug_user_windows_flags()
}
#[inline]
pub fn page_size(&self) -> DebuggeeOffset {
self.target_information().page_size()
}
#[inline]
pub fn ptr_size(&self) -> u64 {
self.target_information().pointer_width().size()
}
#[inline]
pub fn is_32bit(&self) -> bool {
self.target_information().pointer_width().is_32bit()
}
#[inline]
pub fn is_64bit(&self) -> bool {
self.target_information().pointer_width().is_64bit()
}
#[inline]
pub fn memory_view(&self) -> TargetMemoryView<'_> {
TargetMemoryView { ctx: self }
}
pub fn evaluate_value<'a, T>(
&self,
expression: impl Into<WideStrArg<'a>>,
) -> WdResult<T>
where
T: DebugValueT,
{
self.control.evaluate_value(expression)
}
pub fn evaluate_pointer<'a>(
&self,
expression: impl Into<WideStrArg<'a>>,
) -> WdResult<DebuggeeOffset> {
match self.pointer_width() {
PointerWidth::Ptr64 => {
self.control.evaluate_value::<u64>(expression)
}
PointerWidth::Ptr32 => {
Ok(self.control.evaluate_value::<u32>(expression)?
as DebuggeeOffset)
}
}
}
#[inline]
pub fn register_view(&self) -> TargetRegisterView<'_> {
TargetRegisterView { ctx: self }
}
#[inline]
pub fn pseudo_register_view(&self) -> TargetPseudoRegisterView<'_> {
TargetPseudoRegisterView { ctx: self }
}
}
impl AsRef<WdClient> for TargetContext {
fn as_ref(&self) -> &WdClient { &self.client }
}
impl AsRef<DebugClient> for TargetContext {
fn as_ref(&self) -> &DebugClient { &self.client.0 }
}
impl AsRef<WdControl> for TargetContext {
fn as_ref(&self) -> &WdControl { &self.control }
}
impl AsRef<DebugControl> for TargetContext {
fn as_ref(&self) -> &DebugControl { &self.control.0 }
}
impl AsRef<WdDataSpaces> for TargetContext {
fn as_ref(&self) -> &WdDataSpaces { &self.data_spaces }
}
impl AsRef<DebugDataSpaces> for TargetContext {
fn as_ref(&self) -> &DebugDataSpaces { &self.data_spaces.0 }
}
impl AsRef<WdRegisters> for TargetContext {
fn as_ref(&self) -> &WdRegisters { &self.registers }
}
impl AsRef<DebugRegisters> for TargetContext {
fn as_ref(&self) -> &DebugRegisters { &self.registers.0 }
}
impl AsRef<WdSymbols> for TargetContext {
fn as_ref(&self) -> &WdSymbols { &self.symbols }
}
impl AsRef<DebugSymbols> for TargetContext {
fn as_ref(&self) -> &DebugSymbols { &self.symbols.0 }
}
impl AsRef<WdSystemObjects> for TargetContext {
fn as_ref(&self) -> &WdSystemObjects { &self.system_objects }
}
impl AsRef<DebugSystemObjects> for TargetContext {
fn as_ref(&self) -> &DebugSystemObjects { &self.system_objects.0 }
}
impl AsRef<Memory> for TargetContext {
fn as_ref(&self) -> &Memory { &self.memory }
}
macro_rules! impl_mem_access {
($r_func:ident, $w_func:ident, $ty:ty) => {
pub fn $r_func(&self, offset: DebuggeeOffset) -> WdResult<$ty> {
self.data_spaces().$r_func(self.memory(), offset)
}
pub fn $w_func(
&self,
offset: DebuggeeOffset,
value: $ty,
) -> WdResult<()> {
self.data_spaces().$w_func(self.memory(), offset, value)
}
};
}
pub struct TargetRegisterView<'a> {
ctx: &'a TargetContext,
}
impl<'a> TargetRegisterView<'a> {
#[inline]
pub fn context(&self) -> &TargetContext { self.ctx }
pub fn registers(&self) -> &WdRegisters { self.context().registers() }
pub fn get_name_by_index(&self, index: RegisterIndex) -> WdResult<String> {
index.get_name(self.registers())
}
pub fn get_index_by_name<'b>(
&self,
name: impl Into<WideStrArg<'b>>,
) -> WdResult<RegisterIndex> {
RegisterIndex::from_name(self.registers(), name)
}
pub fn get_description<'b>(
&self,
index: impl Into<RegisterSelector<'a>>,
) -> WdResult<DebugRegisterDescription> {
index
.into()
.to_index(self.registers())?
.get_description(self.registers())
}
pub fn read_value<'b, T>(
&self,
reg: impl Into<RegisterSelector<'b>>,
) -> WdResult<T>
where
T: DebugValueT,
{
self.registers().read_value(reg)
}
pub fn read_value_from<'b, T>(
&self,
source: DebugRegSrc,
reg: impl Into<RegisterSelector<'b>>,
) -> WdResult<T>
where
T: DebugValueT,
{
self.registers().read_value_from(source, reg)
}
pub fn write_value<'b, T>(
&self,
reg: impl Into<RegisterSelector<'b>>,
value: T,
) -> WdResult<()>
where
T: Into<DebugValue>,
{
self.registers().write_value(reg, value)
}
pub fn write_value_from<'b, T>(
&self,
source: DebugRegSrc,
reg: impl Into<RegisterSelector<'b>>,
value: T,
) -> WdResult<()>
where
T: Into<DebugValue>,
{
self.registers().write_value_from(source, reg, value)
}
pub fn read_pointer<'b>(
&self,
reg: impl Into<RegisterSelector<'b>>,
) -> WdResult<DebuggeeOffset> {
match self.context().pointer_width() {
PointerWidth::Ptr32 => {
Ok(self.read_value::<u32>(reg)? as DebuggeeOffset)
}
PointerWidth::Ptr64 => self.read_value::<u64>(reg),
}
}
pub fn read_pointer_from<'b>(
&self,
source: DebugRegSrc,
reg: impl Into<RegisterSelector<'b>>,
) -> WdResult<DebuggeeOffset> {
match self.context().pointer_width() {
PointerWidth::Ptr32 => {
Ok(self.read_value_from::<u32>(source, reg)? as DebuggeeOffset)
}
PointerWidth::Ptr64 => self.read_value_from::<u64>(source, reg),
}
}
pub fn write_pointer<'b>(
&self,
reg: impl Into<RegisterSelector<'b>>,
value: DebuggeeOffset,
) -> WdResult<()> {
match self.context().pointer_width() {
PointerWidth::Ptr32 => {
Ok(self.write_value::<u32>(reg, value.try_into()?)?)
}
PointerWidth::Ptr64 => self.write_value::<u64>(reg, value),
}
}
pub fn write_pointer_from<'b>(
&self,
source: DebugRegSrc,
reg: impl Into<RegisterSelector<'b>>,
value: DebuggeeOffset,
) -> WdResult<()> {
match self.context().pointer_width() {
PointerWidth::Ptr32 => Ok(self.write_value_from::<u32>(
source,
reg,
value.try_into()?,
)?),
PointerWidth::Ptr64 => {
self.write_value_from::<u64>(source, reg, value)
}
}
}
pub fn read_instruction_offset(&self) -> WdResult<DebuggeeOffset> {
self.context().registers().get_instruction_offset()
}
pub fn read_instruction_offset_from(
&self,
source: DebugRegSrc,
) -> WdResult<DebuggeeOffset> {
self.context()
.registers()
.get_instruction_offset_from(source)
}
}
pub struct TargetPseudoRegisterView<'a> {
ctx: &'a TargetContext,
}
impl<'a> TargetPseudoRegisterView<'a> {
#[inline]
pub fn context(&self) -> &TargetContext { self.ctx }
pub fn registers(&self) -> &WdRegisters { self.context().registers() }
pub fn get_name_by_index(
&self,
index: PseudoRegisterIndex,
) -> WdResult<String> {
index.get_name(self.registers())
}
pub fn get_index_by_name<'b>(
&self,
name: impl Into<WideStrArg<'b>>,
) -> WdResult<PseudoRegisterIndex> {
PseudoRegisterIndex::from_name(self.registers(), name)
}
pub fn get_type<'b>(
&self,
index: impl Into<PseudoRegisterSelector<'a>>,
) -> WdResult<SymbolTypeId> {
index
.into()
.to_index(self.registers())?
.get_type(self.registers())
}
pub fn read_value_from<'b, T>(
&self,
source: DebugRegSrc,
reg: impl Into<PseudoRegisterSelector<'b>>,
) -> WdResult<T>
where
T: DebugValueT,
{
self.registers().read_pseudo_value_from(source, reg)
}
pub fn write_value_from<'b, T>(
&self,
source: DebugRegSrc,
reg: impl Into<PseudoRegisterSelector<'b>>,
value: T,
) -> WdResult<()>
where
T: Into<DebugValue>,
{
self.registers().write_pseudo_value_from(source, reg, value)
}
pub fn read_pointer_from<'b>(
&self,
source: DebugRegSrc,
reg: impl Into<PseudoRegisterSelector<'b>>,
) -> WdResult<DebuggeeOffset> {
match self.context().pointer_width() {
PointerWidth::Ptr32 => {
Ok(self.read_value_from::<u32>(source, reg)? as DebuggeeOffset)
}
PointerWidth::Ptr64 => self.read_value_from::<u64>(source, reg),
}
}
pub fn write_pointer_from<'b>(
&self,
source: DebugRegSrc,
reg: impl Into<PseudoRegisterSelector<'b>>,
value: DebuggeeOffset,
) -> WdResult<()> {
match self.context().pointer_width() {
PointerWidth::Ptr32 => {
let value: u32 = value.try_into()?;
Ok(self.write_value_from(source, reg, value)?)
}
PointerWidth::Ptr64 => self.write_value_from(source, reg, value),
}
}
pub fn read_instruction_offset(&self) -> WdResult<DebuggeeOffset> {
self.context().registers().get_instruction_offset()
}
pub fn read_instruction_offset_from(
&self,
source: DebugRegSrc,
) -> WdResult<DebuggeeOffset> {
self.context()
.registers()
.get_instruction_offset_from(source)
}
}
impl<'a> From<&'a TargetContext> for TargetRegisterView<'a> {
fn from(value: &'a TargetContext) -> Self { value.register_view() }
}
#[derive(Copy, Clone)]
pub struct TargetMemoryView<'a> {
ctx: &'a TargetContext,
}
impl<'a> TargetMemoryView<'a> {
#[inline]
pub fn context(&self) -> &TargetContext { self.ctx }
pub fn memory(&self) -> &Memory { self.context().memory() }
fn data_spaces(&self) -> &WdDataSpaces { self.context().data_spaces() }
pub fn read_msr(&self, msr: u32) -> WdResult<u64> {
self.data_spaces().read_msr(msr)
}
pub fn write_msr(&self, msr: u32, value: u64) -> WdResult<()> {
self.data_spaces().write_msr(msr, value)
}
pub fn read_memory(
&self,
offset: DebuggeeOffset,
buf: &mut [u8],
) -> WdResult<usize> {
self.data_spaces().read(self.memory(), offset, buf)
}
pub fn write_memory(
&self,
offset: DebuggeeOffset,
buf: &[u8],
) -> WdResult<usize> {
self.data_spaces().write(self.memory(), offset, buf)
}
pub fn read_object<T>(&self, offset: DebuggeeOffset) -> WdResult<T>
where
T: FromBytes,
{
self.data_spaces().read_object(self.memory(), offset)
}
pub fn read_objects<T>(
&self,
offset: DebuggeeOffset,
buf: &mut [T],
) -> WdResult<usize>
where
T: FromBytes,
{
self.data_spaces()
.read_objects_inner(self.memory(), offset, buf)
}
pub fn write_object<T>(
&self,
offset: DebuggeeOffset,
value: &T,
) -> WdResult<()>
where
T: IntoBytes + Immutable,
{
self.data_spaces()
.write_object(self.memory(), offset, value)
}
pub fn write_objects<T>(
&self,
offset: DebuggeeOffset,
buf: &[T],
) -> WdResult<usize>
where
T: IntoBytes + Immutable,
{
self.data_spaces().write_objects(self.memory(), offset, buf)
}
impl_mem_access!(read_u8, write_u8, u8);
impl_mem_access!(read_u16_le, write_u16_le, u16);
impl_mem_access!(read_u16_be, write_u16_be, u16);
impl_mem_access!(read_u32_le, write_u32_le, u32);
impl_mem_access!(read_u32_be, write_u32_be, u32);
impl_mem_access!(read_u64_le, write_u64_le, u64);
impl_mem_access!(read_u64_be, write_u64_be, u64);
impl_mem_access!(read_usize_le, write_usize_le, usize);
impl_mem_access!(read_usize_be, write_usize_be, usize);
impl_mem_access!(read_isize_le, write_isize_le, isize);
impl_mem_access!(read_isize_be, write_isize_be, isize);
impl_mem_access!(read_i8, write_i8, i8);
impl_mem_access!(read_i16_le, write_i16_le, i16);
impl_mem_access!(read_i16_be, write_i16_be, i16);
impl_mem_access!(read_i32_le, write_i32_le, i32);
impl_mem_access!(read_i32_be, write_i32_be, i32);
impl_mem_access!(read_i64_le, write_i64_le, i64);
impl_mem_access!(read_i64_be, write_i64_be, i64);
impl_mem_access!(read_bool, write_bool, bool);
pub fn read_utf8_string(
&self,
offset: DebuggeeOffset,
max_bytes: impl Into<Option<u32>>,
) -> WdResult<(String, ReadStringStatus)> {
self.read_ansi_string(offset, max_bytes, CP_UTF8)
}
pub fn write_utf8_string(
&self,
offset: DebuggeeOffset,
value: impl AsRef<str>,
) -> WdResult<()> {
self.write_ansi_string(offset, value, CP_UTF8)
}
pub fn read_ansi_string(
&self,
offset: DebuggeeOffset,
max_bytes: impl Into<Option<u32>>,
code_page: impl Into<Option<u32>>,
) -> WdResult<(String, ReadStringStatus)> {
self.data_spaces().read_ansi_string(
self.memory(),
offset,
max_bytes,
code_page,
)
}
pub fn write_ansi_string(
&self,
offset: DebuggeeOffset,
value: impl AsRef<str>,
code_page: u32,
) -> WdResult<()> {
self.data_spaces().write_ansi_string(
self.memory(),
offset,
value,
code_page,
)
}
pub fn read_unicode_string(
&self,
offset: DebuggeeOffset,
max_bytes: impl Into<Option<u32>>,
) -> WdResult<(String, ReadStringStatus)> {
self.data_spaces()
.read_unicode_string(self.memory(), offset, max_bytes)
}
pub fn read_unicode_string_lossy(
&self,
offset: DebuggeeOffset,
max_bytes: impl Into<Option<u32>>,
) -> WdResult<(String, ReadStringStatus)> {
self.data_spaces().read_unicode_string_lossy(
self.memory(),
offset,
max_bytes,
)
}
pub fn write_unicode_string(
&self,
offset: DebuggeeOffset,
value: impl Into<WideStrArg<'a>>,
) -> WdResult<()> {
self.data_spaces()
.write_unicode_string(self.memory(), offset, value)
}
pub fn read_pointer(
&self,
offset: DebuggeeOffset,
) -> WdResult<DebuggeeOffset> {
match self.context().pointer_width() {
PointerWidth::Ptr32 => {
Ok(self.data_spaces().read_u32_le(self.memory(), offset)?
as DebuggeeOffset)
}
PointerWidth::Ptr64 => {
self.data_spaces().read_u64_le(self.memory(), offset)
}
}
}
pub fn write_pointer(
&self,
offset: DebuggeeOffset,
value: DebuggeeOffset,
) -> WdResult<()> {
match self.context().pointer_width() {
PointerWidth::Ptr32 => self.data_spaces().write_u32_le(
self.memory(),
offset,
value.try_into()?,
),
PointerWidth::Ptr64 => {
self.data_spaces()
.write_u64_le(self.memory(), offset, value)
}
}
}
}
impl<'a> From<&'a TargetContext> for TargetMemoryView<'a> {
fn from(value: &'a TargetContext) -> Self { value.memory_view() }
}
#[derive(Debug, Clone)]
pub enum WideStrArg<'a> {
Wide(Cow<'a, WStr>),
Utf8(Cow<'a, str>),
}
impl<'a> WideStrArg<'a> {
pub fn into_wide(self) -> ConvertResult<Cow<'a, WStr>> {
match self {
WideStrArg::Wide(s) => Ok(s),
WideStrArg::Utf8(s) => Ok(Cow::Owned(s.try_to_wstring()?)),
}
}
}
impl<'a> From<WString> for WideStrArg<'a> {
fn from(value: WString) -> Self { Self::Wide(Cow::Owned(value)) }
}
impl<'a> From<&'a WString> for WideStrArg<'a> {
fn from(value: &'a WString) -> Self { Self::Wide(Cow::Borrowed(value)) }
}
impl<'a> From<&'a WStr> for WideStrArg<'a> {
fn from(value: &'a WStr) -> Self { Self::Wide(Cow::Borrowed(value)) }
}
impl<'a> From<String> for WideStrArg<'a> {
fn from(value: String) -> Self { Self::Utf8(Cow::Owned(value)) }
}
impl<'a> From<&'a String> for WideStrArg<'a> {
fn from(value: &'a String) -> Self { Self::Utf8(Cow::Borrowed(value)) }
}
impl<'a> From<&'a str> for WideStrArg<'a> {
fn from(value: &'a str) -> Self { Self::Utf8(Cow::Borrowed(value)) }
}
impl<'a> From<Cow<'a, str>> for WideStrArg<'a> {
fn from(value: Cow<'a, str>) -> Self { Self::Utf8(value) }
}
impl<'a> fmt::Display for WideStrArg<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Wide(s) => write!(f, "{s}"),
Self::Utf8(s) => f.write_str(s),
}
}
}
#[derive(Debug, Clone)]
pub enum AnsiStrArg<'a, const CP: u32> {
Ansi(Cow<'a, AStr<CP>>),
Utf8(Cow<'a, str>),
}
impl<'a, const CP: u32> AnsiStrArg<'a, CP> {
pub fn into_ansi(self) -> ConvertResult<Cow<'a, AStr<CP>>> {
match self {
Self::Ansi(s) => Ok(s),
Self::Utf8(s) => Ok(Cow::Owned(s.try_to_astring()?)),
}
}
}
impl<'a, const CP: u32> From<AString<CP>> for AnsiStrArg<'a, CP> {
fn from(value: AString<CP>) -> Self { Self::Ansi(Cow::Owned(value)) }
}
impl<'a, const CP: u32> From<&'a AString<CP>> for AnsiStrArg<'a, CP> {
fn from(value: &'a AString<CP>) -> Self { Self::Ansi(Cow::Borrowed(value)) }
}
impl<'a, const CP: u32> From<&'a AStr<CP>> for AnsiStrArg<'a, CP> {
fn from(value: &'a AStr<CP>) -> Self { Self::Ansi(Cow::Borrowed(value)) }
}
impl<'a, const CP: u32> From<String> for AnsiStrArg<'a, CP> {
fn from(value: String) -> Self { Self::Utf8(Cow::Owned(value)) }
}
impl<'a, const CP: u32> From<&'a String> for AnsiStrArg<'a, CP> {
fn from(value: &'a String) -> Self { Self::Utf8(Cow::Borrowed(value)) }
}
impl<'a, const CP: u32> From<&'a str> for AnsiStrArg<'a, CP> {
fn from(value: &'a str) -> Self { Self::Utf8(Cow::Borrowed(value)) }
}
impl<'a, const CP: u32> From<Cow<'a, str>> for AnsiStrArg<'a, CP> {
fn from(value: Cow<'a, str>) -> Self { Self::Utf8(value) }
}
impl<'a, const CP: u32> fmt::Display for AnsiStrArg<'a, CP> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Ansi(s) => write!(f, "{s}"),
Self::Utf8(s) => f.write_str(s),
}
}
}
pub(crate) fn to_uninit_u8_slice(buffer: &mut [u8]) -> &mut [MaybeUninit<u8>] {
unsafe {
slice::from_raw_parts_mut(
buffer.as_mut_ptr().cast::<MaybeUninit<u8>>(),
buffer.len(),
)
}
}