use crate::common::Signal;
use crate::common::Tid;
use crate::conn::Connection;
use crate::protocol::commands::Command;
use crate::protocol::Packet;
use crate::protocol::ResponseWriter;
use crate::protocol::SpecificIdKind;
use crate::stub::error::InternalError;
use crate::target::Target;
use crate::SINGLE_THREAD_TID;
use core::marker::PhantomData;
mod prelude {
pub(super) use crate::conn::Connection;
pub(super) use crate::internal::BeBytes;
pub(super) use crate::protocol::ResponseWriter;
pub(super) use crate::stub::core_impl::target_result_ext::TargetResultExt;
pub(super) use crate::stub::core_impl::GdbStubImpl;
pub(super) use crate::stub::core_impl::HandlerStatus;
pub(super) use crate::stub::error::InternalError as Error;
pub(super) use crate::target::Target;
}
mod auxv;
mod base;
mod breakpoints;
mod catch_syscalls;
mod exec_file;
mod extended_mode;
mod flash;
mod host_io;
mod host_process_info;
mod libraries;
mod lldb_register_info;
mod memory_map;
mod monitor_cmd;
mod no_ack_mode;
mod resume;
mod reverse_exec;
mod section_offsets;
mod single_register_access;
mod target_xml;
mod thread_extra_info;
mod tracepoints;
mod wasm;
mod x_upcase_packet;
pub(crate) use resume::FinishExecStatus;
pub(crate) mod target_result_ext {
use crate::stub::error::InternalError;
use crate::target::TargetError;
pub(super) trait TargetResultExt<V, T, C> {
fn handle_error(self) -> Result<V, InternalError<T, C>>;
}
impl<V, T, C> TargetResultExt<V, T, C> for Result<V, TargetError<T>> {
fn handle_error(self) -> Result<V, InternalError<T, C>> {
let code = match self {
Ok(v) => return Ok(v),
Err(TargetError::Fatal(e)) => return Err(InternalError::TargetError(e)),
Err(TargetError::NonFatal) => 121,
Err(TargetError::Errno(code)) => code,
#[cfg(feature = "std")]
Err(TargetError::Io(e)) => e.raw_os_error().unwrap_or(121) as u8,
};
Err(InternalError::NonFatalError(code))
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DisconnectReason {
TargetExited(u8),
TargetTerminated(Signal),
Disconnect,
Kill,
}
pub enum State {
Pump,
DeferredStopReason,
CtrlCInterrupt,
Disconnect(DisconnectReason),
}
pub(crate) struct GdbStubImpl<T: Target, C: Connection> {
_target: PhantomData<T>,
_connection: PhantomData<C>,
current_mem_tid: Tid,
current_resume_tid: SpecificIdKind,
features: ProtocolFeatures,
}
pub enum HandlerStatus {
Handled,
NeedsOk,
DeferredStopReason,
Disconnect(DisconnectReason),
}
impl<T: Target, C: Connection> GdbStubImpl<T, C> {
pub fn new() -> GdbStubImpl<T, C> {
GdbStubImpl {
_target: PhantomData,
_connection: PhantomData,
current_mem_tid: SINGLE_THREAD_TID,
current_resume_tid: SpecificIdKind::WithId(SINGLE_THREAD_TID),
features: ProtocolFeatures::empty(),
}
}
pub fn handle_packet(
&mut self,
target: &mut T,
conn: &mut C,
packet: Packet<'_>,
) -> Result<State, InternalError<T::Error, C::Error>> {
match packet {
Packet::Ack => Ok(State::Pump),
Packet::Nack => Err(InternalError::ClientSentNack),
Packet::Interrupt => {
debug!("<-- interrupt packet");
Ok(State::CtrlCInterrupt)
}
Packet::Command(command) => {
if !self.features.no_ack_mode() {
conn.write(b'+').map_err(InternalError::conn_write)?;
}
let mut res = ResponseWriter::new(conn, target.use_rle());
let disconnect_reason = match self.handle_command(&mut res, target, command) {
Ok(HandlerStatus::Handled) => None,
Ok(HandlerStatus::NeedsOk) => {
res.write_str("OK")?;
None
}
Ok(HandlerStatus::DeferredStopReason) => return Ok(State::DeferredStopReason),
Ok(HandlerStatus::Disconnect(reason)) => Some(reason),
Err(InternalError::NonFatalError(code)) => {
res.write_str("E")?;
res.write_num(code)?;
None
}
Err(e) => return Err(e),
};
let is_kill = matches!(disconnect_reason, Some(DisconnectReason::Kill));
if !(target.support_extended_mode().is_none() && is_kill) {
res.flush()?;
}
let state = match disconnect_reason {
Some(reason) => State::Disconnect(reason),
None => State::Pump,
};
Ok(state)
}
}
}
fn handle_command(
&mut self,
res: &mut ResponseWriter<'_, C>,
target: &mut T,
cmd: Command<'_>,
) -> Result<HandlerStatus, InternalError<T::Error, C::Error>> {
match cmd {
Command::Base(cmd) => self.handle_base(res, target, cmd),
Command::TargetXml(cmd) => self.handle_target_xml(res, target, cmd),
Command::Resume(cmd) => self.handle_stop_resume(res, target, cmd),
Command::NoAckMode(cmd) => self.handle_no_ack_mode(res, target, cmd),
Command::XUpcasePacket(cmd) => self.handle_x_upcase_packet(res, target, cmd),
Command::SingleRegisterAccess(cmd) => {
self.handle_single_register_access(res, target, cmd)
}
Command::Breakpoints(cmd) => self.handle_breakpoints(res, target, cmd),
Command::CatchSyscalls(cmd) => self.handle_catch_syscalls(res, target, cmd),
Command::ExtendedMode(cmd) => self.handle_extended_mode(res, target, cmd),
Command::MonitorCmd(cmd) => self.handle_monitor_cmd(res, target, cmd),
Command::SectionOffsets(cmd) => self.handle_section_offsets(res, target, cmd),
Command::ReverseCont(cmd) => self.handle_reverse_cont(res, target, cmd),
Command::ReverseStep(cmd) => self.handle_reverse_step(res, target, cmd),
Command::MemoryMap(cmd) => self.handle_memory_map(res, target, cmd),
Command::FlashOperations(cmd) => self.handle_flash_operations(res, target, cmd),
Command::HostIo(cmd) => self.handle_host_io(res, target, cmd),
Command::ExecFile(cmd) => self.handle_exec_file(res, target, cmd),
Command::Auxv(cmd) => self.handle_auxv(res, target, cmd),
Command::ThreadExtraInfo(cmd) => self.handle_thread_extra_info(res, target, cmd),
Command::LldbRegisterInfo(cmd) => self.handle_lldb_register_info(res, target, cmd),
Command::LibrariesSvr4(cmd) => self.handle_libraries_svr4(res, target, cmd),
Command::Libraries(cmd) => self.handle_libraries(res, target, cmd),
Command::Tracepoints(cmd) => self.handle_tracepoints(res, target, cmd),
Command::HostInfo(cmd) => self.handle_host_info(res, target, cmd),
Command::ProcessInfo(cmd) => self.handle_process_info(res, target, cmd),
Command::Wasm(cmd) => self.handle_wasm(res, target, cmd),
Command::Unknown(cmd) => {
if target.base_ops().resume_ops().is_none() && target.use_resume_stub() {
let is_resume_pkt = cmd
.first()
.map(|c| matches!(c, b'c' | b'C' | b's' | b'S'))
.unwrap_or(false);
if is_resume_pkt {
warn!("attempted to resume target without resume support!");
{
let mut res = ResponseWriter::new(res.as_conn(), target.use_rle());
res.write_str("O")?;
res.write_hex_buf(b"target has not implemented `support_resume()`\n")?;
res.flush()?;
}
res.write_str("S05")?;
}
}
info!("Unknown command: {:?}", core::str::from_utf8(cmd));
Ok(HandlerStatus::Handled)
}
}
}
}
#[derive(Copy, Clone)]
#[repr(transparent)]
struct ProtocolFeatures(u8);
bitflags::bitflags! {
impl ProtocolFeatures: u8 {
const NO_ACK_MODE = 1 << 0;
const MULTIPROCESS = 1 << 1;
}
}
impl ProtocolFeatures {
#[inline(always)]
fn no_ack_mode(&self) -> bool {
self.contains(ProtocolFeatures::NO_ACK_MODE)
}
#[inline(always)]
fn set_no_ack_mode(&mut self, val: bool) {
self.set(ProtocolFeatures::NO_ACK_MODE, val)
}
#[inline(always)]
fn multiprocess(&self) -> bool {
self.contains(ProtocolFeatures::MULTIPROCESS)
}
#[inline(always)]
fn set_multiprocess(&mut self, val: bool) {
self.set(ProtocolFeatures::MULTIPROCESS, val)
}
}