aboutsummaryrefslogtreecommitdiff
path: root/src/stub/core_impl/base.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/stub/core_impl/base.rs')
-rw-r--r--src/stub/core_impl/base.rs138
1 files changed, 90 insertions, 48 deletions
diff --git a/src/stub/core_impl/base.rs b/src/stub/core_impl/base.rs
index e7d77dd..9110297 100644
--- a/src/stub/core_impl/base.rs
+++ b/src/stub/core_impl/base.rs
@@ -1,19 +1,26 @@
use super::prelude::*;
-use crate::protocol::commands::ext::Base;
-
-use crate::arch::{Arch, Registers};
-use crate::common::Tid;
-use crate::protocol::{IdKind, SpecificIdKind, SpecificThreadId};
-use crate::target::ext::base::{BaseOps, ResumeOps};
-use crate::{FAKE_PID, SINGLE_THREAD_TID};
-
use super::DisconnectReason;
+use crate::arch::Arch;
+use crate::arch::Registers;
+use crate::common::Pid;
+use crate::common::Tid;
+use crate::protocol::commands::ext::Base;
+use crate::protocol::IdKind;
+use crate::protocol::SpecificIdKind;
+use crate::protocol::SpecificThreadId;
+use crate::target::ext::base::BaseOps;
+use crate::target::ext::base::ResumeOps;
+use crate::FAKE_PID;
+use crate::SINGLE_THREAD_TID;
impl<T: Target, C: Connection> GdbStubImpl<T, C> {
#[inline(always)]
- fn get_sane_any_tid(&mut self, target: &mut T) -> Result<Tid, Error<T::Error, C::Error>> {
+ fn get_sane_any_tid(
+ &mut self,
+ target: &mut T,
+ ) -> Result<Option<Tid>, Error<T::Error, C::Error>> {
let tid = match target.base_ops() {
- BaseOps::SingleThread(_) => SINGLE_THREAD_TID,
+ BaseOps::SingleThread(_) => Some(SINGLE_THREAD_TID),
BaseOps::MultiThread(ops) => {
let mut first_tid = None;
ops.list_active_threads(&mut |tid| {
@@ -22,18 +29,58 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
}
})
.map_err(Error::TargetError)?;
- // Note that `Error::NoActiveThreads` shouldn't ever occur, since this method is
- // called from the `H` packet handler, which AFAIK is only sent after the GDB
- // client has confirmed that a thread / process exists.
- //
- // If it does, that really sucks, and will require rethinking how to handle "any
- // thread" messages.
- first_tid.ok_or(Error::NoActiveThreads)?
+ // It is possible for this to be `None` in the case where the target has
+ // not yet called `register_thread()`. This can happen, for example, if
+ // there are no active threads in the current target process.
+ first_tid
}
};
Ok(tid)
}
+ pub(crate) fn get_current_pid(
+ &mut self,
+ target: &mut T,
+ ) -> Result<Pid, Error<T::Error, C::Error>> {
+ if let Some(ops) = target
+ .support_extended_mode()
+ .and_then(|ops| ops.support_current_active_pid())
+ {
+ ops.current_active_pid().map_err(Error::TargetError)
+ } else {
+ Ok(FAKE_PID)
+ }
+ }
+
+ // Used by `?` and `vAttach` to return a "reasonable" stop reason.
+ //
+ // This is a bit of an implementation wart, since this is really something
+ // the user ought to be able to customize.
+ //
+ // Works fine for now though...
+ 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_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>,
@@ -66,11 +113,11 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
res.write_num(cmd.packet_buffer_len)?;
// these are the few features that gdbstub unconditionally supports
- res.write_str(concat!(
- ";vContSupported+",
- ";multiprocess+",
- ";QStartNoAckMode+",
- ))?;
+ res.write_str(concat!(";vContSupported+", ";multiprocess+",))?;
+
+ if target.use_no_ack_mode() {
+ res.write_str(";QStartNoAckMode+")?;
+ }
if let Some(resume_ops) = target.base_ops().resume_ops() {
let (reverse_cont, reverse_step) = match resume_ops {
@@ -150,27 +197,12 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
HandlerStatus::Handled
}
- Base::QStartNoAckMode(_) => {
- self.features.set_no_ack_mode(true);
- HandlerStatus::NeedsOk
- }
// -------------------- "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
- res.write_str("T05thread:")?;
- res.write_specific_thread_id(SpecificThreadId {
- pid: self
- .features
- .multiprocess()
- .then_some(SpecificIdKind::WithId(FAKE_PID)),
- tid: SpecificIdKind::WithId(self.get_sane_any_tid(target)?),
- })?;
- res.write_str(";")?;
- HandlerStatus::Handled
+ // TODO: Improve the '?' response.
+ // this will be particularly relevant when working on non-stop mode.
+ self.report_reasonable_stop_reason(res, target)?
}
Base::qAttached(cmd) => {
let is_attached = match target.support_extended_mode() {
@@ -238,7 +270,7 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
let addr = addr + NumCast::from(i).ok_or(Error::TargetMismatch)?;
let data = &mut buf[..chunk_size];
- match target.base_ops() {
+ let data_len = match target.base_ops() {
BaseOps::SingleThread(ops) => ops.read_addrs(addr, data),
BaseOps::MultiThread(ops) => {
ops.read_addrs(addr, data, self.current_mem_tid)
@@ -249,6 +281,8 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
n -= chunk_size;
i += chunk_size;
+ // TODO: add more specific error variant?
+ let data = data.get(..data_len).ok_or(Error::PacketBufferOverflow)?;
res.write_hex_buf(data)?;
}
HandlerStatus::Handled
@@ -302,17 +336,24 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
use crate::protocol::commands::_h_upcase::Op;
match cmd.kind {
Op::Other => match cmd.thread.tid {
- IdKind::Any => self.current_mem_tid = self.get_sane_any_tid(target)?,
+ IdKind::Any => match self.get_sane_any_tid(target)? {
+ Some(tid) => self.current_mem_tid = tid,
+ None => {
+ return Err(Error::NonFatalError(1));
+ }
+ },
// "All" threads doesn't make sense for memory accesses
IdKind::All => return Err(Error::PacketUnexpected),
IdKind::WithId(tid) => self.current_mem_tid = tid,
},
// technically, this variant is deprecated in favor of vCont...
Op::StepContinue => match cmd.thread.tid {
- IdKind::Any => {
- self.current_resume_tid =
- SpecificIdKind::WithId(self.get_sane_any_tid(target)?)
- }
+ IdKind::Any => match self.get_sane_any_tid(target)? {
+ Some(tid) => self.current_resume_tid = SpecificIdKind::WithId(tid),
+ None => {
+ return Err(Error::NonFatalError(1));
+ }
+ },
IdKind::All => self.current_resume_tid = SpecificIdKind::All,
IdKind::WithId(tid) => {
self.current_resume_tid = SpecificIdKind::WithId(tid)
@@ -323,13 +364,14 @@ impl<T: Target, C: Connection> GdbStubImpl<T, C> {
}
Base::qfThreadInfo(_) => {
res.write_str("m")?;
+ let pid = self.get_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) => {
@@ -346,7 +388,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(())