Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for QCatchSyscalls #57

Merged
merged 6 commits into from
May 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions examples/armv4t/gdb/catch_syscalls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use gdbstub::target;
use gdbstub::target::ext::catch_syscalls::SyscallNumbers;

use crate::gdb::Emu;

// This implementation is for illustrative purposes only. If the target doesn't
// support syscalls then there is no need to implement this extension

impl target::ext::catch_syscalls::CatchSyscalls for Emu {
fn enable_catch_syscalls(
&mut self,
filter: Option<SyscallNumbers<u32>>,
) -> target::TargetResult<(), Self> {
match filter {
Some(numbers) => eprintln!(
"Enabled catching syscalls: {:?}",
numbers.collect::<Vec<u32>>()
),
None => eprintln!("Enabled catching all syscalls"),
}
Ok(())
mchesser marked this conversation as resolved.
Show resolved Hide resolved
}

fn disable_catch_syscalls(&mut self) -> target::TargetResult<(), Self> {
eprintln!("Disabled catching syscalls");
Ok(())
}
}
6 changes: 6 additions & 0 deletions examples/armv4t/gdb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::emu::{Emu, Event};
// Additional GDB extensions

mod breakpoints;
mod catch_syscalls;
mod extended_mode;
mod memory_map;
mod monitor_cmd;
Expand Down Expand Up @@ -79,6 +80,11 @@ impl Target for Emu {
fn memory_map(&mut self) -> Option<target::ext::memory_map::MemoryMapOps<Self>> {
Some(self)
}

#[inline(always)]
fn catch_syscalls(&mut self) -> Option<target::ext::catch_syscalls::CatchSyscallsOps<Self>> {
Some(self)
}
}

impl Emu {
Expand Down
28 changes: 28 additions & 0 deletions src/gdbstub_impl/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
}
}

if target.catch_syscalls().is_some() {
res.write_str(";QCatchSyscalls+")?;
}

if T::Arch::target_description_xml().is_some()
|| target.target_description_xml_override().is_some()
{
Expand Down Expand Up @@ -655,6 +659,12 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
};
}

macro_rules! guard_catch_syscall {
() => {
target.catch_syscalls().is_some()
};
}

let status = match stop_reason {
ThreadStopReason::DoneStep | ThreadStopReason::GdbInterrupt => {
res.write_str("S05")?;
Expand Down Expand Up @@ -718,6 +728,21 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {

HandlerStatus::Handled
}
ThreadStopReason::CatchSyscall { number, position } if guard_catch_syscall!() => {
crate::__dead_code_marker!("catch_syscall", "stop_reason");

res.write_str("T05")?;

use crate::target::ext::catch_syscalls::CatchSyscallPosition;
res.write_str(match position {
CatchSyscallPosition::Entry => "syscall_entry:",
CatchSyscallPosition::Return => "syscall_return:",
})?;
res.write_num(number)?;
res.write_str(";")?;

HandlerStatus::Handled
}
_ => return Err(Error::UnsupportedStopReason),
};

Expand All @@ -742,6 +767,9 @@ impl<U> From<StopReason<U>> for ThreadStopReason<U> {
},
StopReason::Signal(sig) => ThreadStopReason::Signal(sig),
StopReason::ReplayLog(pos) => ThreadStopReason::ReplayLog(pos),
StopReason::CatchSyscall { number, position } => {
ThreadStopReason::CatchSyscall { number, position }
}
}
}
}
50 changes: 50 additions & 0 deletions src/gdbstub_impl/ext/catch_syscalls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use super::prelude::*;
use crate::{
arch::Arch,
protocol::commands::{_QCatchSyscalls::QCatchSyscalls, ext::CatchSyscalls},
target::ext::catch_syscalls::SyscallNumbers,
};

