gdbstub/protocol/
commands.rs

1use crate::protocol::packet::PacketBuf;
2use crate::target::Target;
3use paste::paste;
4
5/// Common imports used by >50% of all packet parsers.
6///
7/// Do not clutter this prelude with types only used by a few packets.
8pub mod prelude {
9    pub use crate::protocol::commands::ParseCommand;
10    pub use crate::protocol::common::hex::decode_hex;
11    pub use crate::protocol::common::hex::decode_hex_buf;
12    pub use crate::protocol::packet::PacketBuf;
13    pub use core::convert::TryFrom;
14    pub use core::convert::TryInto;
15}
16
17pub trait ParseCommand<'a>: Sized {
18    /// Try to parse a packet from the packet buffer.
19    fn from_packet(buf: PacketBuf<'a>) -> Option<Self>;
20}
21
22macro_rules! commands {
23    (
24        $(
25            $ext:ident $(use $lt:lifetime)? {
26                $($name:literal => $mod:ident::$command:ident$(<$lifetime:lifetime>)?,)*
27            }
28        )*
29    ) => {paste! {
30        // Most packets follow a consistent model of "only enabled when a
31        // particular IDET is implemented", but there are some exceptions to
32        // this rule that need to be special-cased:
33        //
34        // # Breakpoint packets (z, Z)
35        //
36        // Breakpoint packets are special-cased, as the "Z" packet is parsed
37        // differently depending on whether or not the target implements the
38        // `Agent` extension.
39        //
40        // While it's entirely possible to eagerly parse the "Z" packet for
41        // bytecode, doing so would unnecessary bloat implementations that do
42        // not support evaluating agent expressions.
43
44
45        $($(
46            #[allow(non_snake_case, non_camel_case_types)]
47            pub mod $mod;
48        )*)*
49        pub mod breakpoint;
50
51        pub mod ext {
52            $(
53                #[allow(non_camel_case_types, clippy::enum_variant_names, clippy::upper_case_acronyms)]
54                pub enum [<$ext:camel>] $(<$lt>)? {
55                    $($command(super::$mod::$command<$($lifetime)?>),)*
56                }
57            )*
58
59            use super::breakpoint::{BasicBreakpoint, BytecodeBreakpoint};
60            #[allow(non_camel_case_types)]
61            pub enum Breakpoints<'a> {
62                z(BasicBreakpoint<'a>),
63                Z(BasicBreakpoint<'a>),
64                // Bytecode hasn't yet been plumbed all the way through
65                #[allow(dead_code)]
66                ZWithBytecode(BytecodeBreakpoint<'a>),
67            }
68
69        }
70
71        /// GDB commands
72        pub enum Command<'a> {
73            $(
74                [<$ext:camel>](ext::[<$ext:camel>]$(<$lt>)?),
75            )*
76            Breakpoints(ext::Breakpoints<'a>),
77            Unknown(&'a [u8]),
78        }
79
80        impl<'a> Command<'a> {
81            pub fn from_packet(
82                target: &mut impl Target,
83                mut buf: PacketBuf<'a>
84            ) -> Option<Command<'a>> {
85                // HACK: this locally-scoped trait enables using identifiers
86                // that aren't top-level `Target` IDETs to split-up the packet
87                // parsing code.
88                trait Hack {
89                    fn support_base(&mut self) -> Option<()>;
90                    fn support_target_xml(&mut self) -> Option<()>;
91                    fn support_lldb_register_info(&mut self) -> Option<()>;
92                    fn support_resume(&mut self) -> Option<()>;
93                    fn support_single_register_access(&mut self) -> Option<()>;
94                    fn support_reverse_step(&mut self) -> Option<()>;
95                    fn support_reverse_cont(&mut self) -> Option<()>;
96                    fn support_no_ack_mode(&mut self) -> Option<()>;
97                    fn support_x_upcase_packet(&mut self) -> Option<()>;
98                    fn support_thread_extra_info(&mut self) -> Option<()>;
99                }
100
101                impl<T: Target> Hack for T {
102                    fn support_base(&mut self) -> Option<()> {
103                        Some(())
104                    }
105
106                    fn support_target_xml(&mut self) -> Option<()> {
107                        use crate::arch::Arch;
108                        if self.use_target_description_xml()
109                            && (T::Arch::target_description_xml().is_some()
110                                || self.support_target_description_xml_override().is_some())
111                        {
112                            Some(())
113                        } else {
114                            None
115                        }
116                    }
117
118                    fn support_lldb_register_info(&mut self) -> Option<()> {
119                        use crate::arch::Arch;
120                        if self.use_lldb_register_info()
121                            && (T::Arch::lldb_register_info(usize::MAX).is_some()
122                                || self.support_lldb_register_info_override().is_some())
123                        {
124                            Some(())
125                        } else {
126                            None
127                        }
128                    }
129
130                    fn support_resume(&mut self) -> Option<()> {
131                        self.base_ops().resume_ops().map(drop)
132                    }
133
134                    fn support_single_register_access(&mut self) -> Option<()> {
135                        use crate::target::ext::base::BaseOps;
136                        match self.base_ops() {
137                            BaseOps::SingleThread(ops) => ops.support_single_register_access().map(drop),
138                            BaseOps::MultiThread(ops) => ops.support_single_register_access().map(drop),
139                        }
140                    }
141
142                    fn support_reverse_step(&mut self) -> Option<()> {
143                        use crate::target::ext::base::ResumeOps;
144                        match self.base_ops().resume_ops()? {
145                            ResumeOps::SingleThread(ops) => ops.support_reverse_step().map(drop),
146                            ResumeOps::MultiThread(ops) => ops.support_reverse_step().map(drop),
147                        }
148                    }
149
150                    fn support_reverse_cont(&mut self) -> Option<()> {
151                        use crate::target::ext::base::ResumeOps;
152                        match self.base_ops().resume_ops()? {
153                            ResumeOps::SingleThread(ops) => ops.support_reverse_cont().map(drop),
154                            ResumeOps::MultiThread(ops) => ops.support_reverse_cont().map(drop),
155                        }
156                    }
157
158                    fn support_x_upcase_packet(&mut self) -> Option<()> {
159                        if self.use_x_upcase_packet() {
160                            Some(())
161                        } else {
162                            None
163                        }
164                    }
165
166                    fn support_no_ack_mode(&mut self) -> Option<()> {
167                        if self.use_no_ack_mode() {
168                            Some(())
169                        } else {
170                            None
171                        }
172                    }
173
174                    fn support_thread_extra_info(&mut self) -> Option<()> {
175                        use crate::target::ext::base::BaseOps;
176                        match self.base_ops() {
177                            BaseOps::SingleThread(_) => None,
178                            BaseOps::MultiThread(ops) => ops.support_thread_extra_info().map(drop),
179                        }
180                    }
181                }
182
183                // TODO?: use tries for more efficient longest prefix matching
184
185                $(
186                #[allow(clippy::string_lit_as_bytes)]
187                if target.[< support_ $ext >]().is_some() {
188                    $(
189                    if buf.strip_prefix($name.as_bytes()) {
190                        crate::__dead_code_marker!($name, "prefix_match");
191
192                        let cmd = $mod::$command::from_packet(buf)?;
193
194                        return Some(
195                            Command::[<$ext:camel>](
196                                ext::[<$ext:camel>]::$command(cmd)
197                            )
198                        )
199                    }
200                    )*
201                }
202                )*
203
204                if let Some(_breakpoint_ops) = target.support_breakpoints() {
205                    use breakpoint::{BasicBreakpoint, BytecodeBreakpoint};
206
207                    if buf.strip_prefix(b"z") {
208                        let cmd = BasicBreakpoint::from_slice(buf.into_body())?;
209                        return Some(Command::Breakpoints(ext::Breakpoints::z(cmd)))
210                    }
211
212                    if buf.strip_prefix(b"Z") {
213                        // TODO: agent bytecode currently unimplemented
214                        if true {
215                            let cmd = BasicBreakpoint::from_slice(buf.into_body())?;
216                            return Some(Command::Breakpoints(ext::Breakpoints::Z(cmd)))
217                        } else {
218                            let cmd = BytecodeBreakpoint::from_slice(buf.into_body())?;
219                            return Some(Command::Breakpoints(ext::Breakpoints::ZWithBytecode(cmd)))
220                        }
221                    }
222                }
223
224                Some(Command::Unknown(buf.into_body()))
225            }
226        }
227    }};
228}
229
230commands! {
231    base use 'a {
232        "?" => question_mark::QuestionMark,
233        "D" => _d_upcase::D,
234        "g" => _g::g,
235        "G" => _g_upcase::G<'a>,
236        "H" => _h_upcase::H,
237        "k" => _k::k,
238        "m" => _m::m<'a>,
239        "M" => _m_upcase::M<'a>,
240        "qAttached" => _qAttached::qAttached,
241        "qfThreadInfo" => _qfThreadInfo::qfThreadInfo,
242        "qsThreadInfo" => _qsThreadInfo::qsThreadInfo,
243        "qSupported" => _qSupported::qSupported<'a>,
244        "T" => _t_upcase::T,
245        "vKill" => _vKill::vKill,
246    }
247
248    target_xml use 'a {
249        "qXfer:features:read" => _qXfer_features_read::qXferFeaturesRead<'a>,
250    }
251
252    resume use 'a {
253        "c" => _c::c<'a>,
254        "s" => _s::s<'a>,
255        "vCont" => _vCont::vCont<'a>,
256    }
257
258    x_upcase_packet use 'a {
259        "X" => _x_upcase::X<'a>,
260    }
261
262    no_ack_mode {
263        "QStartNoAckMode" => _QStartNoAckMode::QStartNoAckMode,
264    }
265
266    single_register_access use 'a {
267        "p" => _p::p<'a>,
268        "P" => _p_upcase::P<'a>,
269    }
270
271    extended_mode use 'a {
272        "!" => exclamation_mark::ExclamationMark,
273        "qC" => _qC::qC,
274        "QDisableRandomization" => _QDisableRandomization::QDisableRandomization,
275        "QEnvironmentHexEncoded" => _QEnvironmentHexEncoded::QEnvironmentHexEncoded<'a>,
276        "QEnvironmentReset" => _QEnvironmentReset::QEnvironmentReset,
277        "QEnvironmentUnset" => _QEnvironmentUnset::QEnvironmentUnset<'a>,
278        "QSetWorkingDir" => _QSetWorkingDir::QSetWorkingDir<'a>,
279        "QStartupWithShell" => _QStartupWithShell::QStartupWithShell,
280        "R" => _r_upcase::R,
281        "vAttach" => _vAttach::vAttach,
282        "vRun" => _vRun::vRun<'a>,
283    }
284
285    monitor_cmd use 'a {
286        "qRcmd" => _qRcmd::qRcmd<'a>,
287    }
288
289    section_offsets {
290        "qOffsets" => _qOffsets::qOffsets,
291    }
292
293    reverse_cont {
294        "bc" => _bc::bc,
295    }
296
297    reverse_step {
298        "bs" => _bs::bs,
299    }
300
301    memory_map use 'a {
302        "qXfer:memory-map:read" => _qXfer_memory_map::qXferMemoryMapRead<'a>,
303    }
304
305    flash_operations use 'a {
306        "vFlashErase" => _vFlashErase::vFlashErase<'a>,
307        "vFlashWrite" => _vFlashWrite::vFlashWrite<'a>,
308        "vFlashDone" => _vFlashDone::vFlashDone,
309    }
310
311    auxv use 'a {
312        "qXfer:auxv:read" => _qXfer_auxv_read::qXferAuxvRead<'a>,
313    }
314
315    exec_file use 'a {
316        "qXfer:exec-file:read" => _qXfer_exec_file::qXferExecFileRead<'a>,
317    }
318
319    host_io use 'a {
320        "vFile:open" => _vFile_open::vFileOpen<'a>,
321        "vFile:close" => _vFile_close::vFileClose,
322        "vFile:pread" => _vFile_pread::vFilePread<'a>,
323        "vFile:pwrite" => _vFile_pwrite::vFilePwrite<'a>,
324        "vFile:fstat" => _vFile_fstat::vFileFstat,
325        "vFile:unlink" => _vFile_unlink::vFileUnlink<'a>,
326        "vFile:readlink" => _vFile_readlink::vFileReadlink<'a>,
327        "vFile:setfs" => _vFile_setfs::vFileSetfs,
328    }
329
330    catch_syscalls use 'a {
331        "QCatchSyscalls" => _QCatchSyscalls::QCatchSyscalls<'a>,
332    }
333
334    thread_extra_info use 'a {
335        "qThreadExtraInfo" => _qThreadExtraInfo::qThreadExtraInfo<'a>,
336    }
337
338    lldb_register_info {
339        "qRegisterInfo" => _qRegisterInfo::qRegisterInfo,
340    }
341
342    libraries_svr4 use 'a {
343        "qXfer:libraries-svr4:read" => _qXfer_libraries_svr4_read::qXferLibrariesSvr4Read<'a>,
344    }
345
346    tracepoints use 'a {
347        "QTDPsrc" => _QTDPsrc::QTDPsrc<'a>,
348        "QTDP" => _QTDP::QTDP<'a>,
349        "QTinit" => _QTinit::QTinit,
350        "QTBuffer" => _QTBuffer_upcase::QTBuffer,
351        "QTStart" => _QTStart::QTStart,
352        "QTStop" => _QTStop::QTStop,
353        "QTFrame" => _QTFrame::QTFrame<'a>,
354
355        "qTBuffer" => _qTBuffer::qTBuffer,
356        "qTStatus" => _qTStatus::qTStatus,
357        "qTP" => _qTP::qTP<'a>,
358        "qTfP" => _qTfP::qTfP,
359        "qTsP" => _qTsP::qTsP,
360
361        // QTDV is unimplemented.
362        "qTfV" => _qTfV::qTfV,
363        "qTsV" => _qTsV::qTsV,
364    }
365}