diff options
Diffstat (limited to 'src/stub/core_impl/breakpoints.rs')
-rw-r--r-- | src/stub/core_impl/breakpoints.rs | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/src/stub/core_impl/breakpoints.rs b/src/stub/core_impl/breakpoints.rs new file mode 100644 index 0000000..a54da71 --- /dev/null +++ b/src/stub/core_impl/breakpoints.rs @@ -0,0 +1,100 @@ +use super::prelude::*; +use crate::protocol::commands::ext::Breakpoints; + +use crate::arch::{Arch, BreakpointKind}; + +enum CmdKind { + Add, + Remove, +} + +impl<T: Target, C: Connection> GdbStubImpl<T, C> { + #[inline(always)] + fn handle_breakpoint_common( + &mut self, + ops: crate::target::ext::breakpoints::BreakpointsOps<'_, T>, + cmd: crate::protocol::commands::breakpoint::BasicBreakpoint<'_>, + cmd_kind: CmdKind, + ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { + let addr = + <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr).ok_or(Error::TargetMismatch)?; + + macro_rules! bp_kind { + () => { + BeBytes::from_be_bytes(cmd.kind) + .and_then(<T::Arch as Arch>::BreakpointKind::from_usize) + .ok_or(Error::TargetMismatch)? + }; + } + + let supported = match cmd.type_ { + 0 if ops.support_sw_breakpoint().is_some() => { + let ops = ops.support_sw_breakpoint().unwrap(); + let bp_kind = bp_kind!(); + match cmd_kind { + CmdKind::Add => ops.add_sw_breakpoint(addr, bp_kind), + CmdKind::Remove => ops.remove_sw_breakpoint(addr, bp_kind), + } + } + 1 if ops.support_hw_breakpoint().is_some() => { + let ops = ops.support_hw_breakpoint().unwrap(); + let bp_kind = bp_kind!(); + match cmd_kind { + CmdKind::Add => ops.add_hw_breakpoint(addr, bp_kind), + CmdKind::Remove => ops.remove_hw_breakpoint(addr, bp_kind), + } + } + 2 | 3 | 4 if ops.support_hw_watchpoint().is_some() => { + use crate::target::ext::breakpoints::WatchKind; + let kind = match cmd.type_ { + 2 => WatchKind::Write, + 3 => WatchKind::Read, + 4 => WatchKind::ReadWrite, + #[allow(clippy::unreachable)] // will be optimized out + _ => unreachable!(), + }; + let len = <T::Arch as Arch>::Usize::from_be_bytes(cmd.kind) + .ok_or(Error::TargetMismatch)?; + let ops = ops.support_hw_watchpoint().unwrap(); + match cmd_kind { + CmdKind::Add => ops.add_hw_watchpoint(addr, len, kind), + CmdKind::Remove => ops.remove_hw_watchpoint(addr, len, kind), + } + } + // explicitly handle unguarded variants of known breakpoint types + 0 | 1 | 2 | 3 | 4 => return Ok(HandlerStatus::Handled), + // warn if the GDB client ever sends a type outside the known types + other => { + warn!("unknown breakpoint type: {}", other); + return Ok(HandlerStatus::Handled); + } + }; + + match supported.handle_error()? { + true => Ok(HandlerStatus::NeedsOk), + false => Err(Error::NonFatalError(22)), + } + } + + pub(crate) fn handle_breakpoints( + &mut self, + _res: &mut ResponseWriter<'_, C>, + target: &mut T, + command: Breakpoints<'_>, + ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { + let ops = match target.support_breakpoints() { + Some(ops) => ops, + None => return Ok(HandlerStatus::Handled), + }; + + crate::__dead_code_marker!("breakpoints", "impl"); + + let handler_status = match command { + Breakpoints::z(cmd) => self.handle_breakpoint_common(ops, cmd, CmdKind::Remove)?, + Breakpoints::Z(cmd) => self.handle_breakpoint_common(ops, cmd, CmdKind::Add)?, + // TODO: handle ZWithBytecode once agent expressions are implemented + _ => HandlerStatus::Handled, + }; + Ok(handler_status) + } +} |