1 use core::marker::PhantomData; 2 3 #[cfg(feature = "alloc")] 4 use alloc::collections::BTreeMap; 5 6 use managed::ManagedSlice; 7 8 use crate::common::*; 9 use crate::{ 10 arch::{Arch, RegId, Registers}, 11 connection::Connection, 12 internal::*, 13 protocol::{ 14 commands::{ext, Command}, 15 ConsoleOutput, IdKind, Packet, ResponseWriter, ThreadId, 16 }, 17 target::ext::base::multithread::{Actions, ResumeAction, ThreadStopReason, TidSelector}, 18 target::ext::base::BaseOps, 19 target::Target, 20 util::managed_vec::ManagedVec, 21 FAKE_PID, SINGLE_THREAD_TID, 22 }; 23 24 mod builder; 25 mod error; 26 mod target_result_ext; 27 28 pub use builder::{GdbStubBuilder, GdbStubBuilderError}; 29 pub use error::GdbStubError; 30 31 use target_result_ext::TargetResultExt; 32 33 use GdbStubError as Error; 34 35 /// Describes why the GDB session ended. 36 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 37 pub enum DisconnectReason { 38 /// Target Halted 39 TargetHalted, 40 /// GDB issued a disconnect command 41 Disconnect, 42 /// GDB issued a kill command 43 Kill, 44 } 45 46 /// Debug a [`Target`] using the GDB Remote Serial Protocol over a given 47 /// [`Connection`]. 48 pub struct GdbStub<'a, T: Target, C: Connection> { 49 conn: C, 50 packet_buffer: ManagedSlice<'a, u8>, 51 state: GdbStubImpl<T, C>, 52 } 53 54 impl<'a, T: Target, C: Connection> GdbStub<'a, T, C> { 55 /// Create a [`GdbStubBuilder`] using the provided Connection. builder(conn: C) -> GdbStubBuilder<'a, T, C>56 pub fn builder(conn: C) -> GdbStubBuilder<'a, T, C> { 57 GdbStubBuilder::new(conn) 58 } 59 60 /// Create a new `GdbStub` using the provided connection. 61 /// 62 /// For fine-grained control over various `GdbStub` options, use the 63 /// [`builder()`](GdbStub::builder) method instead. 64 /// 65 /// _Note:_ `new` is only available when the `alloc` feature is enabled. 66 #[cfg(feature = "alloc")] new(conn: C) -> GdbStub<'a, T, C>67 pub fn new(conn: C) -> GdbStub<'a, T, C> { 68 GdbStubBuilder::new(conn).build().unwrap() 69 } 70 71 /// Starts a GDB remote debugging session. 72 /// 73 /// Returns once the GDB client closes the debugging session, or if the 74 /// target halts. run(&mut self, target: &mut T) -> Result<DisconnectReason, Error<T::Error, C::Error>>75 pub fn run(&mut self, target: &mut T) -> Result<DisconnectReason, Error<T::Error, C::Error>> { 76 self.state 77 .run(target, &mut self.conn, &mut self.packet_buffer) 78 } 79 } 80 81 struct GdbStubImpl<T: Target, C: Connection> { 82 _target: PhantomData<T>, 83 _connection: PhantomData<C>, 84 85 packet_buffer_len: usize, 86 current_mem_tid: Tid, 87 current_resume_tid: TidSelector, 88 no_ack_mode: bool, 89 90 // Used to track which Pids were attached to / spawned when running in extended mode. 91 // 92 // An empty `BTreeMap<Pid, bool>` is only 24 bytes (on 64-bit systems), and doesn't allocate 93 // until the first element is inserted, so it should be fine to include it as part of the main 94 // state structure whether or not extended mode is actually being used. 95 #[cfg(feature = "alloc")] 96 attached_pids: BTreeMap<Pid, bool>, 97 } 98 99 enum HandlerStatus { 100 Handled, 101 NeedsOK, 102 Disconnect(DisconnectReason), 103 } 104 105 impl<T: Target, C: Connection> GdbStubImpl<T, C> { new(packet_buffer_len: usize) -> GdbStubImpl<T, C>106 fn new(packet_buffer_len: usize) -> GdbStubImpl<T, C> { 107 GdbStubImpl { 108 _target: PhantomData, 109 _connection: PhantomData, 110 111 packet_buffer_len, 112 // HACK: current_mem_tid is immediately updated with valid value once `run` is called. 113 // While the more idiomatic way to handle this would be to use an Option, given that 114 // it's only ever unset prior to the start of `run`, it's probably okay leaving it as-is 115 // for code-clarity purposes. 116 current_mem_tid: SINGLE_THREAD_TID, 117 current_resume_tid: TidSelector::All, 118 no_ack_mode: false, 119 120 #[cfg(feature = "alloc")] 121 attached_pids: BTreeMap::new(), 122 } 123 } 124 run( &mut self, target: &mut T, conn: &mut C, packet_buffer: &mut ManagedSlice<u8>, ) -> Result<DisconnectReason, Error<T::Error, C::Error>>125 fn run( 126 &mut self, 127 target: &mut T, 128 conn: &mut C, 129 packet_buffer: &mut ManagedSlice<u8>, 130 ) -> Result<DisconnectReason, Error<T::Error, C::Error>> { 131 conn.on_session_start().map_err(Error::ConnectionRead)?; 132 133 // before even accepting packets, we query the target to get a sane value for 134 // `self.current_mem_tid`. 135 // NOTE: this will break if extended mode is ever implemented... 136 137 self.current_mem_tid = match target.base_ops() { 138 BaseOps::SingleThread(_) => SINGLE_THREAD_TID, 139 BaseOps::MultiThread(ops) => { 140 let mut first_tid = None; 141 ops.list_active_threads(&mut |tid| { 142 if first_tid.is_none() { 143 first_tid = Some(tid); 144 } 145 }) 146 .map_err(Error::TargetError)?; 147 first_tid.ok_or(Error::NoActiveThreads)? 148 } 149 }; 150 151 loop { 152 match Self::recv_packet(conn, target, packet_buffer)? { 153 Packet::Ack => {} 154 Packet::Nack => return Err(Error::ClientSentNack), 155 Packet::Interrupt => { 156 debug!("<-- interrupt packet"); 157 let mut res = ResponseWriter::new(conn); 158 res.write_str("S05")?; 159 res.flush()?; 160 } 161 Packet::Command(command) => { 162 // Acknowledge the command 163 if !self.no_ack_mode { 164 conn.write(b'+').map_err(Error::ConnectionRead)?; 165 } 166 167 let mut res = ResponseWriter::new(conn); 168 let disconnect = match self.handle_command(&mut res, target, command) { 169 Ok(HandlerStatus::Handled) => None, 170 Ok(HandlerStatus::NeedsOK) => { 171 res.write_str("OK")?; 172 None 173 } 174 Ok(HandlerStatus::Disconnect(reason)) => Some(reason), 175 // HACK: handling this "dummy" error is required as part of the 176 // `TargetResultExt::handle_error()` machinery. 177 Err(Error::NonFatalError(code)) => { 178 res.write_str("E")?; 179 res.write_num(code)?; 180 None 181 } 182 Err(Error::TargetError(e)) => { 183 // unlike all other errors which are "unrecoverable" in the sense that 184 // the GDB session cannot continue, there's still a chance that a target 185 // might want to keep the debugging session alive to do a "post-mortem" 186 // analysis. As such, we simply report a standard TRAP stop reason. 187 let mut res = ResponseWriter::new(conn); 188 res.write_str("S05")?; 189 res.flush()?; 190 return Err(Error::TargetError(e)); 191 } 192 Err(e) => return Err(e), 193 }; 194 195 // HACK: this could be more elegant... 196 if disconnect != Some(DisconnectReason::Kill) { 197 res.flush()?; 198 } 199 200 if let Some(disconnect_reason) = disconnect { 201 return Ok(disconnect_reason); 202 } 203 } 204 }; 205 } 206 } 207 recv_packet<'a>( conn: &mut C, target: &mut T, pkt_buf: &'a mut ManagedSlice<u8>, ) -> Result<Packet<'a>, Error<T::Error, C::Error>>208 fn recv_packet<'a>( 209 conn: &mut C, 210 target: &mut T, 211 pkt_buf: &'a mut ManagedSlice<u8>, 212 ) -> Result<Packet<'a>, Error<T::Error, C::Error>> { 213 let header_byte = conn.read().map_err(Error::ConnectionRead)?; 214 215 // Wrap the buf in a `ManagedVec` to keep the code readable. 216 let mut buf = ManagedVec::new(pkt_buf); 217 218 buf.clear(); 219 buf.push(header_byte)?; 220 if header_byte == b'$' { 221 // read the packet body 222 loop { 223 let c = conn.read().map_err(Error::ConnectionRead)?; 224 buf.push(c)?; 225 if c == b'#' { 226 break; 227 } 228 } 229 // read the checksum as well 230 buf.push(conn.read().map_err(Error::ConnectionRead)?)?; 231 buf.push(conn.read().map_err(Error::ConnectionRead)?)?; 232 } 233 234 match Packet::from_buf(target, pkt_buf.as_mut()) { 235 Ok(packet) => Ok(packet), 236 Err(e) => Err(Error::PacketParse(e)), 237 } 238 } 239 handle_command( &mut self, res: &mut ResponseWriter<C>, target: &mut T, cmd: Command<'_>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>240 fn handle_command( 241 &mut self, 242 res: &mut ResponseWriter<C>, 243 target: &mut T, 244 cmd: Command<'_>, 245 ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { 246 match cmd { 247 Command::Unknown(cmd) => { 248 info!("Unknown command: {}", cmd); 249 Ok(HandlerStatus::Handled) 250 } 251 Command::Base(cmd) => self.handle_base(res, target, cmd), 252 Command::ExtendedMode(cmd) => self.handle_extended_mode(res, target, cmd), 253 Command::MonitorCmd(cmd) => self.handle_monitor_cmd(res, target, cmd), 254 Command::SectionOffsets(cmd) => self.handle_section_offsets(res, target, cmd), 255 } 256 } 257 handle_base<'a>( &mut self, res: &mut ResponseWriter<C>, target: &mut T, command: ext::Base<'a>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>258 fn handle_base<'a>( 259 &mut self, 260 res: &mut ResponseWriter<C>, 261 target: &mut T, 262 command: ext::Base<'a>, 263 ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { 264 let handler_status = match command { 265 // ------------------ Handshaking and Queries ------------------- // 266 ext::Base::qSupported(cmd) => { 267 // XXX: actually read what the client supports, and enable/disable features 268 // appropriately 269 let _features = cmd.features.into_iter(); 270 271 res.write_str("PacketSize=")?; 272 res.write_num(self.packet_buffer_len)?; 273 274 res.write_str(";vContSupported+")?; 275 res.write_str(";multiprocess+")?; 276 res.write_str(";QStartNoAckMode+")?; 277 278 if let Some(ops) = target.extended_mode() { 279 if ops.configure_aslr().is_some() { 280 res.write_str(";QDisableRandomization+")?; 281 } 282 283 if ops.configure_env().is_some() { 284 res.write_str(";QEnvironmentHexEncoded+")?; 285 res.write_str(";QEnvironmentUnset+")?; 286 res.write_str(";QEnvironmentReset+")?; 287 } 288 289 if ops.configure_startup_shell().is_some() { 290 res.write_str(";QStartupWithShell+")?; 291 } 292 293 if ops.configure_working_dir().is_some() { 294 res.write_str(";QSetWorkingDir+")?; 295 } 296 } 297 298 res.write_str(";swbreak+")?; 299 if target.hw_breakpoint().is_some() || target.hw_watchpoint().is_some() { 300 res.write_str(";hwbreak+")?; 301 } 302 303 // TODO: implement conditional breakpoint support (since that's kool). 304 // res.write_str("ConditionalBreakpoints+;")?; 305 306 if T::Arch::target_description_xml().is_some() 307 || target.target_description_xml_override().is_some() 308 { 309 res.write_str(";qXfer:features:read+")?; 310 } 311 312 HandlerStatus::Handled 313 } 314 ext::Base::QStartNoAckMode(_) => { 315 self.no_ack_mode = true; 316 HandlerStatus::NeedsOK 317 } 318 ext::Base::qXferFeaturesRead(cmd) => { 319 #[allow(clippy::redundant_closure)] 320 let xml = target 321 .target_description_xml_override() 322 .map(|ops| ops.target_description_xml()) 323 .or_else(|| T::Arch::target_description_xml()); 324 325 match xml { 326 Some(xml) => { 327 let xml = xml.trim(); 328 if cmd.offset >= xml.len() { 329 // no more data 330 res.write_str("l")?; 331 } else if cmd.offset + cmd.len >= xml.len() { 332 // last little bit of data 333 res.write_str("l")?; 334 res.write_binary(&xml.as_bytes()[cmd.offset..])? 335 } else { 336 // still more data 337 res.write_str("m")?; 338 res.write_binary(&xml.as_bytes()[cmd.offset..(cmd.offset + cmd.len)])? 339 } 340 } 341 // If the target hasn't provided their own XML, then the initial response to 342 // "qSupported" wouldn't have included "qXfer:features:read", and gdb wouldn't 343 // send this packet unless it was explicitly marked as supported. 344 None => return Err(Error::PacketUnexpected), 345 } 346 HandlerStatus::Handled 347 } 348 349 // -------------------- "Core" Functionality -------------------- // 350 // TODO: Improve the '?' response based on last-sent stop reason. 351 ext::Base::QuestionMark(_) => { 352 res.write_str("S05")?; 353 HandlerStatus::Handled 354 } 355 ext::Base::qAttached(cmd) => { 356 let is_attached = match target.extended_mode() { 357 // when _not_ running in extended mode, just report that we're attaching to an 358 // existing process. 359 None => true, // assume attached to an existing process 360 // When running in extended mode, we must defer to the target 361 Some(ops) => { 362 let pid: Pid = cmd.pid.ok_or(Error::PacketUnexpected)?; 363 364 #[cfg(feature = "alloc")] 365 { 366 let _ = ops; // doesn't actually query the target 367 *self.attached_pids.get(&pid).unwrap_or(&true) 368 } 369 370 #[cfg(not(feature = "alloc"))] 371 { 372 ops.query_if_attached(pid).handle_error()?.was_attached() 373 } 374 } 375 }; 376 res.write_str(if is_attached { "1" } else { "0" })?; 377 HandlerStatus::Handled 378 } 379 ext::Base::g(_) => { 380 let mut regs: <T::Arch as Arch>::Registers = Default::default(); 381 match target.base_ops() { 382 BaseOps::SingleThread(ops) => ops.read_registers(&mut regs), 383 BaseOps::MultiThread(ops) => { 384 ops.read_registers(&mut regs, self.current_mem_tid) 385 } 386 } 387 .handle_error()?; 388 389 let mut err = Ok(()); 390 regs.gdb_serialize(|val| { 391 let res = match val { 392 Some(b) => res.write_hex_buf(&[b]), 393 None => res.write_str("xx"), 394 }; 395 if let Err(e) = res { 396 err = Err(e); 397 } 398 }); 399 err?; 400 HandlerStatus::Handled 401 } 402 ext::Base::G(cmd) => { 403 let mut regs: <T::Arch as Arch>::Registers = Default::default(); 404 regs.gdb_deserialize(cmd.vals) 405 .map_err(|_| Error::TargetMismatch)?; 406 407 match target.base_ops() { 408 BaseOps::SingleThread(ops) => ops.write_registers(®s), 409 BaseOps::MultiThread(ops) => ops.write_registers(®s, self.current_mem_tid), 410 } 411 .handle_error()?; 412 413 HandlerStatus::NeedsOK 414 } 415 ext::Base::m(cmd) => { 416 let buf = cmd.buf; 417 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr) 418 .ok_or(Error::TargetMismatch)?; 419 420 let mut i = 0; 421 let mut n = cmd.len; 422 while n != 0 { 423 let chunk_size = n.min(buf.len()); 424 425 use num_traits::NumCast; 426 427 let addr = addr + NumCast::from(i).ok_or(Error::TargetMismatch)?; 428 let data = &mut buf[..chunk_size]; 429 match target.base_ops() { 430 BaseOps::SingleThread(ops) => ops.read_addrs(addr, data), 431 BaseOps::MultiThread(ops) => { 432 ops.read_addrs(addr, data, self.current_mem_tid) 433 } 434 } 435 .handle_error()?; 436 437 n -= chunk_size; 438 i += chunk_size; 439 440 res.write_hex_buf(data)?; 441 } 442 HandlerStatus::Handled 443 } 444 ext::Base::M(cmd) => { 445 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr) 446 .ok_or(Error::TargetMismatch)?; 447 448 match target.base_ops() { 449 BaseOps::SingleThread(ops) => ops.write_addrs(addr, cmd.val), 450 BaseOps::MultiThread(ops) => { 451 ops.write_addrs(addr, cmd.val, self.current_mem_tid) 452 } 453 } 454 .handle_error()?; 455 456 HandlerStatus::NeedsOK 457 } 458 ext::Base::k(_) | ext::Base::vKill(_) => { 459 match target.extended_mode() { 460 // When not running in extended mode, stop the `GdbStub` and disconnect. 461 None => HandlerStatus::Disconnect(DisconnectReason::Kill), 462 463 // When running in extended mode, a kill command does not necessarily result in 464 // a disconnect... 465 Some(ops) => { 466 let pid = match command { 467 ext::Base::vKill(cmd) => Some(cmd.pid), 468 _ => None, 469 }; 470 471 let should_terminate = ops.kill(pid).handle_error()?; 472 if should_terminate.into() { 473 // manually write OK, since we need to return a DisconnectReason 474 res.write_str("OK")?; 475 HandlerStatus::Disconnect(DisconnectReason::Kill) 476 } else { 477 HandlerStatus::NeedsOK 478 } 479 } 480 } 481 } 482 ext::Base::D(_) => { 483 // TODO: plumb-through Pid when exposing full multiprocess + extended mode 484 res.write_str("OK")?; // manually write OK, since we need to return a DisconnectReason 485 HandlerStatus::Disconnect(DisconnectReason::Disconnect) 486 } 487 ext::Base::Z(cmd) => { 488 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr) 489 .ok_or(Error::TargetMismatch)?; 490 491 use crate::target::ext::breakpoints::WatchKind::*; 492 let supported = match cmd.type_ { 493 0 => (target.sw_breakpoint()).map(|op| op.add_sw_breakpoint(addr)), 494 1 => (target.hw_breakpoint()).map(|op| op.add_hw_breakpoint(addr)), 495 2 => (target.hw_watchpoint()).map(|op| op.add_hw_watchpoint(addr, Write)), 496 3 => (target.hw_watchpoint()).map(|op| op.add_hw_watchpoint(addr, Read)), 497 4 => (target.hw_watchpoint()).map(|op| op.add_hw_watchpoint(addr, ReadWrite)), 498 // only 5 types in the protocol 499 _ => None, 500 }; 501 502 match supported { 503 None => HandlerStatus::Handled, 504 Some(Err(e)) => { 505 Err(e).handle_error()?; 506 HandlerStatus::Handled 507 } 508 Some(Ok(true)) => HandlerStatus::NeedsOK, 509 Some(Ok(false)) => return Err(Error::NonFatalError(22)), 510 } 511 } 512 ext::Base::z(cmd) => { 513 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr) 514 .ok_or(Error::TargetMismatch)?; 515 516 use crate::target::ext::breakpoints::WatchKind::*; 517 let supported = match cmd.type_ { 518 0 => (target.sw_breakpoint()).map(|op| op.remove_sw_breakpoint(addr)), 519 1 => (target.hw_breakpoint()).map(|op| op.remove_hw_breakpoint(addr)), 520 2 => (target.hw_watchpoint()).map(|op| op.remove_hw_watchpoint(addr, Write)), 521 3 => (target.hw_watchpoint()).map(|op| op.remove_hw_watchpoint(addr, Read)), 522 4 => { 523 (target.hw_watchpoint()).map(|op| op.remove_hw_watchpoint(addr, ReadWrite)) 524 } 525 // only 5 types in the protocol 526 _ => None, 527 }; 528 529 match supported { 530 None => HandlerStatus::Handled, 531 Some(Err(e)) => { 532 Err(e).handle_error()?; 533 HandlerStatus::Handled 534 } 535 Some(Ok(true)) => HandlerStatus::NeedsOK, 536 Some(Ok(false)) => return Err(Error::NonFatalError(22)), 537 } 538 } 539 ext::Base::p(p) => { 540 let mut dst = [0u8; 32]; // enough for 256-bit registers 541 let reg = <T::Arch as Arch>::RegId::from_raw_id(p.reg_id); 542 let (reg_id, reg_size) = match reg { 543 Some(v) => v, 544 // empty packet indicates unrecognized query 545 None => return Ok(HandlerStatus::Handled), 546 }; 547 let dst = &mut dst[0..reg_size]; 548 match target.base_ops() { 549 BaseOps::SingleThread(ops) => ops.read_register(reg_id, dst), 550 BaseOps::MultiThread(ops) => { 551 ops.read_register(reg_id, dst, self.current_mem_tid) 552 } 553 } 554 .handle_error()?; 555 556 res.write_hex_buf(dst)?; 557 HandlerStatus::Handled 558 } 559 ext::Base::P(p) => { 560 let reg = <T::Arch as Arch>::RegId::from_raw_id(p.reg_id); 561 match reg { 562 None => return Err(Error::NonFatalError(22)), 563 Some((reg_id, _)) => match target.base_ops() { 564 BaseOps::SingleThread(ops) => ops.write_register(reg_id, p.val), 565 BaseOps::MultiThread(ops) => { 566 ops.write_register(reg_id, p.val, self.current_mem_tid) 567 } 568 } 569 .handle_error()?, 570 } 571 HandlerStatus::NeedsOK 572 } 573 ext::Base::vCont(cmd) => { 574 use crate::protocol::commands::_vCont::{vCont, VContKind}; 575 576 let actions = match cmd { 577 vCont::Query => { 578 res.write_str("vCont;c;C;s;S")?; 579 return Ok(HandlerStatus::Handled); 580 } 581 vCont::Actions(actions) => actions, 582 }; 583 584 // map raw vCont action iterator to a format the `Target` expects 585 let mut err = Ok(()); 586 let mut actions = actions.into_iter().filter_map(|action| { 587 let action = match action { 588 Some(action) => action, 589 None => { 590 err = Err(Error::PacketParse( 591 crate::protocol::PacketParseError::MalformedCommand, 592 )); 593 return None; 594 } 595 }; 596 597 let resume_action = match action.kind { 598 VContKind::Step => ResumeAction::Step, 599 VContKind::Continue => ResumeAction::Continue, 600 _ => { 601 // there seems to be a GDB bug where it doesn't use `vCont` unless 602 // `vCont?` returns support for resuming with a signal. 603 // 604 // This error case can be removed once "Resume with Signal" is 605 // implemented 606 err = Err(Error::ResumeWithSignalUnimplemented); 607 return None; 608 } 609 }; 610 611 let tid = match action.thread { 612 Some(thread) => match thread.tid { 613 IdKind::Any => { 614 err = Err(Error::PacketUnexpected); 615 return None; 616 } 617 IdKind::All => TidSelector::All, 618 IdKind::WithID(tid) => TidSelector::WithID(tid), 619 }, 620 // An action with no thread-id matches all threads 621 None => TidSelector::All, 622 }; 623 624 Some((tid, resume_action)) 625 }); 626 627 let ret = match self.do_vcont(res, target, &mut actions) { 628 Ok(None) => HandlerStatus::Handled, 629 Ok(Some(dc)) => HandlerStatus::Disconnect(dc), 630 Err(e) => return Err(e), 631 }; 632 err?; 633 ret 634 } 635 // TODO?: support custom resume addr in 'c' and 's' 636 ext::Base::c(_) => { 637 match self.do_vcont( 638 res, 639 target, 640 &mut core::iter::once((self.current_resume_tid, ResumeAction::Continue)), 641 ) { 642 Ok(None) => HandlerStatus::Handled, 643 Ok(Some(dc)) => HandlerStatus::Disconnect(dc), 644 Err(e) => return Err(e), 645 } 646 } 647 ext::Base::s(_) => { 648 match self.do_vcont( 649 res, 650 target, 651 &mut core::iter::once((self.current_resume_tid, ResumeAction::Step)), 652 ) { 653 Ok(None) => HandlerStatus::Handled, 654 Ok(Some(dc)) => HandlerStatus::Disconnect(dc), 655 Err(e) => return Err(e), 656 } 657 } 658 659 // ------------------- Multi-threading Support ------------------ // 660 ext::Base::H(cmd) => { 661 use crate::protocol::commands::_h_upcase::Op; 662 match cmd.kind { 663 Op::Other => match cmd.thread.tid { 664 IdKind::Any => {} // reuse old tid 665 // "All" threads doesn't make sense for memory accesses 666 IdKind::All => return Err(Error::PacketUnexpected), 667 IdKind::WithID(tid) => self.current_mem_tid = tid, 668 }, 669 // technically, this variant is deprecated in favor of vCont... 670 Op::StepContinue => match cmd.thread.tid { 671 IdKind::Any => {} // reuse old tid 672 IdKind::All => self.current_resume_tid = TidSelector::All, 673 IdKind::WithID(tid) => self.current_resume_tid = TidSelector::WithID(tid), 674 }, 675 } 676 HandlerStatus::NeedsOK 677 } 678 ext::Base::qfThreadInfo(_) => { 679 res.write_str("m")?; 680 681 match target.base_ops() { 682 BaseOps::SingleThread(_) => res.write_thread_id(ThreadId { 683 pid: Some(IdKind::WithID(FAKE_PID)), 684 tid: IdKind::WithID(SINGLE_THREAD_TID), 685 })?, 686 BaseOps::MultiThread(ops) => { 687 let mut err: Result<_, Error<T::Error, C::Error>> = Ok(()); 688 let mut first = true; 689 ops.list_active_threads(&mut |tid| { 690 // TODO: replace this with a try block (once stabilized) 691 let e = (|| { 692 if !first { 693 res.write_str(",")? 694 } 695 first = false; 696 res.write_thread_id(ThreadId { 697 pid: Some(IdKind::WithID(FAKE_PID)), 698 tid: IdKind::WithID(tid), 699 })?; 700 Ok(()) 701 })(); 702 703 if let Err(e) = e { 704 err = Err(e) 705 } 706 }) 707 .map_err(Error::TargetError)?; 708 err?; 709 } 710 } 711 712 HandlerStatus::Handled 713 } 714 ext::Base::qsThreadInfo(_) => { 715 res.write_str("l")?; 716 HandlerStatus::Handled 717 } 718 ext::Base::T(cmd) => { 719 let alive = match cmd.thread.tid { 720 IdKind::WithID(tid) => match target.base_ops() { 721 BaseOps::SingleThread(_) => tid == SINGLE_THREAD_TID, 722 BaseOps::MultiThread(ops) => { 723 ops.is_thread_alive(tid).map_err(Error::TargetError)? 724 } 725 }, 726 // TODO: double-check if GDB ever sends other variants 727 // Even after ample testing, this arm has never been hit... 728 _ => return Err(Error::PacketUnexpected), 729 }; 730 if alive { 731 HandlerStatus::NeedsOK 732 } else { 733 // any error code will do 734 return Err(Error::NonFatalError(1)); 735 } 736 } 737 }; 738 Ok(handler_status) 739 } 740 handle_monitor_cmd<'a>( &mut self, res: &mut ResponseWriter<C>, target: &mut T, command: ext::MonitorCmd<'a>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>741 fn handle_monitor_cmd<'a>( 742 &mut self, 743 res: &mut ResponseWriter<C>, 744 target: &mut T, 745 command: ext::MonitorCmd<'a>, 746 ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { 747 let ops = match target.monitor_cmd() { 748 Some(ops) => ops, 749 None => return Ok(HandlerStatus::Handled), 750 }; 751 752 let handler_status = match command { 753 ext::MonitorCmd::qRcmd(cmd) => { 754 crate::__dead_code_marker!("qRcmd", "impl"); 755 756 let mut err: Result<_, Error<T::Error, C::Error>> = Ok(()); 757 let mut callback = |msg: &[u8]| { 758 // TODO: replace this with a try block (once stabilized) 759 let e = (|| { 760 let mut res = ResponseWriter::new(res.as_conn()); 761 res.write_str("O")?; 762 res.write_hex_buf(msg)?; 763 res.flush()?; 764 Ok(()) 765 })(); 766 767 if let Err(e) = e { 768 err = Err(e) 769 } 770 }; 771 772 ops.handle_monitor_cmd(cmd.hex_cmd, ConsoleOutput::new(&mut callback)) 773 .map_err(Error::TargetError)?; 774 err?; 775 776 HandlerStatus::NeedsOK 777 } 778 }; 779 780 Ok(handler_status) 781 } 782 handle_section_offsets( &mut self, res: &mut ResponseWriter<C>, target: &mut T, command: ext::SectionOffsets, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>783 fn handle_section_offsets( 784 &mut self, 785 res: &mut ResponseWriter<C>, 786 target: &mut T, 787 command: ext::SectionOffsets, 788 ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { 789 let ops = match target.section_offsets() { 790 Some(ops) => ops, 791 None => return Ok(HandlerStatus::Handled), 792 }; 793 794 let handler_status = match command { 795 ext::SectionOffsets::qOffsets(_cmd) => { 796 use crate::target::ext::section_offsets::Offsets; 797 798 crate::__dead_code_marker!("qOffsets", "impl"); 799 800 match ops.get_section_offsets().map_err(Error::TargetError)? { 801 Offsets::Sections { text, data, bss } => { 802 res.write_str("Text=")?; 803 res.write_num(text)?; 804 805 res.write_str(";Data=")?; 806 res.write_num(data)?; 807 808 // "Note: while a Bss offset may be included in the response, 809 // GDB ignores this and instead applies the Data offset to the Bss section." 810 // 811 // While this would suggest that it's OK to omit `Bss=` entirely, recent 812 // versions of GDB seem to require that `Bss=` is present. 813 // 814 // See https://github.com/bminor/binutils-gdb/blob/master/gdb/remote.c#L4149-L4159 815 let bss = bss.unwrap_or(data); 816 res.write_str(";Bss=")?; 817 res.write_num(bss)?; 818 } 819 Offsets::Segments { text_seg, data_seg } => { 820 res.write_str("TextSeg=")?; 821 res.write_num(text_seg)?; 822 823 if let Some(data) = data_seg { 824 res.write_str(";DataSeg=")?; 825 res.write_num(data)?; 826 } 827 } 828 } 829 HandlerStatus::Handled 830 } 831 }; 832 833 Ok(handler_status) 834 } 835 handle_extended_mode<'a>( &mut self, res: &mut ResponseWriter<C>, target: &mut T, command: ext::ExtendedMode<'a>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>836 fn handle_extended_mode<'a>( 837 &mut self, 838 res: &mut ResponseWriter<C>, 839 target: &mut T, 840 command: ext::ExtendedMode<'a>, 841 ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { 842 let ops = match target.extended_mode() { 843 Some(ops) => ops, 844 None => return Ok(HandlerStatus::Handled), 845 }; 846 847 let handler_status = match command { 848 ext::ExtendedMode::ExclamationMark(_cmd) => { 849 ops.on_start().map_err(Error::TargetError)?; 850 HandlerStatus::NeedsOK 851 } 852 ext::ExtendedMode::R(_cmd) => { 853 ops.restart().map_err(Error::TargetError)?; 854 HandlerStatus::Handled 855 } 856 ext::ExtendedMode::vAttach(cmd) => { 857 ops.attach(cmd.pid).handle_error()?; 858 859 #[cfg(feature = "alloc")] 860 self.attached_pids.insert(cmd.pid, true); 861 862 // TODO: sends OK when running in Non-Stop mode 863 HandlerStatus::Handled 864 } 865 ext::ExtendedMode::vRun(cmd) => { 866 use crate::target::ext::extended_mode::Args; 867 868 let mut pid = ops 869 .run(cmd.filename, Args::new(&mut cmd.args.into_iter())) 870 .handle_error()?; 871 872 // on single-threaded systems, we'll ignore the provided PID and keep 873 // using the FAKE_PID. 874 if let BaseOps::SingleThread(_) = target.base_ops() { 875 pid = FAKE_PID; 876 } 877 878 let _ = pid; // squelch warning on no_std targets 879 #[cfg(feature = "alloc")] 880 self.attached_pids.insert(pid, false); 881 882 // TODO: send a more descriptive stop packet? 883 res.write_str("S05")?; 884 HandlerStatus::Handled 885 } 886 // --------- ASLR --------- // 887 ext::ExtendedMode::QDisableRandomization(cmd) if ops.configure_aslr().is_some() => { 888 let ops = ops.configure_aslr().unwrap(); 889 ops.cfg_aslr(cmd.value).handle_error()?; 890 HandlerStatus::NeedsOK 891 } 892 // --------- Environment --------- // 893 ext::ExtendedMode::QEnvironmentHexEncoded(cmd) if ops.configure_env().is_some() => { 894 let ops = ops.configure_env().unwrap(); 895 ops.set_env(cmd.key, cmd.value).handle_error()?; 896 HandlerStatus::NeedsOK 897 } 898 ext::ExtendedMode::QEnvironmentUnset(cmd) if ops.configure_env().is_some() => { 899 let ops = ops.configure_env().unwrap(); 900 ops.remove_env(cmd.key).handle_error()?; 901 HandlerStatus::NeedsOK 902 } 903 ext::ExtendedMode::QEnvironmentReset(_cmd) if ops.configure_env().is_some() => { 904 let ops = ops.configure_env().unwrap(); 905 ops.reset_env().handle_error()?; 906 HandlerStatus::NeedsOK 907 } 908 // --------- Working Dir --------- // 909 ext::ExtendedMode::QSetWorkingDir(cmd) if ops.configure_working_dir().is_some() => { 910 let ops = ops.configure_working_dir().unwrap(); 911 ops.cfg_working_dir(cmd.dir).handle_error()?; 912 HandlerStatus::NeedsOK 913 } 914 // --------- Startup Shell --------- // 915 ext::ExtendedMode::QStartupWithShell(cmd) 916 if ops.configure_startup_shell().is_some() => 917 { 918 let ops = ops.configure_startup_shell().unwrap(); 919 ops.cfg_startup_with_shell(cmd.value).handle_error()?; 920 HandlerStatus::NeedsOK 921 } 922 _ => HandlerStatus::Handled, 923 }; 924 925 Ok(handler_status) 926 } 927 do_vcont( &mut self, res: &mut ResponseWriter<C>, target: &mut T, actions: &mut dyn Iterator<Item = (TidSelector, ResumeAction)>, ) -> Result<Option<DisconnectReason>, Error<T::Error, C::Error>>928 fn do_vcont( 929 &mut self, 930 res: &mut ResponseWriter<C>, 931 target: &mut T, 932 actions: &mut dyn Iterator<Item = (TidSelector, ResumeAction)>, 933 ) -> Result<Option<DisconnectReason>, Error<T::Error, C::Error>> { 934 let mut err = Ok(()); 935 936 let mut check_gdb_interrupt = || match res.as_conn().peek() { 937 Ok(Some(0x03)) => true, // 0x03 is the interrupt byte 938 Ok(Some(_)) => false, // it's nothing that can't wait... 939 Ok(None) => false, 940 Err(e) => { 941 err = Err(Error::ConnectionRead(e)); 942 true // break ASAP if a connection error occurred 943 } 944 }; 945 946 let stop_reason = match target.base_ops() { 947 BaseOps::SingleThread(ops) => ops 948 .resume( 949 // TODO?: add a more descriptive error if vcont has multiple threads in 950 // single-threaded mode? 951 actions.next().ok_or(Error::PacketUnexpected)?.1, 952 &mut check_gdb_interrupt, 953 ) 954 .map_err(Error::TargetError)? 955 .into(), 956 BaseOps::MultiThread(ops) => ops 957 .resume(Actions::new(actions), &mut check_gdb_interrupt) 958 .map_err(Error::TargetError)?, 959 }; 960 961 err?; 962 963 self.finish_vcont(stop_reason, res) 964 } 965 966 // DEVNOTE: `do_vcont` and `finish_vcont` could be merged into a single 967 // function, at the expense of slightly larger code. In the future, if the 968 // `vCont` machinery is re-written, there's no reason why the two functions 969 // couldn't be re-merged. 970 finish_vcont( &mut self, stop_reason: ThreadStopReason<<T::Arch as Arch>::Usize>, res: &mut ResponseWriter<C>, ) -> Result<Option<DisconnectReason>, Error<T::Error, C::Error>>971 fn finish_vcont( 972 &mut self, 973 stop_reason: ThreadStopReason<<T::Arch as Arch>::Usize>, 974 res: &mut ResponseWriter<C>, 975 ) -> Result<Option<DisconnectReason>, Error<T::Error, C::Error>> { 976 match stop_reason { 977 ThreadStopReason::DoneStep | ThreadStopReason::GdbInterrupt => { 978 res.write_str("S05")?; 979 Ok(None) 980 } 981 ThreadStopReason::Signal(code) => { 982 res.write_str("S")?; 983 res.write_num(code)?; 984 Ok(None) 985 } 986 ThreadStopReason::Halted => { 987 res.write_str("W19")?; // SIGSTOP 988 Ok(Some(DisconnectReason::TargetHalted)) 989 } 990 ThreadStopReason::SwBreak(tid) 991 | ThreadStopReason::HwBreak(tid) 992 | ThreadStopReason::Watch { tid, .. } => { 993 self.current_mem_tid = tid; 994 self.current_resume_tid = TidSelector::WithID(tid); 995 996 res.write_str("T05")?; 997 998 res.write_str("thread:")?; 999 res.write_thread_id(ThreadId { 1000 pid: Some(IdKind::WithID(FAKE_PID)), 1001 tid: IdKind::WithID(tid), 1002 })?; 1003 res.write_str(";")?; 1004 1005 match stop_reason { 1006 // don't include addr on sw/hw break 1007 ThreadStopReason::SwBreak(_) => res.write_str("swbreak:")?, 1008 ThreadStopReason::HwBreak(_) => res.write_str("hwbreak:")?, 1009 ThreadStopReason::Watch { kind, addr, .. } => { 1010 use crate::target::ext::breakpoints::WatchKind; 1011 match kind { 1012 WatchKind::Write => res.write_str("watch:")?, 1013 WatchKind::Read => res.write_str("rwatch:")?, 1014 WatchKind::ReadWrite => res.write_str("awatch:")?, 1015 } 1016 res.write_num(addr)?; 1017 } 1018 _ => unreachable!(), 1019 }; 1020 1021 res.write_str(";")?; 1022 Ok(None) 1023 } 1024 } 1025 } 1026 } 1027 1028 use crate::target::ext::base::singlethread::StopReason; 1029 impl<U> From<StopReason<U>> for ThreadStopReason<U> { from(st_stop_reason: StopReason<U>) -> ThreadStopReason<U>1030 fn from(st_stop_reason: StopReason<U>) -> ThreadStopReason<U> { 1031 match st_stop_reason { 1032 StopReason::DoneStep => ThreadStopReason::DoneStep, 1033 StopReason::GdbInterrupt => ThreadStopReason::GdbInterrupt, 1034 StopReason::Halted => ThreadStopReason::Halted, 1035 StopReason::SwBreak => ThreadStopReason::SwBreak(SINGLE_THREAD_TID), 1036 StopReason::HwBreak => ThreadStopReason::HwBreak(SINGLE_THREAD_TID), 1037 StopReason::Watch { kind, addr } => ThreadStopReason::Watch { 1038 tid: SINGLE_THREAD_TID, 1039 kind, 1040 addr, 1041 }, 1042 StopReason::Signal(sig) => ThreadStopReason::Signal(sig), 1043 } 1044 } 1045 } 1046