1 use super::prelude::*; 2 use crate::protocol::commands::ext::Base; 3 4 use crate::arch::{Arch, Registers}; 5 use crate::protocol::{IdKind, SpecificIdKind, SpecificThreadId}; 6 use crate::target::ext::base::multithread::ThreadStopReason; 7 use crate::target::ext::base::{BaseOps, GdbInterrupt, ReplayLogPosition, ResumeAction}; 8 use crate::{FAKE_PID, SINGLE_THREAD_TID}; 9 10 impl<T: Target, C: Connection> GdbStubImpl<T, C> { 11 #[inline(always)] get_sane_any_tid(&mut self, target: &mut T) -> Result<Tid, Error<T::Error, C::Error>>12 fn get_sane_any_tid(&mut self, target: &mut T) -> Result<Tid, Error<T::Error, C::Error>> { 13 let tid = match target.base_ops() { 14 BaseOps::SingleThread(_) => SINGLE_THREAD_TID, 15 BaseOps::MultiThread(ops) => { 16 let mut first_tid = None; 17 ops.list_active_threads(&mut |tid| { 18 if first_tid.is_none() { 19 first_tid = Some(tid); 20 } 21 }) 22 .map_err(Error::TargetError)?; 23 // Note that `Error::NoActiveThreads` shouldn't ever occur, since this method is 24 // called from the `H` packet handler, which AFAIK is only sent after the GDB 25 // client has confirmed that a thread / process exists. 26 // 27 // If it does, that really sucks, and will require rethinking how to handle "any 28 // thread" messages. 29 first_tid.ok_or(Error::NoActiveThreads)? 30 } 31 }; 32 Ok(tid) 33 } 34 handle_base<'a>( &mut self, res: &mut ResponseWriter<C>, target: &mut T, command: Base<'a>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>35 pub(crate) fn handle_base<'a>( 36 &mut self, 37 res: &mut ResponseWriter<C>, 38 target: &mut T, 39 command: Base<'a>, 40 ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { 41 let handler_status = match command { 42 // ------------------ Handshaking and Queries ------------------- // 43 Base::qSupported(cmd) => { 44 // XXX: actually read what the client supports, and enable/disable features 45 // appropriately 46 let _features = cmd.features.into_iter(); 47 48 res.write_str("PacketSize=")?; 49 res.write_num(cmd.packet_buffer_len)?; 50 51 res.write_str(";vContSupported+")?; 52 res.write_str(";multiprocess+")?; 53 res.write_str(";QStartNoAckMode+")?; 54 55 let (reverse_cont, reverse_step) = match target.base_ops() { 56 BaseOps::MultiThread(ops) => ( 57 ops.support_reverse_cont().is_some(), 58 ops.support_reverse_step().is_some(), 59 ), 60 BaseOps::SingleThread(ops) => ( 61 ops.support_reverse_cont().is_some(), 62 ops.support_reverse_step().is_some(), 63 ), 64 }; 65 66 if reverse_cont { 67 res.write_str(";ReverseContinue+")?; 68 } 69 70 if reverse_step { 71 res.write_str(";ReverseStep+")?; 72 } 73 74 if let Some(ops) = target.extended_mode() { 75 if ops.configure_aslr().is_some() { 76 res.write_str(";QDisableRandomization+")?; 77 } 78 79 if ops.configure_env().is_some() { 80 res.write_str(";QEnvironmentHexEncoded+")?; 81 res.write_str(";QEnvironmentUnset+")?; 82 res.write_str(";QEnvironmentReset+")?; 83 } 84 85 if ops.configure_startup_shell().is_some() { 86 res.write_str(";QStartupWithShell+")?; 87 } 88 89 if ops.configure_working_dir().is_some() { 90 res.write_str(";QSetWorkingDir+")?; 91 } 92 } 93 94 if let Some(ops) = target.breakpoints() { 95 if ops.sw_breakpoint().is_some() { 96 res.write_str(";swbreak+")?; 97 } 98 99 if ops.hw_breakpoint().is_some() || ops.hw_watchpoint().is_some() { 100 res.write_str(";hwbreak+")?; 101 } 102 } 103 104 if T::Arch::target_description_xml().is_some() 105 || target.target_description_xml_override().is_some() 106 { 107 res.write_str(";qXfer:features:read+")?; 108 } 109 110 HandlerStatus::Handled 111 } 112 Base::QStartNoAckMode(_) => { 113 self.no_ack_mode = true; 114 HandlerStatus::NeedsOk 115 } 116 Base::qXferFeaturesRead(cmd) => { 117 #[allow(clippy::redundant_closure)] 118 let xml = target 119 .target_description_xml_override() 120 .map(|ops| ops.target_description_xml()) 121 .or_else(|| T::Arch::target_description_xml()); 122 123 match xml { 124 Some(xml) => { 125 let xml = xml.trim(); 126 if cmd.offset >= xml.len() { 127 // no more data 128 res.write_str("l")?; 129 } else if cmd.offset + cmd.len >= xml.len() { 130 // last little bit of data 131 res.write_str("l")?; 132 res.write_binary(&xml.as_bytes()[cmd.offset..])? 133 } else { 134 // still more data 135 res.write_str("m")?; 136 res.write_binary(&xml.as_bytes()[cmd.offset..(cmd.offset + cmd.len)])? 137 } 138 } 139 // If the target hasn't provided their own XML, then the initial response to 140 // "qSupported" wouldn't have included "qXfer:features:read", and gdb wouldn't 141 // send this packet unless it was explicitly marked as supported. 142 None => return Err(Error::PacketUnexpected), 143 } 144 HandlerStatus::Handled 145 } 146 147 // -------------------- "Core" Functionality -------------------- // 148 // TODO: Improve the '?' response based on last-sent stop reason. 149 // this will be particularly relevant when working on non-stop mode. 150 Base::QuestionMark(_) => { 151 res.write_str("S05")?; 152 HandlerStatus::Handled 153 } 154 Base::qAttached(cmd) => { 155 let is_attached = match target.extended_mode() { 156 // when _not_ running in extended mode, just report that we're attaching to an 157 // existing process. 158 None => true, // assume attached to an existing process 159 // When running in extended mode, we must defer to the target 160 Some(ops) => { 161 let pid: Pid = cmd.pid.ok_or(Error::PacketUnexpected)?; 162 ops.query_if_attached(pid).handle_error()?.was_attached() 163 } 164 }; 165 res.write_str(if is_attached { "1" } else { "0" })?; 166 HandlerStatus::Handled 167 } 168 Base::g(_) => { 169 let mut regs: <T::Arch as Arch>::Registers = Default::default(); 170 match target.base_ops() { 171 BaseOps::SingleThread(ops) => ops.read_registers(&mut regs), 172 BaseOps::MultiThread(ops) => { 173 ops.read_registers(&mut regs, self.current_mem_tid) 174 } 175 } 176 .handle_error()?; 177 178 let mut err = Ok(()); 179 regs.gdb_serialize(|val| { 180 let res = match val { 181 Some(b) => res.write_hex_buf(&[b]), 182 None => res.write_str("xx"), 183 }; 184 if let Err(e) = res { 185 err = Err(e); 186 } 187 }); 188 err?; 189 HandlerStatus::Handled 190 } 191 Base::G(cmd) => { 192 let mut regs: <T::Arch as Arch>::Registers = Default::default(); 193 regs.gdb_deserialize(cmd.vals) 194 .map_err(|_| Error::TargetMismatch)?; 195 196 match target.base_ops() { 197 BaseOps::SingleThread(ops) => ops.write_registers(®s), 198 BaseOps::MultiThread(ops) => ops.write_registers(®s, self.current_mem_tid), 199 } 200 .handle_error()?; 201 202 HandlerStatus::NeedsOk 203 } 204 Base::m(cmd) => { 205 let buf = cmd.buf; 206 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr) 207 .ok_or(Error::TargetMismatch)?; 208 209 let mut i = 0; 210 let mut n = cmd.len; 211 while n != 0 { 212 let chunk_size = n.min(buf.len()); 213 214 use num_traits::NumCast; 215 216 let addr = addr + NumCast::from(i).ok_or(Error::TargetMismatch)?; 217 let data = &mut buf[..chunk_size]; 218 match target.base_ops() { 219 BaseOps::SingleThread(ops) => ops.read_addrs(addr, data), 220 BaseOps::MultiThread(ops) => { 221 ops.read_addrs(addr, data, self.current_mem_tid) 222 } 223 } 224 .handle_error()?; 225 226 n -= chunk_size; 227 i += chunk_size; 228 229 res.write_hex_buf(data)?; 230 } 231 HandlerStatus::Handled 232 } 233 Base::M(cmd) => { 234 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr) 235 .ok_or(Error::TargetMismatch)?; 236 237 match target.base_ops() { 238 BaseOps::SingleThread(ops) => ops.write_addrs(addr, cmd.val), 239 BaseOps::MultiThread(ops) => { 240 ops.write_addrs(addr, cmd.val, self.current_mem_tid) 241 } 242 } 243 .handle_error()?; 244 245 HandlerStatus::NeedsOk 246 } 247 Base::k(_) | Base::vKill(_) => { 248 match target.extended_mode() { 249 // When not running in extended mode, stop the `GdbStub` and disconnect. 250 None => HandlerStatus::Disconnect(DisconnectReason::Kill), 251 252 // When running in extended mode, a kill command does not necessarily result in 253 // a disconnect... 254 Some(ops) => { 255 let pid = match command { 256 Base::vKill(cmd) => Some(cmd.pid), 257 _ => None, 258 }; 259 260 let should_terminate = ops.kill(pid).handle_error()?; 261 if should_terminate.into_bool() { 262 // manually write OK, since we need to return a DisconnectReason 263 res.write_str("OK")?; 264 HandlerStatus::Disconnect(DisconnectReason::Kill) 265 } else { 266 HandlerStatus::NeedsOk 267 } 268 } 269 } 270 } 271 Base::D(_) => { 272 // TODO: plumb-through Pid when exposing full multiprocess + extended mode 273 res.write_str("OK")?; // manually write OK, since we need to return a DisconnectReason 274 HandlerStatus::Disconnect(DisconnectReason::Disconnect) 275 } 276 Base::vCont(cmd) => { 277 use crate::protocol::commands::_vCont::vCont; 278 match cmd { 279 vCont::Query => { 280 res.write_str("vCont;c;C;s;S")?; 281 if match target.base_ops() { 282 BaseOps::SingleThread(ops) => ops.support_resume_range_step().is_some(), 283 BaseOps::MultiThread(ops) => ops.support_range_step().is_some(), 284 } { 285 res.write_str(";r")?; 286 } 287 HandlerStatus::Handled 288 } 289 vCont::Actions(actions) => self.do_vcont(res, target, actions)?, 290 } 291 } 292 // TODO?: support custom resume addr in 'c' and 's' 293 // 294 // unfortunately, this wouldn't be a particularly easy thing to implement, since the 295 // vCont packet doesn't natively support custom resume addresses. This leaves a few 296 // options for the implementation: 297 // 298 // 1. Adding new ResumeActions (i.e: ContinueWithAddr(U) and StepWithAddr(U)) 299 // 2. Automatically calling `read_registers`, updating the `pc`, and calling 300 // `write_registers` prior to resuming. 301 // - will require adding some sort of `get_pc_mut` method to the `Registers` trait. 302 // 303 // Option 1 is easier to implement, but puts more burden on the implementor. Option 2 304 // will require more effort to implement (and will be less performant), but it will hide 305 // this protocol wart from the end user. 306 // 307 // Oh, one more thought - there's a subtle pitfall to watch out for if implementing 308 // Option 1: if the target is using conditional breakpoints, `do_vcont` has to be 309 // modified to only pass the resume with address variants on the _first_ iteration 310 // through the loop. 311 Base::c(_) => { 312 use crate::protocol::commands::_vCont::Actions; 313 314 self.do_vcont( 315 res, 316 target, 317 Actions::new_continue(SpecificThreadId { 318 pid: None, 319 tid: self.current_resume_tid, 320 }), 321 )? 322 } 323 Base::s(_) => { 324 use crate::protocol::commands::_vCont::Actions; 325 326 self.do_vcont( 327 res, 328 target, 329 Actions::new_step(SpecificThreadId { 330 pid: None, 331 tid: self.current_resume_tid, 332 }), 333 )? 334 } 335 336 // ------------------- Multi-threading Support ------------------ // 337 Base::H(cmd) => { 338 use crate::protocol::commands::_h_upcase::Op; 339 match cmd.kind { 340 Op::Other => match cmd.thread.tid { 341 IdKind::Any => self.current_mem_tid = self.get_sane_any_tid(target)?, 342 // "All" threads doesn't make sense for memory accesses 343 IdKind::All => return Err(Error::PacketUnexpected), 344 IdKind::WithId(tid) => self.current_mem_tid = tid, 345 }, 346 // technically, this variant is deprecated in favor of vCont... 347 Op::StepContinue => match cmd.thread.tid { 348 IdKind::Any => { 349 self.current_resume_tid = 350 SpecificIdKind::WithId(self.get_sane_any_tid(target)?) 351 } 352 IdKind::All => self.current_resume_tid = SpecificIdKind::All, 353 IdKind::WithId(tid) => { 354 self.current_resume_tid = SpecificIdKind::WithId(tid) 355 } 356 }, 357 } 358 HandlerStatus::NeedsOk 359 } 360 Base::qfThreadInfo(_) => { 361 res.write_str("m")?; 362 363 match target.base_ops() { 364 BaseOps::SingleThread(_) => res.write_specific_thread_id(SpecificThreadId { 365 pid: Some(SpecificIdKind::WithId(FAKE_PID)), 366 tid: SpecificIdKind::WithId(SINGLE_THREAD_TID), 367 })?, 368 BaseOps::MultiThread(ops) => { 369 let mut err: Result<_, Error<T::Error, C::Error>> = Ok(()); 370 let mut first = true; 371 ops.list_active_threads(&mut |tid| { 372 // TODO: replace this with a try block (once stabilized) 373 let e = (|| { 374 if !first { 375 res.write_str(",")? 376 } 377 first = false; 378 res.write_specific_thread_id(SpecificThreadId { 379 pid: Some(SpecificIdKind::WithId(FAKE_PID)), 380 tid: SpecificIdKind::WithId(tid), 381 })?; 382 Ok(()) 383 })(); 384 385 if let Err(e) = e { 386 err = Err(e) 387 } 388 }) 389 .map_err(Error::TargetError)?; 390 err?; 391 } 392 } 393 394 HandlerStatus::Handled 395 } 396 Base::qsThreadInfo(_) => { 397 res.write_str("l")?; 398 HandlerStatus::Handled 399 } 400 Base::T(cmd) => { 401 let alive = match cmd.thread.tid { 402 IdKind::WithId(tid) => match target.base_ops() { 403 BaseOps::SingleThread(_) => tid == SINGLE_THREAD_TID, 404 BaseOps::MultiThread(ops) => { 405 ops.is_thread_alive(tid).map_err(Error::TargetError)? 406 } 407 }, 408 // TODO: double-check if GDB ever sends other variants 409 // Even after ample testing, this arm has never been hit... 410 _ => return Err(Error::PacketUnexpected), 411 }; 412 if alive { 413 HandlerStatus::NeedsOk 414 } else { 415 // any error code will do 416 return Err(Error::NonFatalError(1)); 417 } 418 } 419 }; 420 Ok(handler_status) 421 } 422 423 #[allow(clippy::type_complexity)] do_vcont_single_thread( ops: &mut dyn crate::target::ext::base::singlethread::SingleThreadOps< Arch = T::Arch, Error = T::Error, >, res: &mut ResponseWriter<C>, actions: &crate::protocol::commands::_vCont::Actions, ) -> Result<ThreadStopReason<<T::Arch as Arch>::Usize>, Error<T::Error, C::Error>>424 fn do_vcont_single_thread( 425 ops: &mut dyn crate::target::ext::base::singlethread::SingleThreadOps< 426 Arch = T::Arch, 427 Error = T::Error, 428 >, 429 res: &mut ResponseWriter<C>, 430 actions: &crate::protocol::commands::_vCont::Actions, 431 ) -> Result<ThreadStopReason<<T::Arch as Arch>::Usize>, Error<T::Error, C::Error>> { 432 use crate::protocol::commands::_vCont::VContKind; 433 434 let mut err = Ok(()); 435 let mut check_gdb_interrupt = || match res.as_conn().peek() { 436 Ok(Some(0x03)) => true, // 0x03 is the interrupt byte 437 Ok(Some(_)) => false, // it's nothing that can't wait... 438 Ok(None) => false, 439 Err(e) => { 440 err = Err(Error::ConnectionRead(e)); 441 true // break ASAP if a connection error occurred 442 } 443 }; 444 445 let mut actions = actions.iter(); 446 let first_action = actions 447 .next() 448 .ok_or(Error::PacketParse( 449 crate::protocol::PacketParseError::MalformedCommand, 450 ))? 451 .ok_or(Error::PacketParse( 452 crate::protocol::PacketParseError::MalformedCommand, 453 ))?; 454 455 let invalid_second_action = match actions.next() { 456 None => false, 457 Some(act) => match act { 458 None => { 459 return Err(Error::PacketParse( 460 crate::protocol::PacketParseError::MalformedCommand, 461 )) 462 } 463 Some(act) => !matches!(act.kind, VContKind::Continue), 464 }, 465 }; 466 467 if invalid_second_action || actions.next().is_some() { 468 return Err(Error::PacketUnexpected); 469 } 470 471 let action = match first_action.kind { 472 VContKind::Step => ResumeAction::Step, 473 VContKind::Continue => ResumeAction::Continue, 474 VContKind::StepWithSig(sig) => ResumeAction::StepWithSignal(sig), 475 VContKind::ContinueWithSig(sig) => ResumeAction::ContinueWithSignal(sig), 476 VContKind::RangeStep(start, end) => { 477 if let Some(ops) = ops.support_resume_range_step() { 478 let start = start.decode().map_err(|_| Error::TargetMismatch)?; 479 let end = end.decode().map_err(|_| Error::TargetMismatch)?; 480 481 let ret = ops 482 .resume_range_step(start, end, GdbInterrupt::new(&mut check_gdb_interrupt)) 483 .map_err(Error::TargetError)? 484 .into(); 485 err?; 486 return Ok(ret); 487 } else { 488 return Err(Error::PacketUnexpected); 489 } 490 } 491 // TODO: update this case when non-stop mode is implemented 492 VContKind::Stop => return Err(Error::PacketUnexpected), 493 }; 494 495 let ret = ops 496 .resume(action, GdbInterrupt::new(&mut check_gdb_interrupt)) 497 .map_err(Error::TargetError)? 498 .into(); 499 err?; 500 Ok(ret) 501 } 502 503 #[allow(clippy::type_complexity)] do_vcont_multi_thread( ops: &mut dyn crate::target::ext::base::multithread::MultiThreadOps< Arch = T::Arch, Error = T::Error, >, res: &mut ResponseWriter<C>, actions: &crate::protocol::commands::_vCont::Actions, ) -> Result<ThreadStopReason<<T::Arch as Arch>::Usize>, Error<T::Error, C::Error>>504 fn do_vcont_multi_thread( 505 ops: &mut dyn crate::target::ext::base::multithread::MultiThreadOps< 506 Arch = T::Arch, 507 Error = T::Error, 508 >, 509 res: &mut ResponseWriter<C>, 510 actions: &crate::protocol::commands::_vCont::Actions, 511 ) -> Result<ThreadStopReason<<T::Arch as Arch>::Usize>, Error<T::Error, C::Error>> { 512 // this is a pretty arbitrary choice, but it seems reasonable for most cases. 513 let mut default_resume_action = ResumeAction::Continue; 514 515 ops.clear_resume_actions().map_err(Error::TargetError)?; 516 517 for action in actions.iter() { 518 use crate::protocol::commands::_vCont::VContKind; 519 520 let action = action.ok_or(Error::PacketParse( 521 crate::protocol::PacketParseError::MalformedCommand, 522 ))?; 523 524 let resume_action = match action.kind { 525 VContKind::Step => ResumeAction::Step, 526 VContKind::Continue => ResumeAction::Continue, 527 // there seems to be a GDB bug where it doesn't use `vCont` unless 528 // `vCont?` returns support for resuming with a signal. 529 VContKind::StepWithSig(sig) => ResumeAction::StepWithSignal(sig), 530 VContKind::ContinueWithSig(sig) => ResumeAction::ContinueWithSignal(sig), 531 VContKind::RangeStep(start, end) => { 532 if let Some(ops) = ops.support_range_step() { 533 match action.thread.map(|thread| thread.tid) { 534 // An action with no thread-id matches all threads 535 None | Some(SpecificIdKind::All) => { 536 return Err(Error::PacketUnexpected) 537 } 538 Some(SpecificIdKind::WithId(tid)) => { 539 let start = start.decode().map_err(|_| Error::TargetMismatch)?; 540 let end = end.decode().map_err(|_| Error::TargetMismatch)?; 541 542 ops.set_resume_action_range_step(tid, start, end) 543 .map_err(Error::TargetError)?; 544 continue; 545 } 546 }; 547 } else { 548 return Err(Error::PacketUnexpected); 549 } 550 } 551 // TODO: update this case when non-stop mode is implemented 552 VContKind::Stop => return Err(Error::PacketUnexpected), 553 }; 554 555 match action.thread.map(|thread| thread.tid) { 556 // An action with no thread-id matches all threads 557 None | Some(SpecificIdKind::All) => default_resume_action = resume_action, 558 Some(SpecificIdKind::WithId(tid)) => ops 559 .set_resume_action(tid, resume_action) 560 .map_err(Error::TargetError)?, 561 }; 562 } 563 564 let mut err = Ok(()); 565 let mut check_gdb_interrupt = || match res.as_conn().peek() { 566 Ok(Some(0x03)) => true, // 0x03 is the interrupt byte 567 Ok(Some(_)) => false, // it's nothing that can't wait... 568 Ok(None) => false, 569 Err(e) => { 570 err = Err(Error::ConnectionRead(e)); 571 true // break ASAP if a connection error occurred 572 } 573 }; 574 575 let ret = ops 576 .resume( 577 default_resume_action, 578 GdbInterrupt::new(&mut check_gdb_interrupt), 579 ) 580 .map_err(Error::TargetError)?; 581 582 err?; 583 584 Ok(ret) 585 } 586 do_vcont( &mut self, res: &mut ResponseWriter<C>, target: &mut T, actions: crate::protocol::commands::_vCont::Actions, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>587 fn do_vcont( 588 &mut self, 589 res: &mut ResponseWriter<C>, 590 target: &mut T, 591 actions: crate::protocol::commands::_vCont::Actions, 592 ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { 593 loop { 594 let stop_reason = match target.base_ops() { 595 BaseOps::SingleThread(ops) => Self::do_vcont_single_thread(ops, res, &actions)?, 596 BaseOps::MultiThread(ops) => Self::do_vcont_multi_thread(ops, res, &actions)?, 597 }; 598 599 match self.finish_exec(res, target, stop_reason)? { 600 Some(status) => break Ok(status), 601 None => continue, 602 } 603 } 604 } 605 write_break_common( &mut self, res: &mut ResponseWriter<C>, tid: Tid, ) -> Result<(), Error<T::Error, C::Error>>606 fn write_break_common( 607 &mut self, 608 res: &mut ResponseWriter<C>, 609 tid: Tid, 610 ) -> Result<(), Error<T::Error, C::Error>> { 611 self.current_mem_tid = tid; 612 self.current_resume_tid = SpecificIdKind::WithId(tid); 613 614 res.write_str("T05")?; 615 616 res.write_str("thread:")?; 617 res.write_specific_thread_id(SpecificThreadId { 618 pid: Some(SpecificIdKind::WithId(FAKE_PID)), 619 tid: SpecificIdKind::WithId(tid), 620 })?; 621 res.write_str(";")?; 622 623 Ok(()) 624 } 625 finish_exec( &mut self, res: &mut ResponseWriter<C>, target: &mut T, stop_reason: ThreadStopReason<<T::Arch as Arch>::Usize>, ) -> Result<Option<HandlerStatus>, Error<T::Error, C::Error>>626 pub(super) fn finish_exec( 627 &mut self, 628 res: &mut ResponseWriter<C>, 629 target: &mut T, 630 stop_reason: ThreadStopReason<<T::Arch as Arch>::Usize>, 631 ) -> Result<Option<HandlerStatus>, Error<T::Error, C::Error>> { 632 macro_rules! guard_reverse_exec { 633 () => {{ 634 let (reverse_cont, reverse_step) = match target.base_ops() { 635 BaseOps::MultiThread(ops) => ( 636 ops.support_reverse_cont().is_some(), 637 ops.support_reverse_step().is_some(), 638 ), 639 BaseOps::SingleThread(ops) => ( 640 ops.support_reverse_cont().is_some(), 641 ops.support_reverse_step().is_some(), 642 ), 643 }; 644 reverse_cont || reverse_step 645 }}; 646 } 647 648 macro_rules! guard_break { 649 ($op:ident) => { 650 target.breakpoints().and_then(|ops| ops.$op()).is_some() 651 }; 652 } 653 654 let status = match stop_reason { 655 ThreadStopReason::DoneStep | ThreadStopReason::GdbInterrupt => { 656 res.write_str("S05")?; 657 HandlerStatus::Handled 658 } 659 ThreadStopReason::Signal(sig) => { 660 res.write_str("S")?; 661 res.write_num(sig)?; 662 HandlerStatus::Handled 663 } 664 ThreadStopReason::Exited(code) => { 665 res.write_str("W")?; 666 res.write_num(code)?; 667 HandlerStatus::Disconnect(DisconnectReason::TargetExited(code)) 668 } 669 ThreadStopReason::Terminated(sig) => { 670 res.write_str("X")?; 671 res.write_num(sig)?; 672 HandlerStatus::Disconnect(DisconnectReason::TargetTerminated(sig)) 673 } 674 ThreadStopReason::SwBreak(tid) if guard_break!(sw_breakpoint) => { 675 crate::__dead_code_marker!("sw_breakpoint", "stop_reason"); 676 677 self.write_break_common(res, tid)?; 678 res.write_str("swbreak:;")?; 679 HandlerStatus::Handled 680 } 681 ThreadStopReason::HwBreak(tid) if guard_break!(hw_breakpoint) => { 682 crate::__dead_code_marker!("hw_breakpoint", "stop_reason"); 683 684 self.write_break_common(res, tid)?; 685 res.write_str("hwbreak:;")?; 686 HandlerStatus::Handled 687 } 688 ThreadStopReason::Watch { tid, kind, addr } if guard_break!(hw_watchpoint) => { 689 crate::__dead_code_marker!("hw_watchpoint", "stop_reason"); 690 691 self.write_break_common(res, tid)?; 692 693 use crate::target::ext::breakpoints::WatchKind; 694 match kind { 695 WatchKind::Write => res.write_str("watch:")?, 696 WatchKind::Read => res.write_str("rwatch:")?, 697 WatchKind::ReadWrite => res.write_str("awatch:")?, 698 } 699 res.write_num(addr)?; 700 res.write_str(";")?; 701 HandlerStatus::Handled 702 } 703 ThreadStopReason::ReplayLog(pos) if guard_reverse_exec!() => { 704 crate::__dead_code_marker!("reverse_exec", "stop_reason"); 705 706 res.write_str("T05")?; 707 708 res.write_str("replaylog:")?; 709 res.write_str(match pos { 710 ReplayLogPosition::Begin => "begin", 711 ReplayLogPosition::End => "end", 712 })?; 713 res.write_str(";")?; 714 715 HandlerStatus::Handled 716 } 717 _ => return Err(Error::UnsupportedStopReason), 718 }; 719 720 Ok(Some(status)) 721 } 722 } 723 724 use crate::target::ext::base::singlethread::StopReason; 725 impl<U> From<StopReason<U>> for ThreadStopReason<U> { from(st_stop_reason: StopReason<U>) -> ThreadStopReason<U>726 fn from(st_stop_reason: StopReason<U>) -> ThreadStopReason<U> { 727 match st_stop_reason { 728 StopReason::DoneStep => ThreadStopReason::DoneStep, 729 StopReason::GdbInterrupt => ThreadStopReason::GdbInterrupt, 730 StopReason::Exited(code) => ThreadStopReason::Exited(code), 731 StopReason::Terminated(sig) => ThreadStopReason::Terminated(sig), 732 StopReason::SwBreak => ThreadStopReason::SwBreak(SINGLE_THREAD_TID), 733 StopReason::HwBreak => ThreadStopReason::HwBreak(SINGLE_THREAD_TID), 734 StopReason::Watch { kind, addr } => ThreadStopReason::Watch { 735 tid: SINGLE_THREAD_TID, 736 kind, 737 addr, 738 }, 739 StopReason::Signal(sig) => ThreadStopReason::Signal(sig), 740 StopReason::ReplayLog(pos) => ThreadStopReason::ReplayLog(pos), 741 } 742 } 743 } 744