diff --git a/thermosphere/src/gdb/context.c b/thermosphere/src/gdb/context.cpp similarity index 97% rename from thermosphere/src/gdb/context.c rename to thermosphere/src/gdb/context.cpp index e09b3b515..6a55b37b6 100644 --- a/thermosphere/src/gdb/context.c +++ b/thermosphere/src/gdb/context.cpp @@ -27,16 +27,7 @@ //#include "context.h" #include "net.h" - #include "debug.h" -#include "query.h" -#include "verbose.h" -#include "thread.h" -#include "debug.h" -#include "regs.h" -#include "mem.h" -#include "hio.h" -#include "stop_points.h" #include "../breakpoints.h" #include "../software_breakpoints.h" diff --git a/thermosphere/src/gdb/hio.h b/thermosphere/src/gdb/hio.h index 0915dc3a9..9eb312176 100644 --- a/thermosphere/src/gdb/hio.h +++ b/thermosphere/src/gdb/hio.h @@ -12,5 +12,3 @@ bool GDB_FetchPackedHioRequest(GDBContext *ctx, u32 addr); bool GDB_IsHioInProgress(GDBContext *ctx); int GDB_SendCurrentHioRequest(GDBContext *ctx); - -GDB_DECLARE_HANDLER(HioReply); diff --git a/thermosphere/src/gdb/gdb_context.hpp b/thermosphere/src/gdb/hyp_gdb_context.hpp similarity index 54% rename from thermosphere/src/gdb/gdb_context.hpp rename to thermosphere/src/gdb/hyp_gdb_context.hpp index e8b18421a..fcc2194f9 100644 --- a/thermosphere/src/gdb/gdb_context.hpp +++ b/thermosphere/src/gdb/hyp_gdb_context.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Atmosphère-NX + * 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, @@ -24,9 +24,17 @@ #pragma once -#include "gdb_defines_internal.hpp" +#include "../defines.hpp" #include "../transport_interface.h" +#define _REENT_ONLY +#include + +#define DECLARE_HANDLER(name) int Handle##name() +#define DECLARE_QUERY_HANDLER(name) DECLARE_HANDLER(Query##name) +#define DECLARE_VERBOSE_HANDLER(name) DECLARE_HANDLER(Verbose##name) +#define DECLARE_XFER_HANDLER(name) DECLARE_HANDLER(Xfer##name) + namespace ams::hyp::gdb { struct PackedGdbHioRequest { @@ -47,14 +55,6 @@ namespace ams::hyp::gdb { bool ctrlC; }; - typedef enum GDBState - { - STATE_DISCONNECTED, - STATE_CONNECTED, - STATE_ATTACHED, - STATE_DETACHING, - } GDBState; - struct DebugEventInfo; class Context final { @@ -106,7 +106,49 @@ namespace ams::hyp::gdb { private: void MigrateRxIrq(u32 coreId) const; + // Comms + int ReceivePacket(); + int DoSendPacket(); + int SendPacket(const char *packetData, size_t len); + int SendFormattedPacket(const char *packetDataFmt, ...); + int SendHexPacket(const void *packetData, size_t len); + int SendNotificationPacket(const char *packetData, size_t len); + int SendStreamData(const char *streamData, size_t offset, size_t length, size_t totalSize, bool forceEmptyLast); + int ReplyEmpty(); + int ReplyErrno(int no); + + char *GetInPlaceOutputBuffer() const { + return m_buffer + 1; + } + private: + // Meta DECLARE_HANDLER(Unsupported); + DECLARE_HANDLER(ReadQuery); + DECLARE_HANDLER(WriteQuery); + DECLARE_QUERY_HANDLER(Xfer); + DECLARE_HANDLER(VerboseCommand); + + // General queries + DECLARE_QUERY_HANDLER(Supported); + DECLARE_QUERY_HANDLER(StartNoAckMode); + DECLARE_QUERY_HANDLER(Attached); + + // XML Transfer + DECLARE_XFER_HANDLER(Features); + + // Resuming features enumeration + DECLARE_VERBOSE_HANDLER(ContinueSupported); + + // "Threads" + // Capitalization in "GetTLSAddr" is intended. + DECLARE_HANDLER(SetThreadId); + DECLARE_HANDLER(IsThreadAlive); + DECLARE_QUERY_HANDLER(CurrentThreadId); + DECLARE_QUERY_HANDLER(fThreadInfo); + DECLARE_QUERY_HANDLER(sThreadInfo); + DECLARE_QUERY_HANDLER(ThreadEvents); + DECLARE_QUERY_HANDLER(ThreadExtraInfo); + DECLARE_QUERY_HANDLER(GetTLSAddr); // Debug DECLARE_VERBOSE_HANDLER(Stopped); @@ -117,6 +159,27 @@ namespace ams::hyp::gdb { DECLARE_VERBOSE_HANDLER(Continue); DECLARE_HANDLER(GetStopReason); + // Stop points + DECLARE_HANDLER(ToggleStopPoint); + + // Memory + DECLARE_HANDLER(ReadMemory); + DECLARE_HANDLER(WriteMemory); + DECLARE_HANDLER(WriteMemoryRaw); + DECLARE_QUERY_HANDLER(SearchMemory); + + // Registers + DECLARE_HANDLER(ReadRegisters); + DECLARE_HANDLER(WriteRegisters); + DECLARE_HANDLER(ReadRegister); + DECLARE_HANDLER(WriteRegister); + + // Hio + DECLARE_HANDLER(HioReply); + + // Custom commands + DECLARE_QUERY_HANDLER(Rcmd); + public: void Initialize(TransportInterfaceType ifaceType, u32 ifaceId, u32 ifaceFlags); void Attach(); @@ -125,9 +188,13 @@ namespace ams::hyp::gdb { void Acquire(); void Release(); - constexpr bool IsAttached() const - { + constexpr bool IsAttached() const { return m_state == State::Attached; } - } -} \ No newline at end of file + }; +} + +#undef DECLARE_HANDLER +#undef DECLARE_QUERY_HANDLER +#undef DECLARE_VERBOSE_HANDLER +#undef DECLARE_XFER_HANDLER diff --git a/thermosphere/src/gdb/gdb_defines_internal.hpp b/thermosphere/src/gdb/hyp_gdb_defines_internal.hpp similarity index 66% rename from thermosphere/src/gdb/gdb_defines_internal.hpp rename to thermosphere/src/gdb/hyp_gdb_defines_internal.hpp index 42e90c9cd..12595498e 100644 --- a/thermosphere/src/gdb/gdb_defines_internal.hpp +++ b/thermosphere/src/gdb/hyp_gdb_defines_internal.hpp @@ -24,7 +24,7 @@ #pragma once -#include "../defines.hpp" +#include "hyp_gdb_context.hpp" // 512+24 is the ideal size as IDA will try to read exactly 0x100 bytes at a time. // IDA seems to want additional bytes as well. @@ -33,14 +33,12 @@ #define GDB_BUF_LEN 0x800 #define GDB_WORK_BUF_LEN 0x1000 -#define HANDLER(name) Handle##name -#define QUERY_HANDLER(name) HANDLER(Query##name) -#define VERBOSE_HANDLER(name) HANDLER(Verbose##name) +#define GDB_HANDLER(name) Handle##name +#define GDB_QUERY_HANDLER(name) GDB_HANDLER(Query##name) +#define GDB_VERBOSE_HANDLER(name) GDB_HANDLER(Verbose##name) +#define GDB_XFER_HANDLER(name) GDB_HANDLER(Xfer##name) -#define DECLARE_HANDLER(name) int HANDLER(name)() -#define DECLARE_QUERY_HANDLER(name) DECLARE_HANDLER(Query##name) -#define DECLARE_VERBOSE_HANDLER(name) DECLARE_HANDLER(Verbose##name) - -#define DEFINE_HANDLER(name) int Context::HANDLER(name)() -#define DEFINE_QUERY_HANDLER(name) DEFINE_HANDLER(Query##name) -#define DECLARE_VERBOSE_HANDLER(name) DEFINE_HANDLER(Verbose##name) \ No newline at end of file +#define GDB_DEFINE_HANDLER(name) int Context::HANDLER(name)() +#define GDB_DEFINE_QUERY_HANDLER(name) DEFINE_HANDLER(Query##name) +#define GDB_DECLARE_VERBOSE_HANDLER(name) DEFINE_HANDLER(Verbose##name) +#define GDB_DECLARE_Xfer_HANDLER(name) DEFINE_HANDLER(Xfer##name) diff --git a/thermosphere/src/gdb/hyp_gdb_packet_data.cpp b/thermosphere/src/gdb/hyp_gdb_packet_data.cpp new file mode 100644 index 000000000..555cd2fba --- /dev/null +++ b/thermosphere/src/gdb/hyp_gdb_packet_data.cpp @@ -0,0 +1,101 @@ +/* + * 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 . + */ + +#pragma once + +#include "hyp_gdb_packet_data.hpp" +#include + +namespace ams::hyp::gdb { + u8 ComputeChecksum(std::string_view packetData) + { + return std::accumulate(packetData.cbegin(), packetData.cend(), u8{0u}); + } + + size_t EncodeHex(char *dst, const void *src, size_t len) + { + static const char *alphabet = "0123456789abcdef"; + const u8 *src8 = reinterpret_cast(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; + } + + size_t DecodeHex(void *dst, std::string_view data) + { + 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) { + return i; + } + + dst8[i] = (v1 << 4) | v2; + } + + return i; + } + + size_t EscapeBinaryData(size_t *encodedCount, void *dst, const void *src, size_t len, size_t maxLen) + { + u8 *dst8 = reinterpret_cast(dst); + const u8 *src8 = reinterpret_cast(src); + len = std::min(len, maxLen); + + u8 *dstMax = dst8 + len; + + while (dst8 < dstMax) { + if (*src8 == '$' || *src8 == '#' || *src8 == '}' || *src8 == '*') { + if (dst8 + 1 >= dstMax) { + break; + } + *dst8++ = '}'; + *dst8++ = *src8++ ^ 0x20; + } + else { + *dst8++ = *src8++; + } + } + + *encodedCount = dst8 - reinterpret_cast(dst); + return src8 - reinterpret_cast(src); + } + + size_t UnescapeBinaryData(void *dst, const void *src, size_t len) + { + u8 *dst8 = reinterpret_cast(dst); + const u8 *src8 = reinterpret_cast(src); + const u8 *srcEnd = src8 + len; + + while (src8 < srcEnd) { + if (*src8 == '}') { + src8++; + *dst8++ = *src8++ ^ 0x20; + } else { + *dst8++ = *src8++; + } + } + + return dst8 - reinterpret_cast(dst); + } +} diff --git a/thermosphere/src/gdb/hyp_gdb_packet_data.hpp b/thermosphere/src/gdb/hyp_gdb_packet_data.hpp new file mode 100644 index 000000000..e4eeb6ff6 --- /dev/null +++ b/thermosphere/src/gdb/hyp_gdb_packet_data.hpp @@ -0,0 +1,109 @@ +/* + * 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 . + */ + +#pragma once + +#include "../defines.hpp" +#include + +namespace ams::hyp::gdb { + + constexpr unsigned long DecodeHexDigit(char src) + { + switch (src) { + case '0' ... '9': return 0 + (src - '0'); + case 'a' ... 'f': return 10 + (src - 'a'); + case 'A' ... 'F': return 10 + (src - 'A'); + default: + return 16; + } + } + + constexpr auto ParseInteger(std::string_view str, u32 base = 0, bool allowPrefix = true) + { + unsigned long res = 0; + long mult = 1; + auto errval = std::tuple{false, 0ul, str.end()}; + + if ((base == 0 && !allowPrefix) || base > 16 || str.empty()) { + return errval; + } + + // Check for +, - + if (str[0] == '+') { + if (!allowPrefix) { + return errval; + } + str.remove_prefix(1); + } else if (str[0] == '-') { + if (!allowPrefix) { + return errval; + } + str.remove_prefix(1); + mult = -1; + } + + if (str.empty()) { + // Oops + return errval; + } + + // Now, check for 0x or leading 0 + if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') { + if (!allowPrefix || (base != 16 && base != 0)) { + return errval; + } else { + str.remove_prefix(2); + base = 16; + } + } else if (base == 0 || str[0] == '0') { + if (!allowPrefix) { + return errval; + } + base = 8; + } else if (base == 0) { + base = 10; + } + + if (str.empty()) { + // Oops + return errval; + } + + auto it = str.begin(); + for (; it != str.end(); ++it) { + unsigned long v = DecodeHexDigit(*it); + if (v >= base) { + break; + } + + res *= base; + res += v; + } + + return std::tuple{true, res * mult, it}; + } + + std::string_view::iterator ParseIntegerList(unsigned long *dst, std::string_view str, size_t nb, char sep, char lastSep, u32 base, bool allowPrefix); + std::string_view::iterator ParseHexIntegerList(unsigned long *dst, std::string_view str, size_t nb, char lastSep); + + 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/net.h b/thermosphere/src/gdb/net.h index c36a694ed..405102867 100644 --- a/thermosphere/src/gdb/net.h +++ b/thermosphere/src/gdb/net.h @@ -8,8 +8,6 @@ #pragma once #include "context.h" -#define _REENT_ONLY -#include u8 GDB_ComputeChecksum(const char *packetData, size_t len); size_t GDB_EncodeHex(char *dst, const void *src, size_t len); diff --git a/thermosphere/src/gdb/query.h b/thermosphere/src/gdb/query.h index adfcd681e..21649a2e3 100644 --- a/thermosphere/src/gdb/query.h +++ b/thermosphere/src/gdb/query.h @@ -9,9 +9,4 @@ #include "context.h" -int GDB_HandleReadQuery(GDBContext *ctx); -int GDB_HandleWriteQuery(GDBContext *ctx); -GDB_DECLARE_QUERY_HANDLER(Supported); -GDB_DECLARE_QUERY_HANDLER(StartNoAckMode); -GDB_DECLARE_QUERY_HANDLER(Attached); diff --git a/thermosphere/src/gdb/xfer.h b/thermosphere/src/gdb/xfer.h index b279721d6..901f9b35c 100644 --- a/thermosphere/src/gdb/xfer.h +++ b/thermosphere/src/gdb/xfer.h @@ -9,9 +9,3 @@ #include "context.h" -#define GDB_XFER_HANDLER(name) GDB_HANDLER(Xfer##name) -#define GDB_DECLARE_XFER_HANDLER(name) int GDB_XFER_HANDLER(name)(GDBContext *ctx, bool write, const char *annex, size_t offset, size_t length) - -GDB_DECLARE_XFER_HANDLER(Features); - -GDB_DECLARE_QUERY_HANDLER(Xfer);