• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/debug/wasm/gdb-server/packet.h"
6 #include "src/debug/wasm/gdb-server/gdb-remote-util.h"
7 
8 namespace v8 {
9 namespace internal {
10 namespace wasm {
11 namespace gdb_server {
12 
Packet()13 Packet::Packet() {
14   seq_ = -1;
15   Clear();
16 }
17 
Clear()18 void Packet::Clear() {
19   data_.clear();
20   read_index_ = 0;
21 }
22 
Rewind()23 void Packet::Rewind() { read_index_ = 0; }
24 
EndOfPacket() const25 bool Packet::EndOfPacket() const { return (read_index_ >= GetPayloadSize()); }
26 
AddRawChar(char ch)27 void Packet::AddRawChar(char ch) { data_.push_back(ch); }
28 
AddWord8(uint8_t byte)29 void Packet::AddWord8(uint8_t byte) {
30   char seq[2];
31   UInt8ToHex(byte, seq);
32   AddRawChar(seq[0]);
33   AddRawChar(seq[1]);
34 }
35 
AddBlock(const void * ptr,uint32_t len)36 void Packet::AddBlock(const void* ptr, uint32_t len) {
37   DCHECK(ptr);
38 
39   const char* p = (const char*)ptr;
40 
41   for (uint32_t offs = 0; offs < len; offs++) {
42     AddWord8(p[offs]);
43   }
44 }
45 
AddString(const char * str)46 void Packet::AddString(const char* str) {
47   DCHECK(str);
48 
49   while (*str) {
50     AddRawChar(*str);
51     str++;
52   }
53 }
54 
AddHexString(const char * str)55 void Packet::AddHexString(const char* str) {
56   DCHECK(str);
57 
58   while (*str) {
59     AddWord8(*str);
60     str++;
61   }
62 }
63 
AddNumberSep(uint64_t val,char sep)64 void Packet::AddNumberSep(uint64_t val, char sep) {
65   char out[sizeof(val) * 2];
66   char temp[2];
67 
68   // Check for -1 optimization
69   if (val == static_cast<uint64_t>(-1)) {
70     AddRawChar('-');
71     AddRawChar('1');
72   } else {
73     int nibbles = 0;
74 
75     // In the GDB remote protocol numbers are formatted as big-endian hex
76     // strings. Leading zeros can be skipped.
77     // For example the value 0x00001234 is formatted as "1234".
78     for (size_t a = 0; a < sizeof(val); a++) {
79       uint8_t byte = static_cast<uint8_t>(val & 0xFF);
80 
81       // Stream in with bytes reversed, starting with the least significant.
82       // So if we have the value 0x00001234, we store 4, then 3, 2, 1.
83       // Note that the characters are later reversed to be in big-endian order.
84       UInt8ToHex(byte, temp);
85       out[nibbles++] = temp[1];
86       out[nibbles++] = temp[0];
87 
88       // Get the next 8 bits;
89       val >>= 8;
90 
91       // Suppress leading zeros, so we are done when val hits zero
92       if (val == 0) {
93         break;
94       }
95     }
96 
97     // Strip the high zero for this byte if present.
98     if ((nibbles > 1) && (out[nibbles - 1] == '0')) nibbles--;
99 
100     // Now write it out reverse to correct the order
101     while (nibbles) {
102       nibbles--;
103       AddRawChar(out[nibbles]);
104     }
105   }
106 
107   // If we asked for a separator, insert it
108   if (sep) AddRawChar(sep);
109 }
110 
GetNumberSep(uint64_t * val,char * sep)111 bool Packet::GetNumberSep(uint64_t* val, char* sep) {
112   uint64_t out = 0;
113   char ch;
114   if (!GetRawChar(&ch)) {
115     return false;
116   }
117 
118   // Numbers are formatted as a big-endian hex strings.
119   // The literals "0" and "-1" as special cases.
120 
121   // Check for -1
122   if (ch == '-') {
123     if (!GetRawChar(&ch)) {
124       return false;
125     }
126 
127     if (ch == '1') {
128       *val = (uint64_t)-1;
129 
130       ch = 0;
131       GetRawChar(&ch);
132       if (sep) {
133         *sep = ch;
134       }
135       return true;
136     }
137     return false;
138   }
139 
140   do {
141     uint8_t nib;
142 
143     // Check for separator
144     if (!NibbleToUInt8(ch, &nib)) {
145       break;
146     }
147 
148     // Add this nibble.
149     out = (out << 4) + nib;
150 
151     // Get the next character (if availible)
152     ch = 0;
153     if (!GetRawChar(&ch)) {
154       break;
155     }
156   } while (1);
157 
158   // Set the value;
159   *val = out;
160 
161   // Add the separator if the user wants it...
162   if (sep != nullptr) *sep = ch;
163 
164   return true;
165 }
166 
GetRawChar(char * ch)167 bool Packet::GetRawChar(char* ch) {
168   DCHECK(ch != nullptr);
169 
170   if (read_index_ >= GetPayloadSize()) return false;
171 
172   *ch = data_[read_index_++];
173 
174   // Check for RLE X*N, where X is the value, N is the reps.
175   if (*ch == '*') {
176     if (read_index_ < 2) {
177       TRACE_GDB_REMOTE("Unexpected RLE at start of packet.\n");
178       return false;
179     }
180 
181     if (read_index_ >= GetPayloadSize()) {
182       TRACE_GDB_REMOTE("Unexpected EoP during RLE.\n");
183       return false;
184     }
185 
186     // GDB does not use "CTRL" characters in the stream, so the
187     // number of reps is encoded as the ASCII value beyond 28
188     // (which when you add a min rep size of 4, forces the rep
189     // character to be ' ' (32) or greater).
190     int32_t cnt = (data_[read_index_] - 28);
191     if (cnt < 3) {
192       TRACE_GDB_REMOTE("Unexpected RLE length.\n");
193       return false;
194     }
195 
196     // We have just read '*' and incremented the read pointer,
197     // so here is the old state, and expected new state.
198     //
199     //   Assume N = 5, we grow by N - size of encoding (3).
200     //
201     // OldP:       R  W
202     // OldD:  012X*N89 = 8 chars
203     // Size:  012X*N89__ = 10 chars
204     // Move:  012X*__N89 = 10 chars
205     // Fill:  012XXXXX89 = 10 chars
206     // NewP:       R    W  (shifted 5 - 3)
207 
208     // First, store the remaining characters to the right into a temp string.
209     std::string right = data_.substr(read_index_ + 1);
210     // Discard the '*' we just read
211     data_.erase(read_index_ - 1);
212     // Append (N-1) 'X' chars
213     *ch = data_[read_index_ - 2];
214     data_.append(cnt - 1, *ch);
215     // Finally, append the remaining characters
216     data_.append(right);
217   }
218   return true;
219 }
220 
GetWord8(uint8_t * value)221 bool Packet::GetWord8(uint8_t* value) {
222   DCHECK(value);
223 
224   // Get two ASCII hex values and convert them to ints
225   char seq[2];
226   if (!GetRawChar(&seq[0]) || !GetRawChar(&seq[1])) {
227     return false;
228   }
229   return HexToUInt8(seq, value);
230 }
231 
GetBlock(void * ptr,uint32_t len)232 bool Packet::GetBlock(void* ptr, uint32_t len) {
233   DCHECK(ptr);
234 
235   uint8_t* p = reinterpret_cast<uint8_t*>(ptr);
236   bool res = true;
237 
238   for (uint32_t offs = 0; offs < len; offs++) {
239     res = GetWord8(&p[offs]);
240     if (false == res) {
241       break;
242     }
243   }
244 
245   return res;
246 }
247 
GetString(std::string * str)248 bool Packet::GetString(std::string* str) {
249   if (EndOfPacket()) {
250     return false;
251   }
252 
253   *str = data_.substr(read_index_);
254   read_index_ = GetPayloadSize();
255   return true;
256 }
257 
GetHexString(std::string * str)258 bool Packet::GetHexString(std::string* str) {
259   // Decode a string encoded as a series of 2-hex digit pairs.
260 
261   if (EndOfPacket()) {
262     return false;
263   }
264 
265   // Pull values until we hit a separator
266   str->clear();
267   char ch1;
268   while (GetRawChar(&ch1)) {
269     uint8_t nib1;
270     if (!NibbleToUInt8(ch1, &nib1)) {
271       read_index_--;
272       break;
273     }
274     char ch2;
275     uint8_t nib2;
276     if (!GetRawChar(&ch2) || !NibbleToUInt8(ch2, &nib2)) {
277       return false;
278     }
279     *str += static_cast<char>((nib1 << 4) + nib2);
280   }
281   return true;
282 }
283 
GetPayload() const284 const char* Packet::GetPayload() const { return data_.c_str(); }
285 
GetPayloadSize() const286 size_t Packet::GetPayloadSize() const { return data_.size(); }
287 
GetSequence(int32_t * ch) const288 bool Packet::GetSequence(int32_t* ch) const {
289   DCHECK(ch);
290 
291   if (seq_ != -1) {
292     *ch = seq_;
293     return true;
294   }
295 
296   return false;
297 }
298 
ParseSequence()299 void Packet::ParseSequence() {
300   size_t saved_read_index = read_index_;
301   unsigned char seq;
302   char ch;
303   if (GetWord8(&seq) && GetRawChar(&ch)) {
304     if (ch == ':') {
305       SetSequence(seq);
306       return;
307     }
308   }
309   // No sequence number present, so reset to original position.
310   read_index_ = saved_read_index;
311 }
312 
SetSequence(int32_t val)313 void Packet::SetSequence(int32_t val) { seq_ = val; }
314 
SetError(ErrDef error)315 void Packet::SetError(ErrDef error) {
316   Clear();
317   AddRawChar('E');
318   AddWord8(static_cast<uint8_t>(error));
319 }
320 
GetPacketData() const321 std::string Packet::GetPacketData() const {
322   char chars[2];
323   const char* ptr = GetPayload();
324   size_t size = GetPayloadSize();
325 
326   std::stringstream outstr;
327 
328   // Signal start of response
329   outstr << '$';
330 
331   char run_xsum = 0;
332 
333   // If there is a sequence, send as two nibble 8bit value + ':'
334   int32_t seq;
335   if (GetSequence(&seq)) {
336     UInt8ToHex(seq, chars);
337     outstr << chars[0];
338     run_xsum += chars[0];
339     outstr << chars[1];
340     run_xsum += chars[1];
341 
342     outstr << ':';
343     run_xsum += ':';
344   }
345 
346   // Send the main payload
347   for (size_t offs = 0; offs < size; ++offs) {
348     outstr << ptr[offs];
349     run_xsum += ptr[offs];
350   }
351 
352   // Send XSUM as two nibble 8bit value preceeded by '#'
353   outstr << '#';
354   UInt8ToHex(run_xsum, chars);
355   outstr << chars[0];
356   outstr << chars[1];
357 
358   return outstr.str();
359 }
360 
361 }  // namespace gdb_server
362 }  // namespace wasm
363 }  // namespace internal
364 }  // namespace v8
365