impl<T: Target, C: Connection> GdbStubImpl<T, C> {
pub(crate) fn handle_catch_syscalls(
&mut self,
_res: &mut ResponseWriter<C>,
target: &mut T,
command: CatchSyscalls,
) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
let ops = match target.catch_syscalls() {
Some(ops) => ops,
None => return Ok(HandlerStatus::Handled),
};

crate::__dead_code_marker!("catch_syscalls", "impl");

let handler_status = match command {
CatchSyscalls::QCatchSyscalls(cmd) => {
match cmd {
QCatchSyscalls::Disable => ops.disable_catch_syscalls().handle_error()?,
QCatchSyscalls::Enable(sysno) => {
let mut error = false;
let mut filter = sysno
.into_iter()
.map(|x| <T::Arch as Arch>::Usize::from_be_bytes(x))
.take_while(|x| {
error = x.is_none();
!error
})
.flatten();
ops.enable_catch_syscalls(Some(SyscallNumbers { inner: &mut filter }))
.handle_error()?;
if error {
return Err(Error::TargetMismatch);
}
}
QCatchSyscalls::EnableAll => ops.enable_catch_syscalls(None).handle_error()?,
}
HandlerStatus::NeedsOk
}
};

Ok(handler_status)
}
}
1 change: 1 addition & 0 deletions src/gdbstub_impl/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod prelude {

mod base;
mod breakpoints;
mod catch_syscalls;
mod extended_mode;
mod memory_map;
mod monitor_cmd;
Expand Down
1 change: 1 addition & 0 deletions src/gdbstub_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
self.handle_single_register_access(res, target, cmd)
}
Command::Breakpoints(cmd) => self.handle_breakpoints(res, target, cmd),
Command::CatchSyscalls(cmd) => self.handle_catch_syscalls(res, target, cmd),
Command::ExtendedMode(cmd) => self.handle_extended_mode(res, target, cmd),
Command::MonitorCmd(cmd) => self.handle_monitor_cmd(res, target, cmd),
Command::SectionOffsets(cmd) => self.handle_section_offsets(res, target, cmd),
Expand Down
5 changes: 5 additions & 0 deletions src/protocol/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub(self) mod prelude {
pub use super::ParseCommand;
pub use crate::common::*;
pub use crate::protocol::common::hex::{decode_hex, decode_hex_buf, is_hex, HexString};
pub use crate::protocol::common::lists;
pub use crate::protocol::common::thread_id::{
IdKind, SpecificIdKind, SpecificThreadId, ThreadId,
};
Expand Down Expand Up @@ -220,4 +221,8 @@ commands! {
memory_map {
"qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead,
}

catch_syscalls use 'a {
"QCatchSyscalls" => _QCatchSyscalls::QCatchSyscalls<'a>,
}
}
23 changes: 23 additions & 0 deletions src/protocol/commands/_QCatchSyscalls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use super::prelude::*;

#[derive(Debug)]
pub enum QCatchSyscalls<'a> {
Disable,
Enable(lists::ArgListHex<'a>),
EnableAll,
}

impl<'a> ParseCommand<'a> for QCatchSyscalls<'a> {
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
let body = buf.into_body();

match body {
[b':', b'0'] => Some(QCatchSyscalls::Disable),
[b':', b'1', b';', sysno @ ..] => Some(QCatchSyscalls::Enable(
lists::ArgListHex::from_packet(sysno)?,
)),
[b':', b'1'] => Some(QCatchSyscalls::EnableAll),
_ => None,
}
}
}
25 changes: 2 additions & 23 deletions src/protocol/commands/_vRun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,7 @@ use super::prelude::*;
#[derive(Debug)]
pub struct vRun<'a> {
pub filename: Option<&'a [u8]>,
pub args: Args<'a>,
}

#[derive(Debug)]
pub struct Args<'a>(&'a mut [u8]);

impl<'a> Args<'a> {
pub fn into_iter(self) -> impl Iterator<Item = &'a [u8]> + 'a {
self.0
.split_mut(|b| *b == b';')
// the `from_packet` method guarantees that the args are valid hex ascii, so this should
// method should never fail.
.map(|raw| decode_hex_buf(raw).unwrap_or(&mut []))
.map(|s| s as &[u8])
.filter(|s| !s.is_empty())
}
pub args: lists::ArgListHex<'a>,
}

impl<'a> ParseCommand<'a> for vRun<'a> {
Expand All @@ -34,15 +19,9 @@ impl<'a> ParseCommand<'a> for vRun<'a> {
};
let args = body.next().unwrap_or(&mut []); // args are optional

// validate that args have valid hex encoding (with ';' delimiters).
// this removes all the error handling from the lazy `Args` iterator.
if args.iter().any(|b| !(is_hex(*b) || *b == b';')) {
return None;
}

Some(vRun {
filename,
args: Args(args),
args: lists::ArgListHex::from_packet(args)?,
})
}
}
Expand Down
26 changes: 26 additions & 0 deletions src/protocol/common/lists.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use crate::protocol::common::hex::{decode_hex_buf, is_hex};

/// A wrapper type around a list of hex encoded arguments separated by `;`.
#[derive(Debug)]
pub struct ArgListHex<'a>(&'a mut [u8]);

