From 697e61850f430f5d906b0396ebf7754390e9bcf0 Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Wed, 5 Feb 2020 22:52:37 +0000 Subject: [PATCH] thermosphere: rewrite packet comms --- thermosphere/src/defines.hpp | 2 +- thermosphere/src/gdb/hvisor_gdb_comms.cpp | 222 +++++++++++ thermosphere/src/gdb/hvisor_gdb_context.hpp | 5 +- .../src/gdb/hvisor_gdb_packet_data.cpp | 10 +- .../src/gdb/hvisor_gdb_packet_data.hpp | 17 + thermosphere/src/gdb/hvisor_gdb_thread.cpp | 23 +- thermosphere/src/gdb/net.c | 371 ------------------ thermosphere/src/gdb/net.h | 29 -- 8 files changed, 261 insertions(+), 418 deletions(-) create mode 100644 thermosphere/src/gdb/hvisor_gdb_comms.cpp delete mode 100644 thermosphere/src/gdb/net.c delete mode 100644 thermosphere/src/gdb/net.h diff --git a/thermosphere/src/defines.hpp b/thermosphere/src/defines.hpp index 7cee9f985..05aa8b135 100644 --- a/thermosphere/src/defines.hpp +++ b/thermosphere/src/defines.hpp @@ -24,4 +24,4 @@ #include #include -using std::size_t; \ No newline at end of file +#include "debug_log.h" diff --git a/thermosphere/src/gdb/hvisor_gdb_comms.cpp b/thermosphere/src/gdb/hvisor_gdb_comms.cpp new file mode 100644 index 000000000..f22fb1eb5 --- /dev/null +++ b/thermosphere/src/gdb/hvisor_gdb_comms.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "hvisor_gdb_defines_internal.hpp" +#include "hvisor_gdb_packet_data.hpp" + +namespace { + + void WriteAck(TransportInterface *iface) + { + char c = '+'; + transportInterfaceWriteData(iface, &c, 1); + } + + int WriteNack(TransportInterface *iface) + { + char c = '-'; + transportInterfaceWriteData(iface, &c, 1); + return 1; + } + +} + +namespace ams::hvisor::gdb { + + int Context::ReceivePacket() + { + char hdr; + bool ctrlC = false; + TransportInterface *iface = m_transportInterface; + + // Read the first character... + transportInterfaceReadData(iface, &hdr, 1); + + switch (hdr) { + case '+': { + // Ack, don't do anything else except maybe NoAckMode state transition + if (m_noAckSent) { + m_noAck = true; + m_noAckSent = false; + } + return 0; + } + case '-': + // Nack, return the previous packet + transportInterfaceWriteData(iface, m_buffer, m_lastSentPacketSize); + return m_lastSentPacketSize; + case '$': + // Normal packet, handled below + break; + case '\x03': + // Normal packet (Control-C), handled below + ctrlC = true; + break; + default: + // Oops, send a nack + DEBUG("Received a packed with an invalid header from GDB, hdr=%c\n", hdr); + return WriteNack(iface); + } + + // We didn't get a nack past this point, read the remaining data if any + + m_buffer[0] = hdr; + if (ctrlC) { + // Will never normally happen, but ok + if (m_state < State::Attached) { + DEBUG("Received connection from GDB, now attaching...\n"); + Attach(); + m_state = State::Attached; + } + return 1; + } + + size_t delimPos = transportInterfaceReadDataUntil(iface, m_buffer + 1, 4 + GDB_BUF_LEN - 1, '#'); + if (m_buffer[delimPos] != '#') { + // The packet is malformed, send a nack + return WriteNack(iface); + } + + m_commandData = std::string_view{m_buffer + 1, delimPos}; + + // Read the checksum + size_t checksumPos = delimPos + 1; + transportInterfaceReadData(iface, m_buffer + checksumPos, 2); + + auto checksumOpt = DecodeHexByte(std::string_view{m_buffer + checksumPos, 2}); + + if (!checksumOpt || *checksumOpt != ComputeChecksum(m_commandData)) { + // Malformed or invalid checksum + return WriteNack(iface); + } else if (!m_noAck) { + WriteAck(iface); + } + + // State transitions... + if (m_state < State::Attached) { + DEBUG("Received connection from GDB, now attaching...\n"); + Attach(); + m_state = State::Attached; + } + + // Debug + /*m_buffer[checksumPos + 2] = '\0'; + DEBUGRAW("->"); + DEBUGRAW(m_buffer); + DEBUGRAW("\n");*/ + + return static_cast(delimPos + 2); + } + + int Context::DoSendPacket(size_t len) + { + transportInterfaceWriteData(m_transportInterface, m_buffer, len); + m_lastSentPacketSize = len; + + // Debugging: + /*m_buffer[len] = 0; + DEBUGRAW("<-"); + DEBUGRAW(ctx->buffer); + DEBUGRAW("\n");*/ + + return static_cast(len); + } + + int Context::SendPacket(std::string_view packetData, char hdr) + { + u8 checksum = ComputeChecksum(packetData); + if (packetData.data() != m_buffer + 1) { + std::memmove(m_buffer + 1, packetData.data(), packetData.size()); + } + + size_t checksumPos = 1 + packetData.size() + 1; + m_buffer[0] = '$'; + m_buffer[checksumPos - 1] = '#'; + EncodeHex(m_buffer + checksumPos, &checksum, 1); + + return DoSendPacket(4 + packetData.size()); + } + + int Context::SendFormattedPacket(const char *packetDataFmt, ...) + { + va_list args; + + va_start(args, packetDataFmt); + int n = vsprintf(m_buffer + 1, packetDataFmt, args); + va_end(args); + + if (n < 0) { + return -1; + } else { + return SendPacket(std::string_view{m_buffer + 1, n}); + } + } + + int Context::SendHexPacket(const void *packetData, size_t len) + { + if (4 + 2 * len < GDB_BUF_LEN) { + return -1; + } + + EncodeHex(m_buffer + 1, packetData, len); + return SendPacket(std::string_view{m_buffer + 1, 2 * len}); + } + + int Context::SendStreamData(std::string_view streamData, size_t offset, size_t length, bool forceEmptyLast) + { + size_t totalSize = streamData.size(); + + // GDB_BUF_LEN does not include the usual $#<1-byte checksum> + length = std::min(length, GDB_BUF_LEN - 1ul); + + char letter; + + if ((forceEmptyLast && offset >= totalSize) || (!forceEmptyLast && offset + length >= totalSize)) { + length = offset >= totalSize ? 0 : totalSize - offset; + letter = 'l'; + } else { + letter = 'm'; + } + + // Note: ctx->buffer[0] = '$' + if (streamData.data() + offset != m_buffer + 2) { + memmove(m_buffer + 2, streamData.data() + offset, length); + } + + m_buffer[1] = letter; + return SendPacket(std::string_view{m_buffer + 1, 1 + length}); + } + + int Context::ReplyOk() + { + return SendPacket("OK"); + } + + int Context::ReplyEmpty() + { + return SendPacket(""); + } + + int Context::ReplyErrno(int no) + { + u8 no8 = static_cast(no); + char resp[] = "E00"; + EncodeHex(resp + 1, &no8, 1); + return SendPacket(resp); + } +} diff --git a/thermosphere/src/gdb/hvisor_gdb_context.hpp b/thermosphere/src/gdb/hvisor_gdb_context.hpp index d65b6dc63..6cdab8d9c 100644 --- a/thermosphere/src/gdb/hvisor_gdb_context.hpp +++ b/thermosphere/src/gdb/hvisor_gdb_context.hpp @@ -110,11 +110,10 @@ namespace ams::hvisor::gdb { // Comms int ReceivePacket(); - int DoSendPacket(); - int SendPacket(std::string_view packetData); + int DoSendPacket(size_t len); + int SendPacket(std::string_view packetData, char hdr = '$'); int SendFormattedPacket(const char *packetDataFmt, ...); int SendHexPacket(const void *packetData, size_t len); - int SendNotificationPacket(std::string_view packetData); int SendStreamData(std::string_view streamData, size_t offset, size_t length, bool forceEmptyLast); int ReplyOk(); int ReplyEmpty(); diff --git a/thermosphere/src/gdb/hvisor_gdb_packet_data.cpp b/thermosphere/src/gdb/hvisor_gdb_packet_data.cpp index 5432ce2f0..68f1e33a0 100644 --- a/thermosphere/src/gdb/hvisor_gdb_packet_data.cpp +++ b/thermosphere/src/gdb/hvisor_gdb_packet_data.cpp @@ -17,7 +17,6 @@ #pragma once #include "hvisor_gdb_packet_data.hpp" -#include namespace ams::hvisor::gdb { @@ -44,14 +43,13 @@ namespace ams::hvisor::gdb { size_t i = 0; u8 *dst8 = reinterpret_cast(dst); for (i = 0; i < data.size() / 2; i++) { - auto v1 = DecodeHexDigit(data[2 * i]); - auto v2 = DecodeHexDigit(data[2 * i + 1]); - - if (v1 >= 16 || v2 >= 16) { + auto bOpt = DecodeHexByte(data); + if (!bOpt) { return i; } - dst8[i] = (v1 << 4) | v2; + dst8[i] = *bOpt; + data.remove_prefix(2); } return i; diff --git a/thermosphere/src/gdb/hvisor_gdb_packet_data.hpp b/thermosphere/src/gdb/hvisor_gdb_packet_data.hpp index cfee0afdd..2988659b5 100644 --- a/thermosphere/src/gdb/hvisor_gdb_packet_data.hpp +++ b/thermosphere/src/gdb/hvisor_gdb_packet_data.hpp @@ -150,9 +150,26 @@ namespace ams::hvisor::gdb { return ParseIntegerList(str, 16, false, ',', lastSep); } + constexpr std::optional DecodeHexByte(std::string_view data) + { + if (data.size() < 2) { + return {}; + } + + auto v1 = DecodeHexDigit(data[0]); + auto v2 = DecodeHexDigit(data[1]); + + if (v1 >= 16 || v2 >= 16) { + return {}; + } + + return (v1 << 4) | v2; + } + u8 ComputeChecksum(std::string_view packetData); size_t EncodeHex(char *dst, const void *src, size_t len); size_t DecodeHex(void *dst, std::string_view data); + size_t EscapeBinaryData(size_t *encodedCount, void *dst, const void *src, size_t len, size_t maxLen); size_t UnescapeBinaryData(void *dst, const void *src, size_t len); diff --git a/thermosphere/src/gdb/hvisor_gdb_thread.cpp b/thermosphere/src/gdb/hvisor_gdb_thread.cpp index 8c9780025..abde4d81e 100644 --- a/thermosphere/src/gdb/hvisor_gdb_thread.cpp +++ b/thermosphere/src/gdb/hvisor_gdb_thread.cpp @@ -1,20 +1,27 @@ /* -* This file is part of Luma3DS. -* Copyright (C) 2016-2019 Aurora Wright, TuxSH -* -* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) -*/ + * Copyright (c) 2019-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include +#include "hvisor_gdb_thread.hpp" #include "hvisor_gdb_defines_internal.hpp" #include "hvisor_gdb_packet_data.hpp" #include "../core_ctx.h" -namespace { -} - namespace ams::hvisor::gdb { int ConvertTidToCoreId(unsigned long tid) diff --git a/thermosphere/src/gdb/net.c b/thermosphere/src/gdb/net.c deleted file mode 100644 index 5aebdbefb..000000000 --- a/thermosphere/src/gdb/net.c +++ /dev/null @@ -1,371 +0,0 @@ -/* -* This file is part of Luma3DS. -* Copyright (C) 2016-2019 Aurora Wright, TuxSH -* -* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) -*/ - -#include "net.h" - -#include -#include -#include -#include -#include "../pattern_utils.h" - -u8 GDB_ComputeChecksum(const char *packetData, size_t len) -{ - unsigned long cksum = 0; - for(size_t i = 0; i < len; i++) { - cksum += packetData[i]; - } - - return (u8)cksum; -} - -size_t GDB_EncodeHex(char *dst, const void *src, size_t len) -{ - static const char *alphabet = "0123456789abcdef"; - const u8 *src8 = (const u8 *)src; - - for (size_t i = 0; i < len; i++) { - dst[2 * i] = alphabet[(src8[i] & 0xf0) >> 4]; - dst[2 * i + 1] = alphabet[src8[i] & 0x0f]; - } - - return 2 * len; -} - -static inline u32 GDB_DecodeHexDigit(char src, bool *ok) -{ - *ok = true; - switch (src) { - case '0' ... '9': return 0 + (src - '0'); - case 'a' ... 'f': return 10 + (src - 'a'); - case 'A' ... 'F': return 10 + (src - 'A'); - default: - *ok = false; - return 0; - } -} - -size_t GDB_DecodeHex(void *dst, const char *src, size_t len) { - size_t i = 0; - u8 *dst8 = (u8 *)dst; - for (i = 0; i < len && src[2 * i] != 0 && src[2 * i + 1] != 0; i++) { - bool ok1, ok2; - dst8[i] = GDB_DecodeHexDigit(src[2 * i], &ok1) << 4; - dst8[i] |= GDB_DecodeHexDigit(src[2 * i + 1], &ok2); - if (!ok1 || !ok2) { - return i; - } - } - - return i; -} - -size_t GDB_EscapeBinaryData(size_t *encodedCount, void *dst, const void *src, size_t len, size_t maxLen) -{ - u8 *dst8 = (u8 *)dst; - const u8 *src8 = (const u8 *)src; - - maxLen = maxLen >= len ? len : maxLen; - - while ((uintptr_t)dst8 < (uintptr_t)dst + maxLen) { - if (*src8 == '$' || *src8 == '#' || *src8 == '}' || *src8 == '*') { - if ((uintptr_t)dst8 + 1 >= (uintptr_t)dst + maxLen) { - break; - } - *dst8++ = '}'; - *dst8++ = *src8++ ^ 0x20; - } - else { - *dst8++ = *src8++; - } - } - - *encodedCount = dst8 - (u8 *)dst; - return src8 - (u8 *)src; -} - -size_t GDB_UnescapeBinaryData(void *dst, const void *src, size_t len) -{ - u8 *dst8 = (u8 *)dst; - const u8 *src8 = (const u8 *)src; - - while ((uintptr_t)src8 < (uintptr_t)src + len) { - if (*src8 == '}') { - src8++; - *dst8++ = *src8++ ^ 0x20; - } else { - *dst8++ = *src8++; - } - } - - return dst8 - (u8 *)dst; -} - -const char *GDB_ParseIntegerList(unsigned long *dst, const char *src, size_t nb, char sep, char lastSep, u32 base, bool allowPrefix) -{ - const char *pos = src; - const char *endpos; - bool ok; - - for (size_t i = 0; i < nb; i++) { - unsigned long n = xstrtoul(pos, (char **)&endpos, (int) base, allowPrefix, &ok); - if(!ok || endpos == pos) { - return NULL; - } - - if (i != nb - 1) { - if (*endpos != sep) { - return NULL; - } - pos = endpos + 1; - } else { - if (*endpos != lastSep && *endpos != 0) { - return NULL; - } - pos = endpos; - } - - dst[i] = n; - } - - return pos; -} - -const char *GDB_ParseHexIntegerList(unsigned long *dst, const char *src, size_t nb, char lastSep) -{ - return GDB_ParseIntegerList(dst, src, nb, ',', lastSep, 16, false); -} - -static int GDB_SendNackIfPossible(GDBContext *ctx) { - if (ctx->flags & GDB_FLAG_NOACK) { - return -1; - } else { - char hdr = '-'; - transportInterfaceWriteData(ctx->transportInterface, &hdr, 1); - return 1; - } -} - -int GDB_ReceivePacket(GDBContext *ctx) -{ - char hdr; - bool ctrlC = false; - TransportInterface *iface = ctx->transportInterface; - - // Read the first character... - transportInterfaceReadData(iface, &hdr, 1); - - // Check if the ack/nack packets are not allowed - if ((hdr == '+' || hdr == '-') && (ctx->flags & GDB_FLAG_NOACK) != 0) { - DEBUG("Received a packed with an invalid header from GDB, hdr=%c\n", hdr); - return -1; - } - - switch (hdr) { - case '+': { - // Ack, don't do anything else except maybe NoAckMode state transition - if (ctx->noAckSent) { - ctx->flags |= GDB_FLAG_NOACK; - ctx->noAckSent = false; - } - return 0; - } - case '-': - // Nack, return the previous packet - transportInterfaceWriteData(iface, ctx->buffer, ctx->lastSentPacketSize); - return ctx->lastSentPacketSize; - case '$': - // Normal packet, handled below - break; - case '\x03': - // Normal packet (Control-C), handled below - ctrlC = true; - break; - default: - // Oops, send a nack - DEBUG("Received a packed with an invalid header from GDB, hdr=%c\n", hdr); - return GDB_SendNackIfPossible(ctx); - } - - // We didn't get a nack past this point, read the remaining data if any - - ctx->buffer[0] = hdr; - if (ctrlC) { - // Will never normally happen, but ok - if (ctx->state < GDB_STATE_ATTACHED) { - DEBUG("Received connection from GDB, now attaching...\n"); - GDB_AttachToContext(ctx); - ctx->state = GDB_STATE_ATTACHED; - } - return 1; - } - - size_t delimPos = transportInterfaceReadDataUntil(iface, ctx->buffer + 1, 4 + GDB_BUF_LEN - 1, '#'); - if (ctx->buffer[delimPos] != '#') { - // The packet is malformed, send a nack - return GDB_SendNackIfPossible(ctx); - } - - // Read the checksum - size_t checksumPos = delimPos + 1; - u8 checksum; - transportInterfaceReadData(iface, ctx->buffer + checksumPos, 2); - - if (GDB_DecodeHex(&checksum, ctx->buffer + checksumPos, 1) != 1) { - // Malformed checksum - return GDB_SendNackIfPossible(ctx); - } else if (GDB_ComputeChecksum(ctx->buffer + 1, delimPos - 1) != checksum) { - // Invalid checksum - return GDB_SendNackIfPossible(ctx); - } - - // Ok, send ack (if possible) - if (!(ctx->flags & GDB_FLAG_NOACK)) { - hdr = '+'; - transportInterfaceWriteData(iface, &hdr, 1); - } - - // State transitions... - if (ctx->state < GDB_STATE_ATTACHED) { - DEBUG("Received connection from GDB, now attaching...\n"); - GDB_AttachToContext(ctx); - ctx->state = GDB_STATE_ATTACHED; - } - - // Debug - ctx->buffer[checksumPos + 2] = '\0'; - DEBUGRAW("->"); - DEBUGRAW(ctx->buffer); - DEBUGRAW("\n"); - - // Set helper attributes, change '#' to NUL - ctx->commandData = ctx->buffer + 2; - ctx->commandEnd = ctx->buffer + delimPos; - ctx->buffer[delimPos] = '\0'; - - return (int)(delimPos + 2); -} - -static int GDB_DoSendPacket(GDBContext *ctx, size_t len) -{ - transportInterfaceWriteData(ctx->transportInterface, ctx->buffer, len); - ctx->lastSentPacketSize = len; - - // Debugging: - ctx->buffer[len] = 0; - DEBUGRAW("<-"); - DEBUGRAW(ctx->buffer); - DEBUGRAW("\n"); - - return (int)len; -} - -int GDB_SendPacket(GDBContext *ctx, const char *packetData, size_t len) -{ - if (packetData != ctx->buffer + 1) { - memmove(ctx->buffer + 1, packetData, len); - } - - ctx->buffer[0] = '$'; - - char *checksumLoc = ctx->buffer + len + 1; - *checksumLoc++ = '#'; - - hexItoa(GDB_ComputeChecksum(ctx->buffer + 1, len), checksumLoc, 2, false); - return GDB_DoSendPacket(ctx, 4 + len); -} - -int GDB_SendFormattedPacket(GDBContext *ctx, const char *packetDataFmt, ...) -{ - va_list args; - - va_start(args, packetDataFmt); - int n = vsprintf(ctx->buffer + 1, packetDataFmt, args); - va_end(args); - - if (n < 0) { - return -1; - } - - ctx->buffer[0] = '$'; - char *checksumLoc = ctx->buffer + n + 1; - *checksumLoc++ = '#'; - - hexItoa(GDB_ComputeChecksum(ctx->buffer + 1, n), checksumLoc, 2, false); - return GDB_DoSendPacket(ctx, 4 + n); -} - -int GDB_SendHexPacket(GDBContext *ctx, const void *packetData, size_t len) -{ - if(4 + 2 * len > GDB_BUF_LEN) - return -1; - - ctx->buffer[0] = '$'; - GDB_EncodeHex(ctx->buffer + 1, packetData, len); - - char *checksumLoc = ctx->buffer + 2 * len + 1; - *checksumLoc++ = '#'; - - hexItoa(GDB_ComputeChecksum(ctx->buffer + 1, 2 * len), checksumLoc, 2, false); - return GDB_DoSendPacket(ctx, 4 + 2 * len); -} - -int GDB_SendNotificationPacket(GDBContext *ctx, const char *packetData, size_t len) -{ - if (packetData != ctx->buffer + 1) { - memmove(ctx->buffer + 1, packetData, len); - } - - ctx->buffer[0] = '%'; - - char *checksumLoc = ctx->buffer + len + 1; - *checksumLoc++ = '#'; - - hexItoa(GDB_ComputeChecksum(ctx->buffer + 1, len), checksumLoc, 2, false); - return GDB_DoSendPacket(ctx, 4 + len); -} - -int GDB_SendStreamData(GDBContext *ctx, const char *streamData, size_t offset, size_t length, size_t totalSize, bool forceEmptyLast) -{ - // GDB_BUF_LEN does not include the usual %#<1-byte checksum> - if(length > GDB_BUF_LEN - 1) { - length = GDB_BUF_LEN - 1; - } - - char letter; - - if ((forceEmptyLast && offset >= totalSize) || (!forceEmptyLast && offset + length >= totalSize)) { - length = offset >= totalSize ? 0 : totalSize - offset; - letter = 'l'; - } else { - letter = 'm'; - } - - // Note: ctx->buffer[0] = '$' - if (streamData + offset != ctx->buffer + 2) { - memmove(ctx->buffer + 2, streamData + offset, length); - } - ctx->buffer[1] = letter; - return GDB_SendPacket(ctx, ctx->buffer + 1, 1 + length); -} - -int GDB_ReplyEmpty(GDBContext *ctx) -{ - return GDB_SendPacket(ctx, "", 0); -} - -int GDB_ReplyOk(GDBContext *ctx) -{ - return GDB_SendPacket(ctx, "OK", 2); -} - -int GDB_ReplyErrno(GDBContext *ctx, int no) -{ - char buf[] = "E01"; - hexItoa(no & 0xFF, buf + 1, 2, false); - return GDB_SendPacket(ctx, buf, 3); -} diff --git a/thermosphere/src/gdb/net.h b/thermosphere/src/gdb/net.h deleted file mode 100644 index 405102867..000000000 --- a/thermosphere/src/gdb/net.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -* This file is part of Luma3DS. -* Copyright (C) 2016-2019 Aurora Wright, TuxSH -* -* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) -*/ - -#pragma once - -#include "context.h" - -u8 GDB_ComputeChecksum(const char *packetData, size_t len); -size_t GDB_EncodeHex(char *dst, const void *src, size_t len); -size_t GDB_DecodeHex(void *dst, const char *src, size_t len); -size_t GDB_EscapeBinaryData(size_t *encodedCount, void *dst, const void *src, size_t len, size_t maxLen); -size_t GDB_UnescapeBinaryData(void *dst, const void *src, size_t len); - -const char *GDB_ParseIntegerList(unsigned long *dst, const char *src, size_t nb, char sep, char lastSep, u32 base, bool allowPrefix); -const char *GDB_ParseHexIntegerList(unsigned long *dst, const char *src, size_t nb, char lastSep); - -int GDB_ReceivePacket(GDBContext *ctx); -int GDB_SendPacket(GDBContext *ctx, const char *packetData, size_t len); -int GDB_SendFormattedPacket(GDBContext *ctx, const char *packetDataFmt, ...); -int GDB_SendHexPacket(GDBContext *ctx, const void *packetData, size_t len); -int GDB_SendNotificationPacket(GDBContext *ctx, const char *packetData, size_t len); -int GDB_SendStreamData(GDBContext *ctx, const char *streamData, size_t offset, size_t length, size_t totalSize, bool forceEmptyLast); -int GDB_ReplyEmpty(GDBContext *ctx); -int GDB_ReplyOk(GDBContext *ctx); -int GDB_ReplyErrno(GDBContext *ctx, int no);