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 8namespace v8 { 9namespace internal { 10namespace wasm { 11namespace gdb_server { 12 13Packet::Packet() { 14 seq_ = -1; 15 Clear(); 16} 17 18void Packet::Clear() { 19 data_.clear(); 20 read_index_ = 0; 21} 22 23void Packet::Rewind() { read_index_ = 0; } 24 25bool Packet::EndOfPacket() const { return (read_index_ >= GetPayloadSize()); } 26 27void Packet::AddRawChar(char ch) { data_.push_back(ch); } 28 29void Packet::AddWord8(uint8_t byte) { 30 char seq[2]; 31 UInt8ToHex(byte, seq); 32 AddRawChar(seq[0]); 33 AddRawChar(seq[1]); 34} 35 36void 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 46void Packet::AddString(const char* str) { 47 DCHECK(str); 48 49 while (*str) { 50 AddRawChar(*str); 51 str++; 52 } 53} 54 55void Packet::AddHexString(const char* str) { 56 DCHECK(str); 57 58 while (*str) { 59 AddWord8(*str); 60 str++; 61 } 62} 63 64void 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 111bool 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 167bool 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 221bool 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 232bool 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 248bool 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 258bool 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 284const char* Packet::GetPayload() const { return data_.c_str(); } 285 286size_t Packet::GetPayloadSize() const { return data_.size(); } 287 288bool 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 299void 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 313void Packet::SetSequence(int32_t val) { seq_ = val; } 314 315void Packet::SetError(ErrDef error) { 316 Clear(); 317 AddRawChar('E'); 318 AddWord8(static_cast<uint8_t>(error)); 319} 320 321std::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