diff --git a/src/stub/core_impl/base.rs b/src/stub/core_impl/base.rs index b42e1f0d..834574d2 100644 --- a/src/stub/core_impl/base.rs +++ b/src/stub/core_impl/base.rs @@ -119,6 +119,18 @@ impl GdbStubImpl { res.write_str(";QStartNoAckMode+")?; } + if target.support_fork_events() { + res.write_str(";fork-events+")?; + } + + if target.support_vfork_events() { + res.write_str(";vfork-events+")?; + } + + if target.support_vforkdone_events() { + res.write_str(";vforkdone-events+")?; + } + if let Some(resume_ops) = target.base_ops().resume_ops() { let (reverse_cont, reverse_step) = match resume_ops { ResumeOps::MultiThread(ops) => ( diff --git a/src/stub/core_impl/resume.rs b/src/stub/core_impl/resume.rs index 38a046c4..f02d7aff 100644 --- a/src/stub/core_impl/resume.rs +++ b/src/stub/core_impl/resume.rs @@ -337,6 +337,24 @@ impl GdbStubImpl { }; } + macro_rules! guard_fork_events { + () => { + target.support_fork_events() + }; + } + + macro_rules! guard_vfork_events { + () => { + target.support_vfork_events() + }; + } + + macro_rules! guard_vforkdone_events { + () => { + target.support_vforkdone_events() + }; + } + let status = match stop_reason { MultiThreadStopReason::DoneStep => { res.write_str("S")?; @@ -425,13 +443,55 @@ impl GdbStubImpl { FinishExecStatus::Handled } + MultiThreadStopReason::Library(tid) => { + self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?; + res.write_str("library;")?; + FinishExecStatus::Handled + } + MultiThreadStopReason::Fork { cur_tid, new_tid } if guard_fork_events!() => { + crate::__dead_code_marker!("fork_events", "stop_reason"); + self.write_stop_common(res, target, Some(cur_tid), Signal::SIGTRAP)?; + res.write_str("fork:")?; + res.write_specific_thread_id(SpecificThreadId { + pid: self + .features + .multiprocess() + .then_some(SpecificIdKind::WithId(self.get_current_pid(target)?)), + tid: SpecificIdKind::WithId(new_tid), + })?; + res.write_str(";")?; + FinishExecStatus::Handled + } + MultiThreadStopReason::VFork { cur_tid, new_tid } if guard_vfork_events!() => { + crate::__dead_code_marker!("vfork_events", "stop_reason"); + self.write_stop_common(res, target, Some(cur_tid), Signal::SIGTRAP)?; + res.write_str("vfork:")?; + res.write_specific_thread_id(SpecificThreadId { + pid: self + .features + .multiprocess() + .then_some(SpecificIdKind::WithId(self.get_current_pid(target)?)), + tid: SpecificIdKind::WithId(new_tid), + })?; + res.write_str(";")?; + FinishExecStatus::Handled + } + MultiThreadStopReason::VForkDone(tid) if guard_vforkdone_events!() => { + crate::__dead_code_marker!("vforkdone_events", "stop_reason"); + self.write_stop_common(res, target, Some(tid), Signal::SIGTRAP)?; + res.write_str("vforkdone;")?; + FinishExecStatus::Handled + } // Explicitly avoid using `_ =>` to handle the "unguarded" variants, as doing so would // squelch the useful compiler error that crops up whenever stop reasons are added. MultiThreadStopReason::SwBreak(_) | MultiThreadStopReason::HwBreak(_) | MultiThreadStopReason::Watch { .. } | MultiThreadStopReason::ReplayLog { .. } - | MultiThreadStopReason::CatchSyscall { .. } => { + | MultiThreadStopReason::CatchSyscall { .. } + | MultiThreadStopReason::Fork { .. } + | MultiThreadStopReason::VFork { .. } + | MultiThreadStopReason::VForkDone { .. } => { return Err(Error::UnsupportedStopReason); } }; diff --git a/src/stub/stop_reason.rs b/src/stub/stop_reason.rs index 70ed15ab..84b6a366 100644 --- a/src/stub/stop_reason.rs +++ b/src/stub/stop_reason.rs @@ -101,6 +101,38 @@ pub enum BaseStopReason { /// The location the event occurred at. position: CatchSyscallPosition, }, + /// A thread hit a specific library event. + /// + /// This stop reason indicates that loaded libraries have changed. The + /// debugger should fetch a new list of loaded libraries. + Library(Tid), + /// A thread created a new process via fork. + /// + /// This indicates that a fork system call was executed, creating a new + /// child process. + Fork { + /// Tid of the thread that called fork + cur_tid: Tid, + /// Tid of the new child process + new_tid: core::num::NonZeroUsize, + }, + /// A thread created a new process via vfork. + /// + /// Similar to Fork, but the parent process is suspended until the child + /// calls exec or exits, as the parent and child temporarily share the + /// same address space. + VFork { + /// Tid of the thread that called vfork + cur_tid: Tid, + /// Tid of the new child process + new_tid: core::num::NonZeroUsize, + }, + /// A vfork child process has completed its operation. + /// + /// This indicates that a child process created by vfork has either called + /// exec or terminated, so the address spaces of parent and child are no + /// longer shared. + VForkDone(Tid), } /// A stop reason for a single threaded target. @@ -140,6 +172,16 @@ impl From> for BaseStopReason { number, position, }, + BaseStopReason::Library(_) => BaseStopReason::Library(crate::SINGLE_THREAD_TID), + BaseStopReason::Fork { new_tid, .. } => BaseStopReason::Fork { + cur_tid: crate::SINGLE_THREAD_TID, + new_tid, + }, + BaseStopReason::VFork { new_tid, .. } => BaseStopReason::VFork { + cur_tid: crate::SINGLE_THREAD_TID, + new_tid, + }, + BaseStopReason::VForkDone(_) => BaseStopReason::VForkDone(crate::SINGLE_THREAD_TID), } } } diff --git a/src/target/mod.rs b/src/target/mod.rs index f7a5b517..5d2fc208 100644 --- a/src/target/mod.rs +++ b/src/target/mod.rs @@ -599,6 +599,24 @@ pub trait Target { true } + /// Support for reporting fork events. + #[inline(always)] + fn support_fork_events(&self) -> bool { + true + } + + /// Support for reporting vfork events. + #[inline(always)] + fn support_vfork_events(&self) -> bool { + true + } + + /// Support for reporting vforkdone events. + #[inline(always)] + fn support_vforkdone_events(&self) -> bool { + true + } + /// Support for setting / removing breakpoints. #[inline(always)] fn support_breakpoints(&mut self) -> Option> {