use crate::{
error::WdErrorKind, expr::PtrEvalExpr, object::traits::list::*,
wd::dbgmodel::WdCallingConventionKind, *,
};
use std::{collections::HashSet, marker::PhantomData, num::ParseIntError};
pub fn parse_masm_value(
s: impl AsRef<str>,
) -> Result<DebuggeeOffset, ParseIntError> {
let s = s.as_ref();
let mut negative = false;
let mut sign_len = 0;
for &byte in s.as_bytes() {
match byte {
b'+' => sign_len += 1,
b'-' => {
sign_len += 1;
negative = !negative;
}
_ => break,
}
}
let value = parse_unsigned_masm_value(&s[sign_len..])?;
Ok(if negative {
value.wrapping_neg()
} else {
value
})
}
fn parse_unsigned_masm_value(
s: impl AsRef<str>,
) -> Result<DebuggeeOffset, ParseIntError> {
let s = s.as_ref();
if s.is_empty() {
return DebuggeeOffset::from_str_radix(s, 16);
}
if let Some(s) = s.strip_suffix("h") {
return DebuggeeOffset::from_str_radix(s, 16);
}
if let Some(s) = s.strip_prefix("0n") {
if s.is_empty() {
return Ok(0);
}
return DebuggeeOffset::from_str_radix(s, 10);
}
if let Some(s) = s.strip_prefix("0t") {
if s.is_empty() {
return Ok(0);
}
return DebuggeeOffset::from_str_radix(s, 8);
}
if let Some(s) = s.strip_prefix("0y") {
if s.is_empty() {
return Ok(0);
}
return DebuggeeOffset::from_str_radix(s, 2);
}
if s.starts_with('`') {
return DebuggeeOffset::from_str_radix(s, 16);
}
let s = s.strip_prefix("0x").unwrap_or(s);
if s.is_empty() {
return Ok(0);
}
if !s.as_bytes().contains(&b'`') {
DebuggeeOffset::from_str_radix(s, 16)
} else {
let s = s.replace('`', "");
DebuggeeOffset::from_str_radix(&s, 16)
}
}
pub struct ListEntryIterator<'a, LE: ListEntry> {
cur: LE::Ptr,
ds: &'a WdDataSpaces,
memory: &'a Memory,
check_sentinel: bool,
sentinel: LE::Ptr,
visited: HashSet<LE::Ptr>,
forward: bool,
detect_cycles: bool,
infinite_loop: bool,
finished: bool,
}
impl<'a, LE: ListEntry> ListEntryIterator<'a, LE> {
#[allow(clippy::too_many_arguments)]
pub fn new(
ds: &'a WdDataSpaces,
memory: &'a Memory,
addr_head: impl Into<LE::Ptr>,
sentinel: Option<DebuggeeOffset>,
forward: bool,
include_initial_entry: bool,
detect_cycles: bool,
infinite_loop: bool,
) -> WdResult<Self> {
let initial_offset = addr_head.into();
let cur = if include_initial_entry {
initial_offset
} else {
let l = initial_offset.read(ds, memory)?;
if forward { l.flink() } else { l.blink() }
};
Ok(Self {
cur,
ds,
memory,
check_sentinel: !include_initial_entry,
sentinel: sentinel.map_or(initial_offset, Into::into),
visited: HashSet::new(),
forward,
detect_cycles: detect_cycles && !infinite_loop,
infinite_loop,
finished: false,
})
}
}
impl<'a, LE: ListEntry> Iterator for ListEntryIterator<'a, LE> {
type Item = WdResult<TObject<LE::Ptr, LE>>;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
if !self.infinite_loop {
if self.check_sentinel && self.sentinel == self.cur {
self.finished = true;
return None;
}
self.check_sentinel = true;
}
let cur = self.cur;
if self.detect_cycles && !self.visited.insert(self.cur) {
self.finished = true;
return Some(Err(WdErrorKind::ListEntryCycleDetected.into_error()));
}
let l = match self.cur.read(self.ds, self.memory) {
Ok(x) => x,
Err(e) => {
self.finished = true;
return Some(Err(e));
}
};
let next_addr = if self.forward { l.flink() } else { l.blink() };
self.cur = next_addr;
Some(Ok(TObject::new(cur, l)))
}
}
impl<'a, LE: ListEntry> std::iter::FusedIterator for ListEntryIterator<'a, LE> {}
pub struct TypedListEntryIterator<'a, LE, T>
where
LE: TypedListEntry<T>,
{
it: ListEntryIterator<'a, LE>,
phantom: PhantomData<T>,
}
impl<'a, LE, T> TypedListEntryIterator<'a, LE, T>
where
LE: TypedListEntry<T>,
{
#[allow(clippy::too_many_arguments)]
pub fn new(
ds: &'a WdDataSpaces,
memory: &'a Memory,
addr_head: impl Into<LE::Ptr>,
sentinel: Option<DebuggeeOffset>,
forward: bool,
include_initial_entry: bool,
detect_cycles: bool,
infinite_loop: bool,
) -> WdResult<Self> {
Ok(Self {
it: ListEntryIterator::new(
ds,
memory,
addr_head,
sentinel,
forward,
include_initial_entry,
detect_cycles,
infinite_loop,
)?,
phantom: Default::default(),
})
}
}
impl<'a, LE, T> Iterator for TypedListEntryIterator<'a, LE, T>
where
LE: TypedListEntry<T>,
{
type Item = WdResult<LE::TypedPtr>;
fn next(&mut self) -> Option<Self::Item> {
match self.it.next()? {
Ok(x) => Some(Ok(x
.ptr()
.offset()
.wrapping_sub(LE::OFFSET_OF_ENTRY)
.into())),
Err(e) => Some(Err(e)),
}
}
}
impl<'a, LE, T> std::iter::FusedIterator for TypedListEntryIterator<'a, LE, T> where
LE: TypedListEntry<T>
{
}
pub struct SListEntryIterator<'a, LE: SListEntry> {
cur: LE::Ptr,
ds: &'a WdDataSpaces,
memory: &'a Memory,
check_sentinel: bool,
sentinel: LE::Ptr,
visited: HashSet<LE::Ptr>,
detect_cycles: bool,
finished: bool,
}
impl<'a, LE: SListEntry> SListEntryIterator<'a, LE> {
#[allow(clippy::too_many_arguments)]
pub fn new(
ds: &'a WdDataSpaces,
memory: &'a Memory,
addr_head: impl Into<LE::Ptr>,
sentinel: Option<DebuggeeOffset>,
detect_cycles: bool,
) -> WdResult<Self> {
let initial_offset = addr_head.into();
Ok(Self {
cur: initial_offset,
ds,
memory,
check_sentinel: false,
sentinel: sentinel.unwrap_or_default().into(),
visited: HashSet::new(),
detect_cycles,
finished: false,
})
}
}
impl<'a, LE: SListEntry> Iterator for SListEntryIterator<'a, LE> {
type Item = WdResult<TObject<LE::Ptr, LE>>;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
if self.check_sentinel && self.sentinel == self.cur {
self.finished = true;
return None;
}
self.check_sentinel = true;
let cur = self.cur;
if self.detect_cycles && !self.visited.insert(self.cur) {
self.finished = true;
return Some(Err(WdErrorKind::ListEntryCycleDetected.into_error()));
}
let l = match self.cur.read(self.ds, self.memory) {
Ok(x) => x,
Err(e) => {
self.finished = true;
return Some(Err(e));
}
};
self.cur = l.next();
if self.cur.is_null() {
self.finished = true;
}
Some(Ok(TObject::new(cur, l)))
}
}
impl<'a, LE: SListEntry> std::iter::FusedIterator
for SListEntryIterator<'a, LE>
{
}
pub struct TypedSListEntryIterator<'a, LE, T>
where
LE: TypedSListEntry<T>,
{
it: SListEntryIterator<'a, LE>,
phantom: PhantomData<T>,
}
impl<'a, LE, T> TypedSListEntryIterator<'a, LE, T>
where
LE: TypedSListEntry<T>,
{
#[allow(clippy::too_many_arguments)]
pub fn new(
ds: &'a WdDataSpaces,
memory: &'a Memory,
addr_head: impl Into<LE::Ptr>,
sentinel: Option<DebuggeeOffset>,
detect_cycles: bool,
) -> WdResult<Self> {
Ok(Self {
it: SListEntryIterator::new(
ds,
memory,
addr_head,
sentinel,
detect_cycles,
)?,
phantom: Default::default(),
})
}
}
impl<'a, LE, T> Iterator for TypedSListEntryIterator<'a, LE, T>
where
LE: TypedSListEntry<T>,
{
type Item = WdResult<LE::TypedPtr>;
fn next(&mut self) -> Option<Self::Item> {
match self.it.next()? {
Ok(x) => Some(Ok(x
.ptr()
.offset()
.wrapping_sub(LE::OFFSET_OF_ENTRY)
.into())),
Err(e) => Some(Err(e)),
}
}
}
impl<'a, LE, T> std::iter::FusedIterator for TypedSListEntryIterator<'a, LE, T> where
LE: TypedSListEntry<T>
{
}
pub trait BitMapWord: Copy {
const BITS: u32;
fn is_bit_set(self, bit_index: usize) -> bool;
}
macro_rules! impl_bitmap_word {
($($ty:ty),* $(,)?) => {
$(
impl BitMapWord for $ty {
const BITS: u32 = <$ty>::BITS;
#[inline]
fn is_bit_set(self, bit_index: usize) -> bool {
self & ((1 as $ty) << bit_index) != 0
}
}
)*
};
}
impl_bitmap_word!(u8, u16, u32, u64);
#[derive(Debug, Clone)]
pub struct BitMapIterator<'a, W> {
bitmap: &'a [W],
bit_idx: usize,
bit_size: usize,
}
impl<'a, W: BitMapWord> BitMapIterator<'a, W> {
pub fn new(
bitmap: &'a [W],
bit_size: impl Into<Option<usize>>,
) -> Option<Self> {
let bm_len = bitmap.len();
let capacity_bits = bm_len.checked_mul(W::BITS as usize)?;
let bit_size = bit_size.into().unwrap_or(capacity_bits);
if bit_size > capacity_bits {
return None;
}
Some(Self {
bitmap,
bit_idx: 0,
bit_size,
})
}
pub fn bit_size(&self) -> usize { self.bit_size }
pub fn remaining_bits(&self) -> usize { self.bit_size - self.bit_idx }
#[inline]
pub fn bit_at(&self, bit_idx: usize) -> Option<bool> {
if bit_idx >= self.bit_size {
return None;
}
let word_bits = W::BITS as usize;
let word_idx = bit_idx / word_bits;
let bit_in_word = bit_idx % word_bits;
Some(self.bitmap[word_idx].is_bit_set(bit_in_word))
}
}
impl<'a, W: BitMapWord> Iterator for BitMapIterator<'a, W> {
type Item = bool;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.bit_idx >= self.bit_size {
return None;
}
let word_bits = W::BITS as usize;
let word_idx = self.bit_idx / word_bits;
let bit_in_word = self.bit_idx % word_bits;
let result = self.bitmap[word_idx].is_bit_set(bit_in_word);
self.bit_idx += 1;
Some(result)
}
#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let target = self.bit_idx.checked_add(n)?;
if target >= self.bit_size {
self.bit_idx = self.bit_size;
return None;
}
let result = self.bit_at(target);
self.bit_idx = target + 1;
result
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.remaining_bits();
(remaining, Some(remaining))
}
}
impl<'a, W: BitMapWord> std::iter::FusedIterator for BitMapIterator<'a, W> {}
pub struct CallSiteArgsIterator<'a> {
index: usize,
reg_args: &'a [&'a str],
sp_name: &'a str,
stack_offset: u64,
ptr_width: PointerWidth,
fmt_width: usize,
}
impl CallSiteArgsIterator<'static> {
pub fn new_with_calling_convention(
ptr_width: PointerWidth,
cconv: WdCallingConventionKind,
) -> WdResult<Self> {
use PointerWidth::*;
use WdCallingConventionKind::*;
let (reg_args, sp_name, stack_offset): (&[&str], &str, u64) =
match (ptr_width, cconv) {
(_, Unknown) => {
return Err(WdErrorKind::InvalidArgument.into_error());
}
(Ptr32, CDecl | StdCall | SysCall) => (&[], "@esp", 0),
(Ptr32, FastCall) => (&["@ecx", "@edx"], "@esp", 0),
(Ptr32, ThisCall) => (&["@ecx"], "@esp", 0),
(Ptr64, CDecl | StdCall | FastCall | ThisCall) => {
(&["@rcx", "@rdx", "@r8", "@r9"], "@rsp", 4)
}
(Ptr64, SysCall) => {
(&["@r10", "@rdx", "@r8", "@r9"], "@rsp", 5)
}
};
Ok(Self::new(ptr_width, reg_args, sp_name, stack_offset))
}
pub fn fmt_width(mut self, fmt_width: usize) -> Self {
self.fmt_width = fmt_width;
self
}
pub fn fit_fmt_width(self, num: usize) -> Self {
let stack_args =
num.saturating_sub(1).saturating_sub(self.reg_args.len());
let width = format!(
"{:x}",
stack_args.saturating_mul(self.ptr_width.size() as usize)
)
.len();
self.fmt_width(width - 1)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ArgLocation {
Register,
Stack,
}
impl ArgLocation {
pub fn is_register(self) -> bool { matches!(self, ArgLocation::Register) }
pub fn is_stack(self) -> bool { matches!(self, ArgLocation::Stack) }
}
#[derive(Debug, Clone)]
pub struct ArgInfo {
pub expression: PtrEvalExpr,
pub location: ArgLocation,
}
impl ArgInfo {
pub fn is_register(&self) -> bool { self.location.is_register() }
pub fn is_stack(&self) -> bool { self.location.is_stack() }
}
impl<'a> CallSiteArgsIterator<'a> {
pub fn new(
ptr_width: PointerWidth,
reg_args: &'a [&'a str],
sp_name: &'a str,
stack_offset: u64,
) -> Self {
Self {
index: 0,
reg_args,
sp_name,
stack_offset,
ptr_width,
fmt_width: 0,
}
}
fn expression_at(&self, index: usize) -> ArgInfo {
if let Some(®) = self.reg_args.get(index) {
return ArgInfo {
expression: reg.into(),
location: ArgLocation::Register,
};
}
let stack_index = (index - self.reg_args.len()) as u64;
let byte_offset =
(stack_index + self.stack_offset) * self.ptr_width.size();
ArgInfo {
expression: format!(
"{}{byte_offset:+#0width$x}",
self.sp_name,
width = self.fmt_width + 4
)
.into(),
location: ArgLocation::Stack,
}
}
}
impl<'a> Iterator for CallSiteArgsIterator<'a> {
type Item = ArgInfo;
fn next(&mut self) -> Option<Self::Item> {
let result = self.expression_at(self.index);
self.index += 1;
Some(result)
}
fn size_hint(&self) -> (usize, Option<usize>) { (usize::MAX, None) }
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.index = self.index.checked_add(n)?;
self.next()
}
}