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}