Skip to main content

gdbstub/protocol/
packet.rs

1use crate::protocol::commands::Command;
2use crate::protocol::common::hex::decode_hex;
3use crate::target::Target;
4
5/// Packet parse error.
6#[derive(Debug)]
7pub enum PacketParseError {
8    #[allow(dead_code)] // used as part of Debug impl
9    ChecksumMismatched {
10        checksum: u8,
11        calculated: u8,
12    },
13    EmptyBuf,
14    MissingChecksum,
15    MalformedChecksum,
16    MalformedCommand,
17    #[allow(dead_code)] // used as part of Debug impl
18    UnexpectedHeader(u8),
19}
20
21/// Top-Level GDB packet
22pub enum Packet<'a> {
23    Ack,
24    Nack,
25    Interrupt,
26    Command(Command<'a>),
27}
28
29/// Wrapper around a byte buffer containing a GDB packet, while also tracking
30/// the range of the buffer containing the packet's "body".
31///
32/// A newly constructed `PacketBuf` will have a body that spans the entire data
33/// portion of the packet (i.e: `b"$data#checksum"`), but this range can be
34/// further restricted as part of packet parsing.
35///
36/// Notably, `PacketBuf` will _always_ maintain a mutable reference back to the
37/// _entire_ underlying packet buffer. This makes it possible to re-use any
38/// unused buffer space as "scratch" space. One notable example of this use-case
39/// is the 'm' packet, which recycles unused packet buffer space as a buffer for
40/// the target's `read_memory` method.
41pub struct PacketBuf<'a> {
42    buf: &'a mut [u8],
43    body_range: core::ops::Range<usize>,
44}
45
46impl<'a> PacketBuf<'a> {
47    /// Validate the contents of the raw packet buffer, checking for checksum
48    /// consistency and structural correctness.
49    pub fn new(pkt_buf: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError> {
50        if pkt_buf.is_empty() {
51            return Err(PacketParseError::EmptyBuf);
52        }
53
54        // split buffer into body and checksum components
55        let mut parts = pkt_buf[1..].split(|b| *b == b'#');
56
57        let body = parts.next().unwrap(); // spit iter always returns at least one element
58        let checksum = parts
59            .next()
60            .ok_or(PacketParseError::MissingChecksum)?
61            .get(..2)
62            .ok_or(PacketParseError::MalformedChecksum)?;
63
64        // validate the checksum
65        let checksum = decode_hex(checksum).map_err(|_| PacketParseError::MalformedChecksum)?;
66        let calculated = body.iter().fold(0u8, |a, x| a.wrapping_add(*x));
67        if calculated != checksum {
68            return Err(PacketParseError::ChecksumMismatched {
69                checksum,
70                calculated,
71            });
72        }
73
74        let body_range = 1..(body.len() + 1); // compensate for the leading '$'
75
76        Ok(PacketBuf {
77            buf: pkt_buf,
78            body_range,
79        })
80    }
81
82    /// (used for tests) Create a packet buffer from a raw body buffer, skipping
83    /// the header/checksum trimming stage.
84    #[cfg(test)]
85    pub fn new_with_raw_body(body: &'a mut [u8]) -> Result<PacketBuf<'a>, PacketParseError> {
86        let len = body.len();
87        Ok(PacketBuf {
88            buf: body,
89            body_range: 0..len,
90        })
91    }
92
93    /// Strip the specified prefix from the packet buffer, returning `true` if
94    /// there was a prefix match.
95    pub fn strip_prefix(&mut self, prefix: &[u8]) -> bool {
96        let body = {
97            // SAFETY: The public interface of `PacketBuf` ensures that `self.body_range`
98            // always stays within the bounds of the provided buffer.
99            #[cfg(not(feature = "paranoid_unsafe"))]
100            unsafe {
101                self.buf.get_unchecked_mut(self.body_range.clone())
102            }
103
104            #[cfg(feature = "paranoid_unsafe")]
105            &mut self.buf[self.body_range.clone()]
106        };
107
108        if body.starts_with(prefix) {
109            // SAFETY: if the current buffer range `starts_with` the specified prefix, then
110            // it is safe to bump `body_range.start` by the prefix length.
111            self.body_range = (self.body_range.start + prefix.len())..self.body_range.end;
112            true
113        } else {
114            false
115        }
116    }
117
118    /// Return a mutable reference to slice of the packet buffer corresponding
119    /// to the current body.
120    pub fn into_body(self) -> &'a mut [u8] {
121        // SAFETY: The public interface of `PacketBuf` ensures that `self.body_range`
122        // always stays within the bounds of the provided buffer.
123        #[cfg(not(feature = "paranoid_unsafe"))]
124        unsafe {
125            self.buf.get_unchecked_mut(self.body_range)
126        }
127
128        #[cfg(feature = "paranoid_unsafe")]
129        &mut self.buf[self.body_range]
130    }
131
132    /// Return a mutable reference to the _entire_ underlying packet buffer, and
133    /// the current body's range.
134    pub fn into_raw_buf(self) -> (&'a mut [u8], core::ops::Range<usize>) {
135        (self.buf, self.body_range)
136    }
137
138    /// Returns the length of the _entire_ underlying packet buffer - not just
139    /// the length of the current range.
140    ///
141    /// This method is used when handing the `qSupported` packet in order to
142    /// obtain the maximum packet size the stub supports.
143    pub fn full_len(&self) -> usize {
144        self.buf.len()
145    }
146}
147
148impl<'a> Packet<'a> {
149    pub fn from_buf(
150        target: &mut impl Target,
151        buf: &'a mut [u8],
152    ) -> Result<Packet<'a>, PacketParseError> {
153        // cannot have empty packet
154        if buf.is_empty() {
155            return Err(PacketParseError::EmptyBuf);
156        }
157
158        match buf[0] {
159            b'$' => Ok(Packet::Command(
160                Command::from_packet(target, PacketBuf::new(buf)?)
161                    .ok_or(PacketParseError::MalformedCommand)?,
162            )),
163            b'+' => Ok(Packet::Ack),
164            b'-' => Ok(Packet::Nack),
165            0x03 => Ok(Packet::Interrupt),
166            _ => Err(PacketParseError::UnexpectedHeader(buf[0])),
167        }
168    }
169}