[−][src]Trait gdbstub::Target
A collection of methods and metadata a GdbStub
can use to control and
debug a system.
There are several provided methods that can optionally be implemented to enable additional advanced GDB debugging functionality.
Handling Threads on Bare-Metal Hardware
On bare-metal targets (such as microcontrollers or emulators), it's common to treat individual CPU cores as a separate "threads". e.g: in a dual-core system, [CPU0, CPU1] might be mapped to [TID1, TID2] (note that TIDs cannot be zero).
What's with the <Self::Arch as Arch>::
syntax?
Yeah, sorry about that!
If rust-lang/rust#38078
every gets fixed, <Self::Arch as Arch>::Foo
will be simplified to just
Self::Arch::Foo
.
Until then, when implementing Target
, it's recommended to use the concrete
type directly. e.g: on a 32-bit platform, instead of writing <Self::Arch as Arch>::Usize
, use u32
directly.
Associated Types
Loading content...Required methods
fn resume(
&mut self,
actions: Actions<'_>,
check_gdb_interrupt: &mut dyn FnMut() -> bool
) -> Result<(Tid, StopReason<<Self::Arch as Arch>::Usize>), Self::Error>
&mut self,
actions: Actions<'_>,
check_gdb_interrupt: &mut dyn FnMut() -> bool
) -> Result<(Tid, StopReason<<Self::Arch as Arch>::Usize>), Self::Error>
Resume execution on the target.
actions
specifies how various threads should be resumed (i.e:
single-step vs. resume). It is guaranteed to contain at least one
action.
The check_gdb_interrupt
callback can be invoked to check if GDB sent
an Interrupt packet (i.e: the user pressed Ctrl-C). It's recommended to
invoke this callback every-so-often while the system is running (e.g:
every X cycles/milliseconds). Periodically checking for incoming
interrupt packets is not required, but it is recommended.
Implementation requirements
These requirements cannot be satisfied by gdbstub
internally, and must
be handled on a per-target basis.
Adjusting PC after a breakpoint is hit
The GDB remote serial protocol documentation notes the following:
On some architectures, such as x86, at the architecture level, when a breakpoint instruction executes the program counter points at the breakpoint address plus an offset. On such targets, the stub is responsible for adjusting the PC to point back at the breakpoint address.
Omitting PC adjustment may result in unexpected execution flow and/or breakpoints not appearing to work correctly.
Kinds of Targets
Single-Threaded Targets
For single-threaded Target's (i.e: those that have not implemented any
(optional|multithreading) features), it's safe to ignore the
TidSelector
component of the actions
iterator entirely. Moreover,
it's safe to assume that there will only ever be a single action
returned by the actions
iterator. As such, the following snippet
should never panic:
let (_, action) = actions.next().unwrap();
Lastly, when returning a (Tid, StopReason)
pair, the
gdbstub::SINGLE_THREAD_TID
constant
should be used for the Tid
field.
Multi-Threaded Targets
If a Target ever lists more than one thread as active in
list_active_threads
, gdbstub
switches to multithreaded mode. In
this mode, the actions
iterator may return more than one action
.
At the moment, gdbstub
only supports GDB's
"All-Stop" mode,
whereby all threads should be stopped when returning from resume
(not just the thread responsible for the StopReason
).
Bare-Metal Targets
See the section above on how to use "threads" on bare-metal (threadless) targets to debug individual CPU cores.
fn read_registers(
&mut self,
regs: &mut <Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
&mut self,
regs: &mut <Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
Read the target's registers.
On multi-threaded systems, this method must respect the currently
selected thread (set via the set_current_thread
method).
fn write_registers(
&mut self,
regs: &<Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
&mut self,
regs: &<Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
Write the target's registers.
On multi-threaded systems, this method must respect the currently
selected thread (set via the set_current_thread
method).
fn read_addrs(
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &mut [u8]
) -> Result<bool, Self::Error>
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &mut [u8]
) -> Result<bool, Self::Error>
Read bytes from the specified address range.
Handling non-fatal invalid memory reads
If the requested address range could not be accessed (e.g: due to
MMU protection, unhanded page fault, etc...), return Ok(false)
to
signal that the requested memory could not be read.
As a reminder, Err(Self::Error)
should only be returned if a memory
read results in a fatal target error.
fn write_addrs(
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &[u8]
) -> Result<bool, Self::Error>
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &[u8]
) -> Result<bool, Self::Error>
Write bytes to the specified address range.
Handling non-fatal invalid memory writes
If the requested address range could not be accessed (e.g: due to
MMU protection, unhanded page fault, etc...), return Ok(false)
to
signal that the requested memory could not be written to.
As a reminder, Err(Self::Error)
should only be returned if a memory
write results in a fatal target error.
fn update_sw_breakpoint(
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> Result<bool, Self::Error>
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> Result<bool, Self::Error>
Set/remove a software breakpoint.
Return Ok(false)
if the operation could not be completed.
See this stackoverflow discussion about the differences between hardware and software breakpoints.
Author's recommendation: If you're implementing Target
for an
emulator using an interpreted CPU (as opposed to a JIT), the
simplest way to implement "software" breakpoints is to check the
PC
value after each CPU cycle.
Provided methods
fn read_register(
&mut self,
reg_id: <<Self::Arch as Arch>::Registers as Registers>::RegId,
dst: &mut [u8]
) -> OptResult<(), Self::Error>
&mut self,
reg_id: <<Self::Arch as Arch>::Registers as Registers>::RegId,
dst: &mut [u8]
) -> OptResult<(), Self::Error>
Read a single register on the target.
Implementations should write the value of the register using target's
native byte order in the buffer dst
.
On multi-threaded systems, this method must respect the currently
selected thread (set via the set_current_thread
method).
fn write_register(
&mut self,
reg_id: <<Self::Arch as Arch>::Registers as Registers>::RegId,
val: &[u8]
) -> OptResult<(), Self::Error>
&mut self,
reg_id: <<Self::Arch as Arch>::Registers as Registers>::RegId,
val: &[u8]
) -> OptResult<(), Self::Error>
Write a single register on the target.
The val
buffer contains the new value of the register in the target's
native byte order.
On multi-threaded systems, this method must respect the currently
selected thread (set via the set_current_thread
method).
fn update_hw_breakpoint(
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> OptResult<bool, Self::Error>
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> OptResult<bool, Self::Error>
(optional) Set/remove a hardware breakpoint.
Return Ok(false)
if the operation could not be completed.
See this stackoverflow discussion about the differences between hardware and software breakpoints.
Author's recommendation: If you're implementing Target
for an
emulator using an interpreted CPU (as opposed to a JIT), there
shouldn't be any reason to implement this method (as software
breakpoints are likely to be just-as-fast).
fn update_hw_watchpoint(
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp,
kind: WatchKind
) -> OptResult<bool, Self::Error>
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp,
kind: WatchKind
) -> OptResult<bool, Self::Error>
(optional) Set/remove a hardware watchpoint.
Return Ok(false)
if the operation could not be completed.
See the GDB documentation regarding watchpoints for how they're supposed to work.
NOTE: If this method isn't implemented, GDB will default to using software watchpoints, which tend to be excruciatingly slow (as they are implemented by single-stepping the system, and reading the watched memory location after each step).
fn handle_monitor_cmd(
&mut self,
cmd: &[u8],
out: ConsoleOutput<'_>
) -> OptResult<(), Self::Error>
&mut self,
cmd: &[u8],
out: ConsoleOutput<'_>
) -> OptResult<(), Self::Error>
(optional) Handle custom commands sent using the monitor
command.
The GDB remote serial protocol includes a built-in mechanism to send
arbitrary commands to the remote stub: the monitor
command. For
example, running monitor dbg
from the GDB client will invoke
handle_monitor_cmd
with cmd = b"dbg"
.
Commands are not guaranteed to be valid UTF-8, hence the use of
&[u8]
as opposed to &str
.
Intermediate console output can be written back to the GDB client using
the provided ConsoleOutput
object + the
gdbstub::output!
macro.
Note: The maximum length of incoming commands is limited by the size
of the packet buffer provided to the GdbStub
.
Specifically, commands can only be up to (buf.len() - 10) / 2
bytes.
fn list_active_threads(
&mut self,
thread_is_active: &mut dyn FnMut(Tid)
) -> Result<(), Self::Error>
&mut self,
thread_is_active: &mut dyn FnMut(Tid)
) -> Result<(), Self::Error>
(optional|multithreading) List all currently active threads.
See the section above on implementing thread-related methods on bare-metal (threadless) targets.
fn set_current_thread(&mut self, tid: Tid) -> OptResult<(), Self::Error>
(optional|multithreading) Select a specific thread to perform subsequent operations on (e.g: read/write registers, access memory, etc...)
This method must be implemented if list_active_threads
ever
returns more than one thread!
fn is_thread_alive(&mut self, tid: Tid) -> OptResult<bool, Self::Error>
(optional|multithreading) Check if the specified thread is alive.
As a convenience, this method provides a default implementation which
uses list_active_threads
to do a linear-search through all active
threads. On thread-heavy systems, it may be more efficient
to override this method with a more direct query.
Trait Implementations
impl<A, E, '_> Target for &'_ mut dyn Target<Arch = A, Error = E> where
A: Arch,
[src]
A: Arch,
type Arch = A
The target's architecture.
type Error = E
A target-specific fatal error.
fn resume(
&mut self,
actions: Actions<'_>,
check_gdb_interrupt: &mut dyn FnMut() -> bool
) -> Result<(Tid, StopReason<<Self::Arch as Arch>::Usize>), Self::Error>
[src]
&mut self,
actions: Actions<'_>,
check_gdb_interrupt: &mut dyn FnMut() -> bool
) -> Result<(Tid, StopReason<<Self::Arch as Arch>::Usize>), Self::Error>
fn read_register(
&mut self,
reg_number: <<Self::Arch as Arch>::Registers as Registers>::RegId,
dst: &mut [u8]
) -> OptResult<(), Self::Error>
[src]
&mut self,
reg_number: <<Self::Arch as Arch>::Registers as Registers>::RegId,
dst: &mut [u8]
) -> OptResult<(), Self::Error>
fn read_registers(
&mut self,
regs: &mut <Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
[src]
&mut self,
regs: &mut <Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
fn write_registers(
&mut self,
regs: &<Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
[src]
&mut self,
regs: &<Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
fn write_register(
&mut self,
reg_number: <<Self::Arch as Arch>::Registers as Registers>::RegId,
val: &[u8]
) -> OptResult<(), Self::Error>
[src]
&mut self,
reg_number: <<Self::Arch as Arch>::Registers as Registers>::RegId,
val: &[u8]
) -> OptResult<(), Self::Error>
fn read_addrs(
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &mut [u8]
) -> Result<bool, Self::Error>
[src]
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &mut [u8]
) -> Result<bool, Self::Error>
fn write_addrs(
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &[u8]
) -> Result<bool, Self::Error>
[src]
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &[u8]
) -> Result<bool, Self::Error>
fn update_sw_breakpoint(
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> Result<bool, Self::Error>
[src]
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> Result<bool, Self::Error>
fn update_hw_breakpoint(
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> OptResult<bool, Self::Error>
[src]
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> OptResult<bool, Self::Error>
fn update_hw_watchpoint(
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp,
kind: WatchKind
) -> OptResult<bool, Self::Error>
[src]
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp,
kind: WatchKind
) -> OptResult<bool, Self::Error>
fn handle_monitor_cmd(
&mut self,
cmd: &[u8],
out: ConsoleOutput<'_>
) -> OptResult<(), Self::Error>
[src]
&mut self,
cmd: &[u8],
out: ConsoleOutput<'_>
) -> OptResult<(), Self::Error>
fn list_active_threads(
&mut self,
thread_is_active: &mut dyn FnMut(Tid)
) -> Result<(), Self::Error>
[src]
&mut self,
thread_is_active: &mut dyn FnMut(Tid)
) -> Result<(), Self::Error>
fn set_current_thread(&mut self, tid: Tid) -> OptResult<(), Self::Error>
[src]
fn is_thread_alive(&mut self, tid: Tid) -> OptResult<bool, Self::Error>
[src]
Implementations on Foreign Types
impl<A, E> Target for Box<dyn Target<Arch = A, Error = E>> where
A: Arch,
[src]
A: Arch,
type Arch = A
type Error = E
fn resume(
&mut self,
actions: Actions<'_>,
check_gdb_interrupt: &mut dyn FnMut() -> bool
) -> Result<(Tid, StopReason<<Self::Arch as Arch>::Usize>), Self::Error>
[src]
&mut self,
actions: Actions<'_>,
check_gdb_interrupt: &mut dyn FnMut() -> bool
) -> Result<(Tid, StopReason<<Self::Arch as Arch>::Usize>), Self::Error>
fn read_register(
&mut self,
reg_number: <<Self::Arch as Arch>::Registers as Registers>::RegId,
dst: &mut [u8]
) -> OptResult<(), Self::Error>
[src]
&mut self,
reg_number: <<Self::Arch as Arch>::Registers as Registers>::RegId,
dst: &mut [u8]
) -> OptResult<(), Self::Error>
fn read_registers(
&mut self,
regs: &mut <Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
[src]
&mut self,
regs: &mut <Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
fn write_registers(
&mut self,
regs: &<Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
[src]
&mut self,
regs: &<Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
fn write_register(
&mut self,
reg_number: <<Self::Arch as Arch>::Registers as Registers>::RegId,
val: &[u8]
) -> OptResult<(), Self::Error>
[src]
&mut self,
reg_number: <<Self::Arch as Arch>::Registers as Registers>::RegId,
val: &[u8]
) -> OptResult<(), Self::Error>
fn read_addrs(
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &mut [u8]
) -> Result<bool, Self::Error>
[src]
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &mut [u8]
) -> Result<bool, Self::Error>
fn write_addrs(
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &[u8]
) -> Result<bool, Self::Error>
[src]
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &[u8]
) -> Result<bool, Self::Error>
fn update_sw_breakpoint(
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> Result<bool, Self::Error>
[src]
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> Result<bool, Self::Error>
fn update_hw_breakpoint(
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> OptResult<bool, Self::Error>
[src]
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> OptResult<bool, Self::Error>
fn update_hw_watchpoint(
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp,
kind: WatchKind
) -> OptResult<bool, Self::Error>
[src]
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp,
kind: WatchKind
) -> OptResult<bool, Self::Error>
fn handle_monitor_cmd(
&mut self,
cmd: &[u8],
out: ConsoleOutput<'_>
) -> OptResult<(), Self::Error>
[src]
&mut self,
cmd: &[u8],
out: ConsoleOutput<'_>
) -> OptResult<(), Self::Error>
fn list_active_threads(
&mut self,
thread_is_active: &mut dyn FnMut(Tid)
) -> Result<(), Self::Error>
[src]
&mut self,
thread_is_active: &mut dyn FnMut(Tid)
) -> Result<(), Self::Error>
fn set_current_thread(&mut self, tid: Tid) -> OptResult<(), Self::Error>
[src]
fn is_thread_alive(&mut self, tid: Tid) -> OptResult<bool, Self::Error>
[src]
Implementors
impl<A, E, '_> Target for &'_ mut dyn Target<Arch = A, Error = E> where
A: Arch,
[src]
A: Arch,
type Arch = A
type Error = E
fn resume(
&mut self,
actions: Actions<'_>,
check_gdb_interrupt: &mut dyn FnMut() -> bool
) -> Result<(Tid, StopReason<<Self::Arch as Arch>::Usize>), Self::Error>
[src]
&mut self,
actions: Actions<'_>,
check_gdb_interrupt: &mut dyn FnMut() -> bool
) -> Result<(Tid, StopReason<<Self::Arch as Arch>::Usize>), Self::Error>
fn read_register(
&mut self,
reg_number: <<Self::Arch as Arch>::Registers as Registers>::RegId,
dst: &mut [u8]
) -> OptResult<(), Self::Error>
[src]
&mut self,
reg_number: <<Self::Arch as Arch>::Registers as Registers>::RegId,
dst: &mut [u8]
) -> OptResult<(), Self::Error>
fn read_registers(
&mut self,
regs: &mut <Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
[src]
&mut self,
regs: &mut <Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
fn write_registers(
&mut self,
regs: &<Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
[src]
&mut self,
regs: &<Self::Arch as Arch>::Registers
) -> Result<(), Self::Error>
fn write_register(
&mut self,
reg_number: <<Self::Arch as Arch>::Registers as Registers>::RegId,
val: &[u8]
) -> OptResult<(), Self::Error>
[src]
&mut self,
reg_number: <<Self::Arch as Arch>::Registers as Registers>::RegId,
val: &[u8]
) -> OptResult<(), Self::Error>
fn read_addrs(
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &mut [u8]
) -> Result<bool, Self::Error>
[src]
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &mut [u8]
) -> Result<bool, Self::Error>
fn write_addrs(
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &[u8]
) -> Result<bool, Self::Error>
[src]
&mut self,
start_addr: <Self::Arch as Arch>::Usize,
data: &[u8]
) -> Result<bool, Self::Error>
fn update_sw_breakpoint(
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> Result<bool, Self::Error>
[src]
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> Result<bool, Self::Error>
fn update_hw_breakpoint(
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> OptResult<bool, Self::Error>
[src]
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp
) -> OptResult<bool, Self::Error>
fn update_hw_watchpoint(
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp,
kind: WatchKind
) -> OptResult<bool, Self::Error>
[src]
&mut self,
addr: <Self::Arch as Arch>::Usize,
op: BreakOp,
kind: WatchKind
) -> OptResult<bool, Self::Error>
fn handle_monitor_cmd(
&mut self,
cmd: &[u8],
out: ConsoleOutput<'_>
) -> OptResult<(), Self::Error>
[src]
&mut self,
cmd: &[u8],
out: ConsoleOutput<'_>
) -> OptResult<(), Self::Error>
fn list_active_threads(
&mut self,
thread_is_active: &mut dyn FnMut(Tid)
) -> Result<(), Self::Error>
[src]
&mut self,
thread_is_active: &mut dyn FnMut(Tid)
) -> Result<(), Self::Error>