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