use crate::protocol::packet::PacketBuf; use crate::target::Target; use paste::paste; /// Common imports used by >50% of all packet parsers. /// /// Do not clutter this prelude with types only used by a few packets. pub mod prelude { pub use crate::protocol::commands::ParseCommand; pub use crate::protocol::common::hex::decode_hex; pub use crate::protocol::common::hex::decode_hex_buf; pub use crate::protocol::packet::PacketBuf; pub use core::convert::TryFrom; pub use core::convert::TryInto; } pub trait ParseCommand<'a>: Sized { /// Try to parse a packet from the packet buffer. fn from_packet(buf: PacketBuf<'a>) -> Option; } macro_rules! commands { ( $( $ext:ident $(use $lt:lifetime)? { $($name:literal => $mod:ident::$command:ident$(<$lifetime:lifetime>)?,)* } )* ) => {paste! { // Most packets follow a consistent model of "only enabled when a // particular IDET is implemented", but there are some exceptions to // this rule that need to be special-cased: // // # Breakpoint packets (z, Z) // // Breakpoint packets are special-cased, as the "Z" packet is parsed // differently depending on whether or not the target implements the // `Agent` extension. // // While it's entirely possible to eagerly parse the "Z" packet for // bytecode, doing so would unnecessary bloat implementations that do // not support evaluating agent expressions. $($( #[allow(non_snake_case, non_camel_case_types)] pub mod $mod; )*)* pub mod breakpoint; pub mod ext { $( #[allow(non_camel_case_types, clippy::enum_variant_names)] pub enum [<$ext:camel>] $(<$lt>)? { $($command(super::$mod::$command<$($lifetime)?>),)* } )* use super::breakpoint::{BasicBreakpoint, BytecodeBreakpoint}; #[allow(non_camel_case_types)] pub enum Breakpoints<'a> { z(BasicBreakpoint<'a>), Z(BasicBreakpoint<'a>), ZWithBytecode(BytecodeBreakpoint<'a>), } } /// GDB commands pub enum Command<'a> { $( [<$ext:camel>](ext::[<$ext:camel>]$(<$lt>)?), )* Breakpoints(ext::Breakpoints<'a>), Unknown(&'a [u8]), } impl<'a> Command<'a> { pub fn from_packet( target: &mut impl Target, mut buf: PacketBuf<'a> ) -> Option> { // HACK: this locally-scoped trait enables using identifiers // that aren't top-level `Target` IDETs to split-up the packet // parsing code. trait Hack { fn support_base(&mut self) -> Option<()>; fn support_target_xml(&mut self) -> Option<()>; fn support_lldb_register_info(&mut self) -> Option<()>; fn support_resume(&mut self) -> Option<()>; fn support_single_register_access(&mut self) -> Option<()>; fn support_reverse_step(&mut self) -> Option<()>; fn support_reverse_cont(&mut self) -> Option<()>; fn support_no_ack_mode(&mut self) -> Option<()>; fn support_x_upcase_packet(&mut self) -> Option<()>; fn support_thread_extra_info(&mut self) -> Option<()>; } impl Hack for T { fn support_base(&mut self) -> Option<()> { Some(()) } fn support_target_xml(&mut self) -> Option<()> { use crate::arch::Arch; if self.use_target_description_xml() && (T::Arch::target_description_xml().is_some() || self.support_target_description_xml_override().is_some()) { Some(()) } else { None } } fn support_lldb_register_info(&mut self) -> Option<()> { use crate::arch::Arch; if self.use_lldb_register_info() && (T::Arch::lldb_register_info(usize::max_value()).is_some() || self.support_lldb_register_info_override().is_some()) { Some(()) } else { None } } fn support_resume(&mut self) -> Option<()> { self.base_ops().resume_ops().map(drop) } fn support_single_register_access(&mut self) -> Option<()> { use crate::target::ext::base::BaseOps; match self.base_ops() { BaseOps::SingleThread(ops) => ops.support_single_register_access().map(drop), BaseOps::MultiThread(ops) => ops.support_single_register_access().map(drop), } } fn support_reverse_step(&mut self) -> Option<()> { use crate::target::ext::base::ResumeOps; match self.base_ops().resume_ops()? { ResumeOps::SingleThread(ops) => ops.support_reverse_step().map(drop), ResumeOps::MultiThread(ops) => ops.support_reverse_step().map(drop), } } fn support_reverse_cont(&mut self) -> Option<()> { use crate::target::ext::base::ResumeOps; match self.base_ops().resume_ops()? { ResumeOps::SingleThread(ops) => ops.support_reverse_cont().map(drop), ResumeOps::MultiThread(ops) => ops.support_reverse_cont().map(drop), } } fn support_x_upcase_packet(&mut self) -> Option<()> { if self.use_x_upcase_packet() { Some(()) } else { None } } fn support_no_ack_mode(&mut self) -> Option<()> { if self.use_no_ack_mode() { Some(()) } else { None } } fn support_thread_extra_info(&mut self) -> Option<()> { use crate::target::ext::base::BaseOps; match self.base_ops() { BaseOps::SingleThread(_) => None, BaseOps::MultiThread(ops) => ops.support_thread_extra_info().map(drop), } } } // TODO?: use tries for more efficient longest prefix matching $( #[allow(clippy::string_lit_as_bytes)] if target.[< support_ $ext >]().is_some() { $( if buf.strip_prefix($name.as_bytes()) { crate::__dead_code_marker!($name, "prefix_match"); let cmd = $mod::$command::from_packet(buf)?; return Some( Command::[<$ext:camel>]( ext::[<$ext:camel>]::$command(cmd) ) ) } )* } )* if let Some(_breakpoint_ops) = target.support_breakpoints() { use breakpoint::{BasicBreakpoint, BytecodeBreakpoint}; if buf.strip_prefix(b"z") { let cmd = BasicBreakpoint::from_slice(buf.into_body())?; return Some(Command::Breakpoints(ext::Breakpoints::z(cmd))) } if buf.strip_prefix(b"Z") { // TODO: agent bytecode currently unimplemented if true { let cmd = BasicBreakpoint::from_slice(buf.into_body())?; return Some(Command::Breakpoints(ext::Breakpoints::Z(cmd))) } else { let cmd = BytecodeBreakpoint::from_slice(buf.into_body())?; return Some(Command::Breakpoints(ext::Breakpoints::ZWithBytecode(cmd))) } } } Some(Command::Unknown(buf.into_body())) } } }}; } commands! { base use 'a { "?" => question_mark::QuestionMark, "D" => _d_upcase::D, "g" => _g::g, "G" => _g_upcase::G<'a>, "H" => _h_upcase::H, "k" => _k::k, "m" => _m::m<'a>, "M" => _m_upcase::M<'a>, "qAttached" => _qAttached::qAttached, "qfThreadInfo" => _qfThreadInfo::qfThreadInfo, "qsThreadInfo" => _qsThreadInfo::qsThreadInfo, "qSupported" => _qSupported::qSupported<'a>, "T" => _t_upcase::T, "vKill" => _vKill::vKill, } target_xml use 'a { "qXfer:features:read" => _qXfer_features_read::qXferFeaturesRead<'a>, } resume use 'a { "c" => _c::c<'a>, "s" => _s::s<'a>, "vCont" => _vCont::vCont<'a>, } x_upcase_packet use 'a { "X" => _x_upcase::X<'a>, } no_ack_mode { "QStartNoAckMode" => _QStartNoAckMode::QStartNoAckMode, } single_register_access use 'a { "p" => _p::p<'a>, "P" => _p_upcase::P<'a>, } extended_mode use 'a { "!" => exclamation_mark::ExclamationMark, "qC" => _qC::qC, "QDisableRandomization" => _QDisableRandomization::QDisableRandomization, "QEnvironmentHexEncoded" => _QEnvironmentHexEncoded::QEnvironmentHexEncoded<'a>, "QEnvironmentReset" => _QEnvironmentReset::QEnvironmentReset, "QEnvironmentUnset" => _QEnvironmentUnset::QEnvironmentUnset<'a>, "QSetWorkingDir" => _QSetWorkingDir::QSetWorkingDir<'a>, "QStartupWithShell" => _QStartupWithShell::QStartupWithShell, "R" => _r_upcase::R, "vAttach" => _vAttach::vAttach, "vRun" => _vRun::vRun<'a>, } monitor_cmd use 'a { "qRcmd" => _qRcmd::qRcmd<'a>, } section_offsets { "qOffsets" => _qOffsets::qOffsets, } reverse_cont { "bc" => _bc::bc, } reverse_step { "bs" => _bs::bs, } memory_map use 'a { "qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead<'a>, } auxv use 'a { "qXfer:auxv:read" => _qXfer_auxv_read::qXferAuxvRead<'a>, } exec_file use 'a { "qXfer:exec-file:read" => _qXfer_exec_file::qXferExecFileRead<'a>, } host_io use 'a { "vFile:open" => _vFile_open::vFileOpen<'a>, "vFile:close" => _vFile_close::vFileClose, "vFile:pread" => _vFile_pread::vFilePread<'a>, "vFile:pwrite" => _vFile_pwrite::vFilePwrite<'a>, "vFile:fstat" => _vFile_fstat::vFileFstat, "vFile:unlink" => _vFile_unlink::vFileUnlink<'a>, "vFile:readlink" => _vFile_readlink::vFileReadlink<'a>, "vFile:setfs" => _vFile_setfs::vFileSetfs, } catch_syscalls use 'a { "QCatchSyscalls" => _QCatchSyscalls::QCatchSyscalls<'a>, } thread_extra_info use 'a { "qThreadExtraInfo" => _qThreadExtraInfo::qThreadExtraInfo<'a>, } lldb_register_info { "qRegisterInfo" => _qRegisterInfo::qRegisterInfo, } libraries_svr4 use 'a { "qXfer:libraries-svr4:read" => _qXfer_libraries_svr4_read::qXferLibrariesSvr4Read<'a>, } }