impl<'a> ArgListHex<'a> {
pub fn from_packet(args: &'a mut [u8]) -> Option<Self> {
// validate that args have valid hex encoding (with ';' delimiters).
// this removes all the error handling from the lazy `Args` iterator.
if args.iter().any(|b| !(is_hex(*b) || *b == b';')) {
return None;
}
Some(Self(args))
}

pub fn into_iter(self) -> impl Iterator<Item = &'a [u8]> + 'a {
self.0
.split_mut(|b| *b == b';')
// the `from_packet` method guarantees that the args are valid hex ascii, so this should
// method should never fail.
.map(|raw| decode_hex_buf(raw).unwrap_or(&mut []))
.map(|s| s as &[u8])
.filter(|s| !s.is_empty())
}
}
1 change: 1 addition & 0 deletions src/protocol/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod hex;
pub mod lists;
pub mod thread_id;

/// Lightweight wrapper around `&[u8]` which denotes that the contained data is
Expand Down
12 changes: 12 additions & 0 deletions src/target/ext/base/multithread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use crate::arch::Arch;
use crate::common::*;
use crate::target::ext::breakpoints::WatchKind;
use crate::target::ext::catch_syscalls::CatchSyscallPosition;
use crate::target::{Target, TargetResult};

use super::{ReplayLogPosition, SingleRegisterAccessOps};
Expand Down Expand Up @@ -325,4 +326,15 @@ pub enum ThreadStopReason<U> {
/// further execution can be done. This stop reason tells GDB that this has
/// occurred.
ReplayLog(ReplayLogPosition),
/// The program has reached a syscall entry or return location.
///
/// Requires: [`CatchSyscalls`].
///
/// [`CatchSyscalls`]: crate::target::ext::catch_syscalls::CatchSyscalls
CatchSyscall {
/// The syscall number.
number: U,
/// The location the event occured at.
position: CatchSyscallPosition,
},
}
12 changes: 12 additions & 0 deletions src/target/ext/base/singlethread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use crate::arch::Arch;
use crate::target::ext::breakpoints::WatchKind;
use crate::target::ext::catch_syscalls::CatchSyscallPosition;
use crate::target::{Target, TargetResult};

use super::{ReplayLogPosition, SingleRegisterAccessOps};
Expand Down Expand Up @@ -236,4 +237,15 @@ pub enum StopReason<U> {
/// further execution can be done. This stop reason tells GDB that this has
/// occurred.
ReplayLog(ReplayLogPosition),
/// The program has reached a syscall entry or return location.
///
/// Requires: [`CatchSyscalls`].
///
/// [`CatchSyscalls`]: crate::target::ext::catch_syscalls::CatchSyscalls
CatchSyscall {
/// The syscall number.
number: U,
/// The location the event occured at.
position: CatchSyscallPosition,
},
}
54 changes: 54 additions & 0 deletions src/target/ext/catch_syscalls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! Enable or disable catching syscalls from the inferior process.

use crate::arch::Arch;
use crate::target::{Target, TargetResult};

/// Target Extension - Enable and disable catching syscalls from the inferior
/// process.
///
mchesser marked this conversation as resolved.
Show resolved Hide resolved
/// Implementing this extension allows the target to support the `catch syscall`
/// GDB client command. See [GDB documentation](https://sourceware.org/gdb/onlinedocs/gdb/Set-Catchpoints.html)
/// for further details.
///
/// Corresponds to GDB's [`QCatchSyscalls`](https://sourceware.org/gdb/current/onlinedocs/gdb/General-Query-Packets.html#QCatchSyscalls) command.
pub trait CatchSyscalls: Target {
/// Enables catching syscalls from the inferior process.
///
/// If `filter` is `None`, then all syscalls should be reported to GDB. If a
/// filter is provided, only the syscalls listed in the filter should be
/// reported to GDB.
///
/// Note: filters are not combined, subsequent calls this method should
/// replace any existing syscall filtering.
fn enable_catch_syscalls(
&mut self,
filter: Option<SyscallNumbers<<Self::Arch as Arch>::Usize>>,
) -> TargetResult<(), Self>;

/// Disables catching syscalls from the inferior process.
fn disable_catch_syscalls(&mut self) -> TargetResult<(), Self>;
}

define_ext!(CatchSyscallsOps, CatchSyscalls);

/// Describes where the syscall catchpoint was triggered at.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CatchSyscallPosition {
/// Reached the entry location of the syscall.
Entry,
/// Reached the return location of the syscall.
Return,
}

/// Iterator of syscall numbers that should be reported to GDB.
pub struct SyscallNumbers<'a, U> {
pub(crate) inner: &'a mut dyn Iterator<Item = U>,
}

impl<U> Iterator for SyscallNumbers<'_, U> {
type Item = U;

fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
1 change: 1 addition & 0 deletions src/target/ext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ macro_rules! define_ext {

pub mod base;
pub mod breakpoints;
pub mod catch_syscalls;
pub mod extended_mode;
pub mod memory_map;
pub mod monitor_cmd;
Expand Down
Loading