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