Skip to content

Commit

Permalink
vAttach: get vAttach working with multi-process (#129)
Browse files Browse the repository at this point in the history
* vAttach: get vAttach working with multi-process

Get vAttach working to the point where it is now possible to switch
processes by running `attach [PID]` in GDB.

Signed-off-by: Sean Cross <sean@xobs.io>

* base: move `report_stop_reason` into its own function

Create a central place for reporting the stop reason. This will be used
when GDB issues a `QuestionMark` packet.

Signed-off-by: Sean Cross <sean@xobs.io>

* extended_mode: use `report_stop_reason` for vAttach

When attaching to a process, report the stop reason using the new
`report_stop_reason()` function. This enables attaching to processes
without hardcoding the `S00` response.

Signed-off-by: Sean Cross <sean@xobs.io>

* base: qC: return self.current_mem_tid if it's set

If self.current_mem_tid is set to a value other than the default
sentinal value, return the current thread ID.

Otherwise, look through the list of active threads and return the first
active one.

Signed-off-by: Sean Cross <sean@xobs.io>

---------

Signed-off-by: Sean Cross <sean@xobs.io>
  • Loading branch information
xobs committed Mar 22, 2023
1 parent 4627ad7 commit bf4ad90
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 33 deletions.
1 change: 1 addition & 0 deletions src/protocol/commands.rs
Expand Up @@ -228,6 +228,7 @@ commands! {
"M" => _m_upcase::M<'a>,
"qAttached" => _qAttached::qAttached,
"qfThreadInfo" => _qfThreadInfo::qfThreadInfo,
"qC" => _qC::qC,
"QStartNoAckMode" => _QStartNoAckMode::QStartNoAckMode,
"qsThreadInfo" => _qsThreadInfo::qsThreadInfo,
"qSupported" => _qSupported::qSupported<'a>,
Expand Down
14 changes: 14 additions & 0 deletions src/protocol/commands/_qC.rs
@@ -0,0 +1,14 @@
use super::prelude::*;

#[derive(Debug)]
pub struct qC;

impl<'a> ParseCommand<'a> for qC {
#[inline(always)]
fn from_packet(buf: PacketBuf<'a>) -> Option<Self> {
if !buf.into_body().is_empty() {
return None;
}
Some(qC)
}
}
113 changes: 92 additions & 21 deletions src/stub/core_impl/base.rs
Expand Up @@ -2,7 +2,7 @@ use super::prelude::*;
use crate::protocol::commands::ext::Base;

use crate::arch::{Arch, Registers};
use crate::common::Tid;
use crate::common::{Pid, Tid};
use crate::protocol::{IdKind, SpecificIdKind, SpecificThreadId};
use crate::target::ext::base::{BaseOps, ResumeOps};
use crate::{FAKE_PID, SINGLE_THREAD_TID};
Expand Down Expand Up @@ -34,6 +34,40 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
Ok(tid)
}

pub(crate) fn get_sane_current_pid(
&mut self,
target: &mut T,
) -> Result<Pid, Error<T::Error, C::Error>> {
match target.base_ops() {
BaseOps::SingleThread(_) => Ok(FAKE_PID),
BaseOps::MultiThread(ops) => ops.active_pid().map_err(Error::TargetError),
}
}

#[inline(always)]
pub(crate) fn report_reasonable_stop_reason(
&mut self,
res: &mut ResponseWriter<'_, C>,
target: &mut T,
) -> Result<HandlerStatus, Error<T::Error, C::Error>> {
// Reply with a valid thread-id or GDB issues a warning when more
// than one thread is active
if let Some(tid) = self.get_sane_any_tid(target)? {
res.write_str("T05thread:")?;
res.write_specific_thread_id(SpecificThreadId {
pid: self
.features
.multiprocess()
.then_some(SpecificIdKind::WithId(self.get_sane_current_pid(target)?)),
tid: SpecificIdKind::WithId(tid),
})?;
} else {
res.write_str("W00")?;
}
res.write_str(";")?;
Ok(HandlerStatus::Handled)
}

pub(crate) fn handle_base(
&mut self,
res: &mut ResponseWriter<'_, C>,
Expand Down Expand Up @@ -158,24 +192,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
// -------------------- "Core" Functionality -------------------- //
// TODO: Improve the '?' response based on last-sent stop reason.
// this will be particularly relevant when working on non-stop mode.
Base::QuestionMark(_) => {
// Reply with a valid thread-id or GDB issues a warning when more
// than one thread is active
if let Some(tid) = self.get_sane_any_tid(target)? {
res.write_str("T05thread:")?;
res.write_specific_thread_id(SpecificThreadId {
pid: self
.features
.multiprocess()
.then_some(SpecificIdKind::WithId(FAKE_PID)),
tid: SpecificIdKind::WithId(tid),
})?;
} else {
res.write_str("W00")?;
}
res.write_str(";")?;
HandlerStatus::Handled
}
Base::QuestionMark(_) => self.report_reasonable_stop_reason(res, target)?,
Base::qAttached(cmd) => {
let is_attached = match target.support_extended_mode() {
// when _not_ running in extended mode, just report that we're attaching to an
Expand Down Expand Up @@ -332,15 +349,69 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
}
HandlerStatus::NeedsOk
}
Base::qC(_) => {
res.write_str("QC")?;
let pid = self.get_sane_current_pid(target)?;

match target.base_ops() {
BaseOps::SingleThread(_) => res.write_specific_thread_id(SpecificThreadId {
pid: self
.features
.multiprocess()
.then_some(SpecificIdKind::WithId(pid)),
tid: SpecificIdKind::WithId(SINGLE_THREAD_TID),
})?,
BaseOps::MultiThread(ops) => {
if self.current_mem_tid == SINGLE_THREAD_TID {
let mut err: Result<_, Error<T::Error, C::Error>> = Ok(());
let mut first = true;
ops.list_active_threads(&mut |tid| {
// TODO: replace this with a try block (once stabilized)
let e = (|| {
if !first {
return Ok(());
}
first = false;
res.write_specific_thread_id(SpecificThreadId {
pid: self
.features
.multiprocess()
.then_some(SpecificIdKind::WithId(pid)),
tid: SpecificIdKind::WithId(tid),
})?;
Ok(())
})();

if let Err(e) = e {
err = Err(e)
}
})
.map_err(Error::TargetError)?;
err?
} else {
res.write_specific_thread_id(SpecificThreadId {
pid: self
.features
.multiprocess()
.then_some(SpecificIdKind::WithId(pid)),
tid: SpecificIdKind::WithId(self.current_mem_tid),
})?;
}
}
}

HandlerStatus::Handled
}
Base::qfThreadInfo(_) => {
res.write_str("m")?;
let pid = self.get_sane_current_pid(target)?;

match target.base_ops() {
BaseOps::SingleThread(_) => res.write_specific_thread_id(SpecificThreadId {
pid: self
.features
.multiprocess()
.then_some(SpecificIdKind::WithId(FAKE_PID)),
.then_some(SpecificIdKind::WithId(pid)),
tid: SpecificIdKind::WithId(SINGLE_THREAD_TID),
})?,
BaseOps::MultiThread(ops) => {
Expand All @@ -357,7 +428,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
pid: self
.features
.multiprocess()
.then_some(SpecificIdKind::WithId(FAKE_PID)),
.then_some(SpecificIdKind::WithId(pid)),
tid: SpecificIdKind::WithId(tid),
})?;
Ok(())
Expand Down
4 changes: 1 addition & 3 deletions src/stub/core_impl/extended_mode.rs
Expand Up @@ -26,9 +26,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
}
ExtendedMode::vAttach(cmd) => {
ops.attach(cmd.pid).handle_error()?;

// TODO: sends OK when running in Non-Stop mode
HandlerStatus::Handled
self.report_reasonable_stop_reason(res, target)?
}
ExtendedMode::vRun(cmd) => {
use crate::target::ext::extended_mode::Args;
Expand Down
16 changes: 8 additions & 8 deletions src/stub/core_impl/resume.rs
Expand Up @@ -9,7 +9,6 @@ use crate::stub::MultiThreadStopReason;
use crate::target::ext::base::reverse_exec::ReplayLogPosition;
use crate::target::ext::base::ResumeOps;
use crate::target::ext::catch_syscalls::CatchSyscallPosition;
use crate::FAKE_PID;

use super::DisconnectReason;

Expand Down Expand Up @@ -257,6 +256,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
fn write_stop_common(
&mut self,
res: &mut ResponseWriter<'_, C>,
target: &mut T,
tid: Option<Tid>,
signal: Signal,
) -> Result<(), Error<T::Error, C::Error>> {
Expand All @@ -272,7 +272,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
pid: self
.features
.multiprocess()
.then_some(SpecificIdKind::WithId(FAKE_PID)),
.then_some(SpecificIdKind::WithId(self.get_sane_current_pid(target)?)),
tid: SpecificIdKind::WithId(tid),
})?;
res.write_str(";")?;
Expand Down Expand Up @@ -345,20 +345,20 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
FinishExecStatus::Disconnect(DisconnectReason::TargetTerminated(sig))
}
MultiThreadStopReason::SignalWithThread { tid, signal } => {
self.write_stop_common(res, Some(tid), signal)?;
self.write_stop_common(res, target, Some(tid), signal)?;
FinishExecStatus::Handled
}
MultiThreadStopReason::SwBreak(tid) if guard_break!(support_sw_breakpoint) => {
crate::__dead_code_marker!("sw_breakpoint", "stop_reason");

self.write_stop_common(res, Some(tid), Signal::SIGTRAP)?;
self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?;
res.write_str("swbreak:;")?;
FinishExecStatus::Handled
}
MultiThreadStopReason::HwBreak(tid) if guard_break!(support_hw_breakpoint) => {
crate::__dead_code_marker!("hw_breakpoint", "stop_reason");

self.write_stop_common(res, Some(tid), Signal::SIGTRAP)?;
self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?;
res.write_str("hwbreak:;")?;
FinishExecStatus::Handled
}
Expand All @@ -367,7 +367,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
{
crate::__dead_code_marker!("hw_watchpoint", "stop_reason");

self.write_stop_common(res, Some(tid), Signal::SIGTRAP)?;
self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?;

use crate::target::ext::breakpoints::WatchKind;
match kind {
Expand All @@ -382,7 +382,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
MultiThreadStopReason::ReplayLog { tid, pos } if guard_reverse_exec!() => {
crate::__dead_code_marker!("reverse_exec", "stop_reason");

self.write_stop_common(res, tid, Signal::SIGTRAP)?;
self.write_stop_common(res, target, tid, Signal::SIGTRAP)?;

res.write_str("replaylog:")?;
res.write_str(match pos {
Expand All @@ -400,7 +400,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
} if guard_catch_syscall!() => {
crate::__dead_code_marker!("catch_syscall", "stop_reason");

self.write_stop_common(res, tid, Signal::SIGTRAP)?;
self.write_stop_common(res, target, tid, Signal::SIGTRAP)?;

res.write_str(match position {
CatchSyscallPosition::Entry => "syscall_entry:",
Expand Down
23 changes: 22 additions & 1 deletion src/target/ext/base/multithread.rs
Expand Up @@ -2,8 +2,9 @@

use crate::arch::Arch;
use crate::common::Signal;
use crate::common::Tid;
use crate::common::{Pid, Tid};
use crate::target::{Target, TargetResult};
use crate::FAKE_PID;

/// Base required debugging operations for multi threaded targets.
pub trait MultiThreadBase: Target {
Expand Down Expand Up @@ -110,6 +111,26 @@ pub trait MultiThreadBase: Target {
) -> Option<crate::target::ext::thread_extra_info::ThreadExtraInfoOps<'_, Self>> {
None
}

/// Override the reported PID when running in multithread mode (default: 1)
///
/// When implementing gdbstub on a platform that supports multiple
/// processes, the active PID needs to match the attached process.
/// Failing to do so will cause GDB to fail to attach to the target
/// process.
///
/// This should reflect the currently-debugged process which should be
/// updated when switching processes after calling [`attach()`].
///
/// This function is only useful if your target implements multiple
/// processes.
///
/// [`attach()`]:
/// crate::target::ext::extended_mode::ExtendedMode::attach
#[inline(always)]
fn active_pid(&mut self) -> Result<Pid, Self::Error> {
Ok(FAKE_PID)
}
}

/// Target extension - support for resuming multi threaded targets.
Expand Down

0 comments on commit bf4ad90

Please sign in to comment.