aboutsummaryrefslogtreecommitdiff
path: root/src/stub/core_impl/breakpoints.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/stub/core_impl/breakpoints.rs')
-rw-r--r--src/stub/core_impl/breakpoints.rs100
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)
+ }
+}