1 use super::prelude::*; 2 use super::DisconnectReason; 3 use crate::arch::Arch; 4 use crate::arch::Registers; 5 use crate::common::Pid; 6 use crate::common::Tid; 7 use crate::protocol::commands::ext::Base; 8 use crate::protocol::IdKind; 9 use crate::protocol::SpecificIdKind; 10 use crate::protocol::SpecificThreadId; 11 use crate::target::ext::base::BaseOps; 12 use crate::target::ext::base::ResumeOps; 13 use crate::FAKE_PID; 14 use crate::SINGLE_THREAD_TID; 15 16 impl<T: Target, C: Connection> GdbStubImpl<T, C> { 17 #[inline(always)] get_sane_any_tid( &mut self, target: &mut T, ) -> Result<Option<Tid>, Error<T::Error, C::Error>>18 fn get_sane_any_tid( 19 &mut self, 20 target: &mut T, 21 ) -> Result<Option<Tid>, Error<T::Error, C::Error>> { 22 let tid = match target.base_ops() { 23 BaseOps::SingleThread(_) => Some(SINGLE_THREAD_TID), 24 BaseOps::MultiThread(ops) => { 25 let mut first_tid = None; 26 ops.list_active_threads(&mut |tid| { 27 if first_tid.is_none() { 28 first_tid = Some(tid); 29 } 30 }) 31 .map_err(Error::TargetError)?; 32 // It is possible for this to be `None` in the case where the target has 33 // not yet called `register_thread()`. This can happen, for example, if 34 // there are no active threads in the current target process. 35 first_tid 36 } 37 }; 38 Ok(tid) 39 } 40 get_current_pid( &mut self, target: &mut T, ) -> Result<Pid, Error<T::Error, C::Error>>41 pub(crate) fn get_current_pid( 42 &mut self, 43 target: &mut T, 44 ) -> Result<Pid, Error<T::Error, C::Error>> { 45 if let Some(ops) = target 46 .support_extended_mode() 47 .and_then(|ops| ops.support_current_active_pid()) 48 { 49 ops.current_active_pid().map_err(Error::TargetError) 50 } else { 51 Ok(FAKE_PID) 52 } 53 } 54 55 // Used by `?` and `vAttach` to return a "reasonable" stop reason. 56 // 57 // This is a bit of an implementation wart, since this is really something 58 // the user ought to be able to customize. 59 // 60 // Works fine for now though... report_reasonable_stop_reason( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>61 pub(crate) fn report_reasonable_stop_reason( 62 &mut self, 63 res: &mut ResponseWriter<'_, C>, 64 target: &mut T, 65 ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { 66 // Reply with a valid thread-id or GDB issues a warning when more 67 // than one thread is active 68 if let Some(tid) = self.get_sane_any_tid(target)? { 69 res.write_str("T05thread:")?; 70 res.write_specific_thread_id(SpecificThreadId { 71 pid: self 72 .features 73 .multiprocess() 74 .then_some(SpecificIdKind::WithId(self.get_current_pid(target)?)), 75 tid: SpecificIdKind::WithId(tid), 76 })?; 77 } else { 78 res.write_str("W00")?; 79 } 80 res.write_str(";")?; 81 Ok(HandlerStatus::Handled) 82 } 83 handle_base( &mut self, res: &mut ResponseWriter<'_, C>, target: &mut T, command: Base<'_>, ) -> Result<HandlerStatus, Error<T::Error, C::Error>>84 pub(crate) fn handle_base( 85 &mut self, 86 res: &mut ResponseWriter<'_, C>, 87 target: &mut T, 88 command: Base<'_>, 89 ) -> Result<HandlerStatus, Error<T::Error, C::Error>> { 90 let handler_status = match command { 91 // ------------------ Handshaking and Queries ------------------- // 92 Base::qSupported(cmd) => { 93 use crate::protocol::commands::_qSupported::Feature; 94 95 // perform incoming feature negotiation 96 for feature in cmd.features.into_iter() { 97 let (feature, supported) = match feature { 98 Ok(Some(v)) => v, 99 Ok(None) => continue, 100 Err(()) => { 101 return Err(Error::PacketParse( 102 crate::protocol::PacketParseError::MalformedCommand, 103 )) 104 } 105 }; 106 107 match feature { 108 Feature::Multiprocess => self.features.set_multiprocess(supported), 109 } 110 } 111 112 res.write_str("PacketSize=")?; 113 res.write_num(cmd.packet_buffer_len)?; 114 115 // these are the few features that gdbstub unconditionally supports 116 res.write_str(concat!(";vContSupported+", ";multiprocess+",))?; 117 118 if target.use_no_ack_mode() { 119 res.write_str(";QStartNoAckMode+")?; 120 } 121 122 if let Some(resume_ops) = target.base_ops().resume_ops() { 123 let (reverse_cont, reverse_step) = match resume_ops { 124 ResumeOps::MultiThread(ops) => ( 125 ops.support_reverse_cont().is_some(), 126 ops.support_reverse_step().is_some(), 127 ), 128 ResumeOps::SingleThread(ops) => ( 129 ops.support_reverse_cont().is_some(), 130 ops.support_reverse_step().is_some(), 131 ), 132 }; 133 134 if reverse_cont { 135 res.write_str(";ReverseContinue+")?; 136 } 137 138 if reverse_step { 139 res.write_str(";ReverseStep+")?; 140 } 141 } 142 143 if let Some(ops) = target.support_extended_mode() { 144 if ops.support_configure_aslr().is_some() { 145 res.write_str(";QDisableRandomization+")?; 146 } 147 148 if ops.support_configure_env().is_some() { 149 res.write_str(";QEnvironmentHexEncoded+")?; 150 res.write_str(";QEnvironmentUnset+")?; 151 res.write_str(";QEnvironmentReset+")?; 152 } 153 154 if ops.support_configure_startup_shell().is_some() { 155 res.write_str(";QStartupWithShell+")?; 156 } 157 158 if ops.support_configure_working_dir().is_some() { 159 res.write_str(";QSetWorkingDir+")?; 160 } 161 } 162 163 if let Some(ops) = target.support_breakpoints() { 164 if ops.support_sw_breakpoint().is_some() { 165 res.write_str(";swbreak+")?; 166 } 167 168 if ops.support_hw_breakpoint().is_some() 169 || ops.support_hw_watchpoint().is_some() 170 { 171 res.write_str(";hwbreak+")?; 172 } 173 } 174 175 if target.support_catch_syscalls().is_some() { 176 res.write_str(";QCatchSyscalls+")?; 177 } 178 179 if target.use_target_description_xml() 180 && (T::Arch::target_description_xml().is_some() 181 || target.support_target_description_xml_override().is_some()) 182 { 183 res.write_str(";qXfer:features:read+")?; 184 } 185 186 if target.support_memory_map().is_some() { 187 res.write_str(";qXfer:memory-map:read+")?; 188 } 189 190 if target.support_exec_file().is_some() { 191 res.write_str(";qXfer:exec-file:read+")?; 192 } 193 194 if target.support_auxv().is_some() { 195 res.write_str(";qXfer:auxv:read+")?; 196 } 197 198 if target.support_libraries_svr4().is_some() { 199 res.write_str(";qXfer:libraries-svr4:read+")?; 200 } 201 202 HandlerStatus::Handled 203 } 204 205 // -------------------- "Core" Functionality -------------------- // 206 Base::QuestionMark(_) => { 207 // TODO: Improve the '?' response. 208 // this will be particularly relevant when working on non-stop mode. 209 self.report_reasonable_stop_reason(res, target)? 210 } 211 Base::qAttached(cmd) => { 212 let is_attached = match target.support_extended_mode() { 213 // when _not_ running in extended mode, just report that we're attaching to an 214 // existing process. 215 None => true, // assume attached to an existing process 216 // When running in extended mode, we must defer to the target 217 Some(ops) => { 218 match cmd.pid { 219 Some(pid) => ops.query_if_attached(pid).handle_error()?.was_attached(), 220 None => true, // assume attached to an existing process 221 } 222 } 223 }; 224 res.write_str(if is_attached { "1" } else { "0" })?; 225 HandlerStatus::Handled 226 } 227 Base::g(_) => { 228 let mut regs: <T::Arch as Arch>::Registers = Default::default(); 229 match target.base_ops() { 230 BaseOps::SingleThread(ops) => ops.read_registers(&mut regs), 231 BaseOps::MultiThread(ops) => { 232 ops.read_registers(&mut regs, self.current_mem_tid) 233 } 234 } 235 .handle_error()?; 236 237 let mut err = Ok(()); 238 regs.gdb_serialize(|val| { 239 let res = match val { 240 Some(b) => res.write_hex_buf(&[b]), 241 None => res.write_str("xx"), 242 }; 243 if let Err(e) = res { 244 err = Err(e); 245 } 246 }); 247 err?; 248 HandlerStatus::Handled 249 } 250 Base::G(cmd) => { 251 let mut regs: <T::Arch as Arch>::Registers = Default::default(); 252 regs.gdb_deserialize(cmd.vals) 253 .map_err(|_| Error::TargetMismatch)?; 254 255 match target.base_ops() { 256 BaseOps::SingleThread(ops) => ops.write_registers(®s), 257 BaseOps::MultiThread(ops) => ops.write_registers(®s, self.current_mem_tid), 258 } 259 .handle_error()?; 260 261 HandlerStatus::NeedsOk 262 } 263 Base::m(cmd) => { 264 let buf = cmd.buf; 265 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr) 266 .ok_or(Error::TargetMismatch)?; 267 268 let mut i = 0; 269 let mut n = cmd.len; 270 while n != 0 { 271 let chunk_size = n.min(buf.len()); 272 273 use num_traits::NumCast; 274 275 let addr = addr + NumCast::from(i).ok_or(Error::TargetMismatch)?; 276 let data = &mut buf[..chunk_size]; 277 let data_len = match target.base_ops() { 278 BaseOps::SingleThread(ops) => ops.read_addrs(addr, data), 279 BaseOps::MultiThread(ops) => { 280 ops.read_addrs(addr, data, self.current_mem_tid) 281 } 282 } 283 .handle_error()?; 284 285 n -= chunk_size; 286 i += chunk_size; 287 288 // TODO: add more specific error variant? 289 let data = data.get(..data_len).ok_or(Error::PacketBufferOverflow)?; 290 res.write_hex_buf(data)?; 291 } 292 HandlerStatus::Handled 293 } 294 Base::M(cmd) => { 295 let addr = <T::Arch as Arch>::Usize::from_be_bytes(cmd.addr) 296 .ok_or(Error::TargetMismatch)?; 297 298 match target.base_ops() { 299 BaseOps::SingleThread(ops) => ops.write_addrs(addr, cmd.val), 300 BaseOps::MultiThread(ops) => { 301 ops.write_addrs(addr, cmd.val, self.current_mem_tid) 302 } 303 } 304 .handle_error()?; 305 306 HandlerStatus::NeedsOk 307 } 308 Base::k(_) | Base::vKill(_) => { 309 match target.support_extended_mode() { 310 // When not running in extended mode, stop the `GdbStub` and disconnect. 311 None => HandlerStatus::Disconnect(DisconnectReason::Kill), 312 313 // When running in extended mode, a kill command does not necessarily result in 314 // a disconnect... 315 Some(ops) => { 316 let pid = match command { 317 Base::vKill(cmd) => Some(cmd.pid), 318 _ => None, 319 }; 320 321 let should_terminate = ops.kill(pid).handle_error()?; 322 if should_terminate.into_bool() { 323 // manually write OK, since we need to return a DisconnectReason 324 res.write_str("OK")?; 325 HandlerStatus::Disconnect(DisconnectReason::Kill) 326 } else { 327 HandlerStatus::NeedsOk 328 } 329 } 330 } 331 } 332 Base::D(cmd) => { 333 // TODO: plumb-through Pid when exposing full multiprocess + extended mode 334 let _pid = cmd.pid; 335 res.write_str("OK")?; // manually write OK, since we need to return a DisconnectReason 336 HandlerStatus::Disconnect(DisconnectReason::Disconnect) 337 } 338 339 // ------------------- Multi-threading Support ------------------ // 340 Base::H(cmd) => { 341 use crate::protocol::commands::_h_upcase::Op; 342 match cmd.kind { 343 Op::Other => match cmd.thread.tid { 344 IdKind::Any => match self.get_sane_any_tid(target)? { 345 Some(tid) => self.current_mem_tid = tid, 346 None => { 347 return Err(Error::NonFatalError(1)); 348 } 349 }, 350 // "All" threads doesn't make sense for memory accesses 351 IdKind::All => return Err(Error::PacketUnexpected), 352 IdKind::WithId(tid) => self.current_mem_tid = tid, 353 }, 354 // technically, this variant is deprecated in favor of vCont... 355 Op::StepContinue => match cmd.thread.tid { 356 IdKind::Any => match self.get_sane_any_tid(target)? { 357 Some(tid) => self.current_resume_tid = SpecificIdKind::WithId(tid), 358 None => { 359 return Err(Error::NonFatalError(1)); 360 } 361 }, 362 IdKind::All => self.current_resume_tid = SpecificIdKind::All, 363 IdKind::WithId(tid) => { 364 self.current_resume_tid = SpecificIdKind::WithId(tid) 365 } 366 }, 367 } 368 HandlerStatus::NeedsOk 369 } 370 Base::qfThreadInfo(_) => { 371 res.write_str("m")?; 372 let pid = self.get_current_pid(target)?; 373 374 match target.base_ops() { 375 BaseOps::SingleThread(_) => res.write_specific_thread_id(SpecificThreadId { 376 pid: self 377 .features 378 .multiprocess() 379 .then_some(SpecificIdKind::WithId(pid)), 380 tid: SpecificIdKind::WithId(SINGLE_THREAD_TID), 381 })?, 382 BaseOps::MultiThread(ops) => { 383 let mut err: Result<_, Error<T::Error, C::Error>> = Ok(()); 384 let mut first = true; 385 ops.list_active_threads(&mut |tid| { 386 // TODO: replace this with a try block (once stabilized) 387 let e = (|| { 388 if !first { 389 res.write_str(",")? 390 } 391 first = false; 392 res.write_specific_thread_id(SpecificThreadId { 393 pid: self 394 .features 395 .multiprocess() 396 .then_some(SpecificIdKind::WithId(pid)), 397 tid: SpecificIdKind::WithId(tid), 398 })?; 399 Ok(()) 400 })(); 401 402 if let Err(e) = e { 403 err = Err(e) 404 } 405 }) 406 .map_err(Error::TargetError)?; 407 err?; 408 } 409 } 410 411 HandlerStatus::Handled 412 } 413 Base::qsThreadInfo(_) => { 414 res.write_str("l")?; 415 HandlerStatus::Handled 416 } 417 Base::T(cmd) => { 418 let alive = match cmd.thread.tid { 419 IdKind::WithId(tid) => match target.base_ops() { 420 BaseOps::SingleThread(_) => tid == SINGLE_THREAD_TID, 421 BaseOps::MultiThread(ops) => { 422 ops.is_thread_alive(tid).map_err(Error::TargetError)? 423 } 424 }, 425 _ => return Err(Error::PacketUnexpected), 426 }; 427 if alive { 428 HandlerStatus::NeedsOk 429 } else { 430 // any error code will do 431 return Err(Error::NonFatalError(1)); 432 } 433 } 434 }; 435 Ok(handler_status) 436 } 437 } 438