use std::{
cell::Cell,
sync::{Arc, Weak},
};
use crate::{error::*, os::tid_t, register::*, target::UDbgTarget};
use cfg_if::*;
pub type BpID = isize;
#[repr(u8)]
#[derive(Copy, Clone, Debug)]
pub enum HwbpType {
Execute = 0,
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
Write = 1,
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
Read = 1,
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
Write = 2,
Access = 3,
}
#[derive(Copy, Clone, Debug)]
pub enum BpType {
Soft,
Table,
Hwbp(HwbpType, u8),
}
impl BpType {
#[inline]
pub fn is_hard(&self) -> bool {
if let Self::Hwbp(_, _) = self {
true
} else {
false
}
}
#[inline]
pub fn is_soft(&self) -> bool {
if let Self::Soft = self {
true
} else {
false
}
}
#[inline]
pub fn is_table(&self) -> bool {
if let Self::Table = self {
true
} else {
false
}
}
}
impl ToString for BpType {
fn to_string(&self) -> String {
match self {
Self::Soft => "soft".into(),
Self::Table => "table".into(),
Self::Hwbp(t, l) => {
format!(
"hwbp:{}{}",
match t {
HwbpType::Execute => "e",
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
HwbpType::Read => "r",
HwbpType::Write => "w",
HwbpType::Access => "a",
},
["1", "2", "8", "4"][*l as usize]
)
}
}
}
}
impl From<u8> for HwbpType {
fn from(b: u8) -> Self {
match b {
0 => HwbpType::Execute,
1 => HwbpType::Write,
3 => HwbpType::Access,
_ => unreachable!(),
}
}
}
impl Into<u8> for HwbpLen {
fn into(self) -> u8 {
self as u8
}
}
#[repr(u8)]
#[derive(Debug, Copy, Clone)]
pub enum HwbpLen {
L1 = 0,
L2,
L4,
L8,
}
impl HwbpLen {
pub fn to_int(self) -> u32 {
match self {
Self::L1 => 1,
Self::L2 => 2,
Self::L4 => 4,
Self::L8 => 8,
}
}
pub fn encode(self) -> u8 {
if crate::consts::IS_X86 {
return match self {
Self::L1 => 0,
Self::L2 => 1,
Self::L4 => 3,
Self::L8 => 2,
};
}
if crate::consts::IS_ARM {
return ((1 << self.to_int()) - 1) as _;
}
unreachable!()
}
}
#[derive(Debug)]
pub struct BpOpt {
pub address: usize,
pub rw: Option<HwbpType>,
pub len: Option<HwbpLen>,
pub table: bool, pub temp: bool,
pub enable: bool,
pub tid: Option<tid_t>,
}
impl From<usize> for BpOpt {
fn from(address: usize) -> Self {
Self::int3(address)
}
}
impl From<(usize, HwbpType)> for BpOpt {
fn from((address, ty): (usize, HwbpType)) -> Self {
Self::hwbp(address, ty, None)
}
}
impl BpOpt {
pub fn int3(address: usize) -> Self {
Self {
address,
temp: false,
enable: true,
tid: None,
rw: None,
len: None,
table: false,
}
}
pub fn hwbp(address: usize, ty: HwbpType, len: Option<HwbpLen>) -> Self {
Self {
address,
temp: false,
enable: true,
tid: None,
rw: ty.into(),
len,
table: false,
}
}
pub fn temp(mut self, b: bool) -> Self {
self.temp = b;
self
}
pub fn enable(mut self, b: bool) -> Self {
self.enable = b;
self
}
pub fn thread(mut self, tid: tid_t) -> Self {
self.tid = Some(tid);
self
}
pub fn len(mut self, len: HwbpLen) -> Self {
self.len = len.into();
self
}
}
#[derive(Clone, Copy, Debug)]
pub struct HwbpInfo {
pub rw: u8,
pub len: u8,
pub index: u8,
}
cfg_if! {
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
#[derive(Copy, Clone)]
pub enum InnerBpType {
Soft(BpInsn),
Hard(HwbpInfo),
Table {index: isize, origin: usize},
}
pub type BpInsn = [u8; 1];
pub const BP_INSN: &BpInsn = &[0xCC];
} else if #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] {
#[derive(Copy, Clone)]
pub enum InnerBpType {
Soft(BpInsn),
Hard(HwbpInfo),
Table {index: isize, origin: usize},
}
pub type BpInsn = [u8; 4];
pub const BP_INSN: &BpInsn = &[0x00, 0x00, 0x3E, 0xD4];
}
}
pub trait UDbgBreakpoint: 'static {
fn get_id(&self) -> BpID;
fn address(&self) -> usize;
fn enabled(&self) -> bool;
fn get_type(&self) -> BpType;
fn hit_count(&self) -> usize;
fn set_count(&self, count: usize);
fn set_hit_thread(&self, tid: tid_t);
fn hit_tid(&self) -> tid_t;
fn origin_bytes(&self) -> Option<&[u8]>;
fn enable(&self, enable: bool) -> UDbgResult<()>;
fn remove(&self) -> UDbgResult<()>;
}
#[derive(Clone)]
pub struct Breakpoint {
pub address: usize,
pub enabled: Cell<bool>,
pub temp: Cell<bool>,
pub bp_type: InnerBpType,
pub hit_count: Cell<usize>,
pub hit_tid: Option<tid_t>,
pub target: Weak<dyn UDbgTarget>,
pub common: *const crate::os::TargetCommon,
}
impl Breakpoint {
pub fn get_hwbp_len(&self) -> Option<usize> {
if let InnerBpType::Hard(info) = self.bp_type {
Some(match info.len as _ {
LEN_1 => 1,
LEN_2 => 2,
LEN_4 => 4,
LEN_8 => 8,
_ => 0,
})
} else {
None
}
}
#[inline]
pub fn is_hard(&self) -> bool {
self.get_type().is_hard()
}
#[inline]
pub fn is_soft(&self) -> bool {
self.get_type().is_soft()
}
#[inline]
pub fn is_table(&self) -> bool {
self.get_type().is_table()
}
#[inline]
pub fn hard_index(&self) -> Option<usize> {
if let InnerBpType::Hard(info) = self.bp_type {
Some(info.index as usize)
} else {
None
}
}
}
impl UDbgBreakpoint for Breakpoint {
fn get_id(&self) -> BpID {
self.address as BpID
}
fn address(&self) -> usize {
self.address
}
fn enabled(&self) -> bool {
self.enabled.get()
}
fn get_type(&self) -> BpType {
match self.bp_type {
InnerBpType::Soft { .. } => BpType::Soft,
InnerBpType::Table { .. } => BpType::Table,
InnerBpType::Hard(info) => BpType::Hwbp(info.rw.into(), info.len),
}
}
fn hit_count(&self) -> usize {
self.hit_count.get()
}
fn set_count(&self, count: usize) {}
fn set_hit_thread(&self, tid: tid_t) {}
fn hit_tid(&self) -> tid_t {
0
}
fn origin_bytes<'a>(&'a self) -> Option<&'a [u8]> {
if let InnerBpType::Soft(raw) = &self.bp_type {
Some(raw)
} else {
None
}
}
fn enable(&self, enable: bool) -> UDbgResult<()> {
let t = self.target.upgrade().ok_or(UDbgError::NoTarget)?;
unsafe {
let common = self.common.as_ref().unwrap();
common.enable_breadpoint(t.as_ref(), self, enable)?;
Ok(())
}
}
fn remove(&self) -> UDbgResult<()> {
let t = self.target.upgrade().ok_or(UDbgError::NoTarget)?;
unsafe {
let common = self.common.as_ref().unwrap();
self.enable(false);
common.remove_breakpoint(t.as_ref(), self);
Ok(())
}
}
}
pub trait BreakpointManager {
fn add_breakpoint(&self, opt: BpOpt) -> UDbgResult<Arc<dyn UDbgBreakpoint>> {
Err(UDbgError::NotSupport)
}
fn get_breakpoint(&self, id: BpID) -> Option<Arc<dyn UDbgBreakpoint>> {
None
}
fn get_bp_by_address(&self, a: usize) -> Option<Arc<dyn UDbgBreakpoint>> {
self.get_breakpoint(a as BpID)
}
fn get_breakpoints(&self) -> Vec<Arc<dyn UDbgBreakpoint>> {
vec![]
}
}