1 use super::prelude::*; 2 use crate::protocol::commands::ext::Resume; 3 4 use crate::arch::Arch; 5 use crate::common::{Signal, Tid}; 6 use crate::protocol::commands::_vCont::Actions; 7 use crate::protocol::{SpecificIdKind, SpecificThreadId}; 8 use crate::stub::MultiThreadStopReason; 9 use crate::target::ext::base::reverse_exec::ReplayLogPosition; 10 use crate::target::ext::base::ResumeOps; 11 use crate::target::ext::catch_syscalls::CatchSyscallPosition; 12 use crate::FAKE_PID; 13 14 use super::DisconnectReason; 15 16 impl<T: Target, C: Connection> GdbStubImpl<T, C> { handle_stop_resume( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: Resume<'_>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>17 pub(crate) fn handle_stop_resume( 18 &mut self, 19 res: &mut ResponseWriter<'_, C>, 20 target: &mut T, 21 command: Resume<'_>, 22 ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { 23 let mut ops = match target.base_ops().resume_ops() { 24 Some(ops) => ops, 25 None => return Ok(HandlerStatus::Handled), 26 }; 27 28 let actions = match command { 29 Resume::vCont(cmd) => { 30 use crate::protocol::commands::_vCont::vCont; 31 match cmd { 32 vCont::Query => { 33 // Continue is part of the base protocol 34 res.write_str("vCont;c;C")?; 35 36 // Single stepping is optional 37 if match &mut ops { 38 ResumeOps::SingleThread(ops) => ops.support_single_step().is_some(), 39 ResumeOps::MultiThread(ops) => ops.support_single_step().is_some(), 40 } { 41 res.write_str(";s;S")?; 42 } 43 44 // Range stepping is optional 45 if match &mut ops { 46 ResumeOps::SingleThread(ops) => ops.support_range_step().is_some(), 47 ResumeOps::MultiThread(ops) => ops.support_range_step().is_some(), 48 } { 49 res.write_str(";r")?; 50 } 51 52 // doesn't actually invoke vCont 53 return Ok(HandlerStatus::Handled); 54 } 55 vCont::Actions(actions) => actions, 56 } 57 } 58 // TODO?: support custom resume addr in 'c' and 's' 59 // 60 // vCont doesn't have a notion of "resume addr", and since the implementation of these 61 // packets reuse vCont infrastructure, supporting this obscure feature will be a bit 62 // annoying... 63 // 64 // TODO: add `support_legacy_s_c_packets` flag (similar to `use_X_packet`) 65 Resume::c(_) => Actions::new_continue(SpecificThreadId { 66 pid: None, 67 tid: self.current_resume_tid, 68 }), 69 Resume::s(_) => Actions::new_step(SpecificThreadId { 70 pid: None, 71 tid: self.current_resume_tid, 72 }), 73 }; 74 75 self.do_vcont(ops, actions) 76 } 77 do_vcont_single_thread( ops: &mut dyn crate::target::ext::base::singlethread::SingleThreadResume< Arch = T::Arch, Error = T::Error, >, actions: &Actions<'_>, ) -> Result<(), Error<T::Error, C::Error>>78 fn do_vcont_single_thread( 79 ops: &mut dyn crate::target::ext::base::singlethread::SingleThreadResume< 80 Arch = T::Arch, 81 Error = T::Error, 82 >, 83 actions: &Actions<'_>, 84 ) -> Result<(), Error<T::Error, C::Error>> { 85 use crate::protocol::commands::_vCont::VContKind; 86 87 let mut actions = actions.iter(); 88 let first_action = actions 89 .next() 90 .ok_or(Error::PacketParse( 91 crate::protocol::PacketParseError::MalformedCommand, 92 ))? 93 .ok_or(Error::PacketParse( 94 crate::protocol::PacketParseError::MalformedCommand, 95 ))?; 96 97 let invalid_second_action = match actions.next() { 98 None => false, 99 Some(act) => match act { 100 None => { 101 return Err(Error::PacketParse( 102 crate::protocol::PacketParseError::MalformedCommand, 103 )) 104 } 105 Some(act) => !matches!(act.kind, VContKind::Continue), 106 }, 107 }; 108 109 if invalid_second_action || actions.next().is_some() { 110 return Err(Error::PacketUnexpected); 111 } 112 113 match first_action.kind { 114 VContKind::Continue | VContKind::ContinueWithSig(_) => { 115 let signal = match first_action.kind { 116 VContKind::ContinueWithSig(sig) => Some(sig), 117 _ => None, 118 }; 119 120 ops.resume(signal).map_err(Error::TargetError)?; 121 Ok(()) 122 } 123 VContKind::Step | VContKind::StepWithSig(_) if ops.support_single_step().is_some() => { 124 let ops = ops.support_single_step().unwrap(); 125 126 let signal = match first_action.kind { 127 VContKind::StepWithSig(sig) => Some(sig), 128 _ => None, 129 }; 130 131 ops.step(signal).map_err(Error::TargetError)?; 132 Ok(()) 133 } 134 VContKind::RangeStep(start, end) if ops.support_range_step().is_some() => { 135 let ops = ops.support_range_step().unwrap(); 136 137 let start = start.decode().map_err(|_| Error::TargetMismatch)?; 138 let end = end.decode().map_err(|_| Error::TargetMismatch)?; 139 140 ops.resume_range_step(start, end) 141 .map_err(Error::TargetError)?; 142 Ok(()) 143 } 144 // TODO: update this case when non-stop mode is implemented 145 VContKind::Stop => Err(Error::PacketUnexpected), 146 147 // Instead of using `_ =>`, explicitly list out any remaining unguarded cases. 148 VContKind::RangeStep(..) | VContKind::Step | VContKind::StepWithSig(..) => { 149 error!("GDB client sent resume action not reported by `vCont?`"); 150 Err(Error::PacketUnexpected) 151 } 152 } 153 } 154 do_vcont_multi_thread( ops: &mut dyn crate::target::ext::base::multithread::MultiThreadResume< Arch = T::Arch, Error = T::Error, >, actions: &Actions<'_>, ) -> Result<(), Error<T::Error, C::Error>>155 fn do_vcont_multi_thread( 156 ops: &mut dyn crate::target::ext::base::multithread::MultiThreadResume< 157 Arch = T::Arch, 158 Error = T::Error, 159 >, 160 actions: &Actions<'_>, 161 ) -> Result<(), Error<T::Error, C::Error>> { 162 ops.clear_resume_actions().map_err(Error::TargetError)?; 163 164 for action in actions.iter() { 165 use crate::protocol::commands::_vCont::VContKind; 166 167 let action = action.ok_or(Error::PacketParse( 168 crate::protocol::PacketParseError::MalformedCommand, 169 ))?; 170 171 match action.kind { 172 VContKind::Continue | VContKind::ContinueWithSig(_) => { 173 let signal = match action.kind { 174 VContKind::ContinueWithSig(sig) => Some(sig), 175 _ => None, 176 }; 177 178 match action.thread.map(|thread| thread.tid) { 179 // An action with no thread-id matches all threads 180 None | Some(SpecificIdKind::All) => { 181 // Target API contract specifies that the default 182 // resume action for all threads is continue. 183 } 184 Some(SpecificIdKind::WithId(tid)) => ops 185 .set_resume_action_continue(tid, signal) 186 .map_err(Error::TargetError)?, 187 } 188 } 189 VContKind::Step | VContKind::StepWithSig(_) 190 if ops.support_single_step().is_some() => 191 { 192 let ops = ops.support_single_step().unwrap(); 193 194 let signal = match action.kind { 195 VContKind::StepWithSig(sig) => Some(sig), 196 _ => None, 197 }; 198 199 match action.thread.map(|thread| thread.tid) { 200 // An action with no thread-id matches all threads 201 None | Some(SpecificIdKind::All) => { 202 error!("GDB client sent 'step' as default resume action"); 203 return Err(Error::PacketUnexpected); 204 } 205 Some(SpecificIdKind::WithId(tid)) => { 206 ops.set_resume_action_step(tid, signal) 207 .map_err(Error::TargetError)?; 208 } 209 }; 210 } 211 212 VContKind::RangeStep(start, end) if ops.support_range_step().is_some() => { 213 let ops = ops.support_range_step().unwrap(); 214 215 match action.thread.map(|thread| thread.tid) { 216 // An action with no thread-id matches all threads 217 None | Some(SpecificIdKind::All) => { 218 error!("GDB client sent 'range step' as default resume action"); 219 return Err(Error::PacketUnexpected); 220 } 221 Some(SpecificIdKind::WithId(tid)) => { 222 let start = start.decode().map_err(|_| Error::TargetMismatch)?; 223 let end = end.decode().map_err(|_| Error::TargetMismatch)?; 224 225 ops.set_resume_action_range_step(tid, start, end) 226 .map_err(Error::TargetError)?; 227 } 228 }; 229 } 230 // TODO: update this case when non-stop mode is implemented 231 VContKind::Stop => return Err(Error::PacketUnexpected), 232 233 // Instead of using `_ =>`, explicitly list out any remaining unguarded cases. 234 VContKind::RangeStep(..) | VContKind::Step | VContKind::StepWithSig(..) => { 235 error!("GDB client sent resume action not reported by `vCont?`"); 236 return Err(Error::PacketUnexpected); 237 } 238 } 239 } 240 241 ops.resume().map_err(Error::TargetError) 242 } 243 do_vcont( &mut self, ops: ResumeOps<'_, T::Arch, T::Error>, actions: Actions<'_>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>244 fn do_vcont( 245 &mut self, 246 ops: ResumeOps<'_, T::Arch, T::Error>, 247 actions: Actions<'_>, 248 ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { 249 match ops { 250 ResumeOps::SingleThread(ops) => Self::do_vcont_single_thread(ops, &actions)?, 251 ResumeOps::MultiThread(ops) => Self::do_vcont_multi_thread(ops, &actions)?, 252 }; 253 254 Ok(HandlerStatus::DeferredStopReason) 255 } 256 write_stop_common( &mut self, res: &mut ResponseWriter<'_, C>, tid: Option<Tid>, signal: Signal, ) -> Result<(), Error<T::Error, C::Error>>257 fn write_stop_common( 258 &mut self, 259 res: &mut ResponseWriter<'_, C>, 260 tid: Option<Tid>, 261 signal: Signal, 262 ) -> Result<(), Error<T::Error, C::Error>> { 263 res.write_str("T")?; 264 res.write_num(signal as u8)?; 265 266 if let Some(tid) = tid { 267 self.current_mem_tid = tid; 268 self.current_resume_tid = SpecificIdKind::WithId(tid); 269 270 res.write_str("thread:")?; 271 res.write_specific_thread_id(SpecificThreadId { 272 pid: self 273 .features 274 .multiprocess() 275 .then_some(SpecificIdKind::WithId(FAKE_PID)), 276 tid: SpecificIdKind::WithId(tid), 277 })?; 278 res.write_str(";")?; 279 } 280 281 Ok(()) 282 } 283 finish_exec( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, stop_reason: MultiThreadStopReason<<T::Arch as Arch>::Usize>, ) -> Result<FinishExecStatus, Error<T::Error, C::Error>>284 pub(crate) fn finish_exec( 285 &mut self, 286 res: &mut ResponseWriter<'_, C>, 287 target: &mut T, 288 stop_reason: MultiThreadStopReason<<T::Arch as Arch>::Usize>, 289 ) -> Result<FinishExecStatus, Error<T::Error, C::Error>> { 290 macro_rules! guard_reverse_exec { 291 () => {{ 292 if let Some(resume_ops) = target.base_ops().resume_ops() { 293 let (reverse_cont, reverse_step) = match resume_ops { 294 ResumeOps::MultiThread(ops) => ( 295 ops.support_reverse_cont().is_some(), 296 ops.support_reverse_step().is_some(), 297 ), 298 ResumeOps::SingleThread(ops) => ( 299 ops.support_reverse_cont().is_some(), 300 ops.support_reverse_step().is_some(), 301 ), 302 }; 303 304 reverse_cont || reverse_step 305 } else { 306 false 307 } 308 }}; 309 } 310 311 macro_rules! guard_break { 312 ($op:ident) => { 313 target 314 .support_breakpoints() 315 .and_then(|ops| ops.$op()) 316 .is_some() 317 }; 318 } 319 320 macro_rules! guard_catch_syscall { 321 () => { 322 target.support_catch_syscalls().is_some() 323 }; 324 } 325 326 let status = match stop_reason { 327 MultiThreadStopReason::DoneStep => { 328 res.write_str("S")?; 329 res.write_num(Signal::SIGTRAP as u8)?; 330 FinishExecStatus::Handled 331 } 332 MultiThreadStopReason::Signal(sig) => { 333 res.write_str("S")?; 334 res.write_num(sig as u8)?; 335 FinishExecStatus::Handled 336 } 337 MultiThreadStopReason::Exited(code) => { 338 res.write_str("W")?; 339 res.write_num(code)?; 340 FinishExecStatus::Disconnect(DisconnectReason::TargetExited(code)) 341 } 342 MultiThreadStopReason::Terminated(sig) => { 343 res.write_str("X")?; 344 res.write_num(sig as u8)?; 345 FinishExecStatus::Disconnect(DisconnectReason::TargetTerminated(sig)) 346 } 347 MultiThreadStopReason::SignalWithThread { tid, signal } => { 348 self.write_stop_common(res, Some(tid), signal)?; 349 FinishExecStatus::Handled 350 } 351 MultiThreadStopReason::SwBreak(tid) if guard_break!(support_sw_breakpoint) => { 352 crate::__dead_code_marker!("sw_breakpoint", "stop_reason"); 353 354 self.write_stop_common(res, Some(tid), Signal::SIGTRAP)?; 355 res.write_str("swbreak:;")?; 356 FinishExecStatus::Handled 357 } 358 MultiThreadStopReason::HwBreak(tid) if guard_break!(support_hw_breakpoint) => { 359 crate::__dead_code_marker!("hw_breakpoint", "stop_reason"); 360 361 self.write_stop_common(res, Some(tid), Signal::SIGTRAP)?; 362 res.write_str("hwbreak:;")?; 363 FinishExecStatus::Handled 364 } 365 MultiThreadStopReason::Watch { tid, kind, addr } 366 if guard_break!(support_hw_watchpoint) => 367 { 368 crate::__dead_code_marker!("hw_watchpoint", "stop_reason"); 369 370 self.write_stop_common(res, Some(tid), Signal::SIGTRAP)?; 371 372 use crate::target::ext::breakpoints::WatchKind; 373 match kind { 374 WatchKind::Write => res.write_str("watch:")?, 375 WatchKind::Read => res.write_str("rwatch:")?, 376 WatchKind::ReadWrite => res.write_str("awatch:")?, 377 } 378 res.write_num(addr)?; 379 res.write_str(";")?; 380 FinishExecStatus::Handled 381 } 382 MultiThreadStopReason::ReplayLog { tid, pos } if guard_reverse_exec!() => { 383 crate::__dead_code_marker!("reverse_exec", "stop_reason"); 384 385 self.write_stop_common(res, tid, Signal::SIGTRAP)?; 386 387 res.write_str("replaylog:")?; 388 res.write_str(match pos { 389 ReplayLogPosition::Begin => "begin", 390 ReplayLogPosition::End => "end", 391 })?; 392 res.write_str(";")?; 393 394 FinishExecStatus::Handled 395 } 396 MultiThreadStopReason::CatchSyscall { 397 tid, 398 number, 399 position, 400 } if guard_catch_syscall!() => { 401 crate::__dead_code_marker!("catch_syscall", "stop_reason"); 402 403 self.write_stop_common(res, tid, Signal::SIGTRAP)?; 404 405 res.write_str(match position { 406 CatchSyscallPosition::Entry => "syscall_entry:", 407 CatchSyscallPosition::Return => "syscall_return:", 408 })?; 409 res.write_num(number)?; 410 res.write_str(";")?; 411 412 FinishExecStatus::Handled 413 } 414 // Explicitly avoid using `_ =>` to handle the "unguarded" variants, as doing so would 415 // squelch the useful compiler error that crops up whenever stop reasons are added. 416 MultiThreadStopReason::SwBreak(_) 417 | MultiThreadStopReason::HwBreak(_) 418 | MultiThreadStopReason::Watch { .. } 419 | MultiThreadStopReason::ReplayLog { .. } 420 | MultiThreadStopReason::CatchSyscall { .. } => { 421 return Err(Error::UnsupportedStopReason); 422 } 423 }; 424 425 Ok(status) 426 } 427 } 428 429 pub(crate) enum FinishExecStatus { 430 Handled, 431 Disconnect(DisconnectReason), 432 } 433