diff options
Diffstat (limited to '')
-rw-r--r-- | src/core/debugger/gdbstub.cpp | 171 | ||||
-rw-r--r-- | src/core/debugger/gdbstub.h | 1 |
2 files changed, 158 insertions, 14 deletions
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 0c36069a6..682651a86 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -34,6 +34,65 @@ constexpr char GDB_STUB_REPLY_ERR[] = "E01"; constexpr char GDB_STUB_REPLY_OK[] = "OK"; constexpr char GDB_STUB_REPLY_EMPTY[] = ""; +static u8 CalculateChecksum(std::string_view data) { + return std::accumulate(data.begin(), data.end(), u8{0}, + [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); }); +} + +static std::string EscapeGDB(std::string_view data) { + std::string escaped; + escaped.reserve(data.size()); + + for (char c : data) { + switch (c) { + case '#': + escaped += "}\x03"; + break; + case '$': + escaped += "}\x04"; + break; + case '*': + escaped += "}\x0a"; + break; + case '}': + escaped += "}\x5d"; + break; + default: + escaped += c; + break; + } + } + + return escaped; +} + +static std::string EscapeXML(std::string_view data) { + std::string escaped; + escaped.reserve(data.size()); + + for (char c : data) { + switch (c) { + case '&': + escaped += "&"; + break; + case '"': + escaped += """; + break; + case '<': + escaped += "<"; + break; + case '>': + escaped += ">"; + break; + default: + escaped += c; + break; + } + } + + return escaped; +} + GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_) : DebuggerFrontend(backend_), system{system_} { if (system.CurrentProcess()->Is64BitProcess()) { @@ -255,6 +314,80 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction } } +// Structure offsets are from Atmosphere +// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp + +static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory, + const Kernel::KThread* thread) { + // Read thread type from TLS + const VAddr tls_thread_type{memory.Read32(thread->GetTLSAddress() + 0x1fc)}; + const VAddr argument_thread_type{thread->GetArgument()}; + + if (argument_thread_type && tls_thread_type != argument_thread_type) { + // Probably not created by nnsdk, no name available. + return std::nullopt; + } + + if (!tls_thread_type) { + return std::nullopt; + } + + const u16 version{memory.Read16(tls_thread_type + 0x26)}; + VAddr name_pointer{}; + if (version == 1) { + name_pointer = memory.Read32(tls_thread_type + 0xe4); + } else { + name_pointer = memory.Read32(tls_thread_type + 0xe8); + } + + if (!name_pointer) { + // No name provided. + return std::nullopt; + } + + return memory.ReadCString(name_pointer, 256); +} + +static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory, + const Kernel::KThread* thread) { + // Read thread type from TLS + const VAddr tls_thread_type{memory.Read64(thread->GetTLSAddress() + 0x1f8)}; + const VAddr argument_thread_type{thread->GetArgument()}; + + if (argument_thread_type && tls_thread_type != argument_thread_type) { + // Probably not created by nnsdk, no name available. + return std::nullopt; + } + + if (!tls_thread_type) { + return std::nullopt; + } + + const u16 version{memory.Read16(tls_thread_type + 0x46)}; + VAddr name_pointer{}; + if (version == 1) { + name_pointer = memory.Read64(tls_thread_type + 0x1a0); + } else { + name_pointer = memory.Read64(tls_thread_type + 0x1a8); + } + + if (!name_pointer) { + // No name provided. + return std::nullopt; + } + + return memory.ReadCString(name_pointer, 256); +} + +static std::optional<std::string> GetThreadName(Core::System& system, + const Kernel::KThread* thread) { + if (system.CurrentProcess()->Is64BitProcess()) { + return GetNameFromThreadType64(system.Memory(), thread); + } else { + return GetNameFromThreadType32(system.Memory(), thread); + } +} + static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) { switch (thread->GetWaitReasonForDebugging()) { case Kernel::ThreadWaitReasonForDebugging::Sleep: @@ -332,20 +465,36 @@ void GDBStub::HandleQuery(std::string_view command) { } else if (command.starts_with("sThreadInfo")) { // end of list SendReply("l"); - } else if (command.starts_with("Xfer:threads:read")) { + } else if (command.starts_with("Xfer:threads:read::")) { std::string buffer; - buffer += R"(l<?xml version="1.0"?>)"; + buffer += R"(<?xml version="1.0"?>)"; buffer += "<threads>"; const auto& threads = system.GlobalSchedulerContext().GetThreadList(); - for (const auto& thread : threads) { - buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="Thread {:d}">{}</thread>)", + for (const auto* thread : threads) { + auto thread_name{GetThreadName(system, thread)}; + if (!thread_name) { + thread_name = fmt::format("Thread {:d}", thread->GetThreadID()); + } + + buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)", thread->GetThreadID(), thread->GetActiveCore(), - thread->GetThreadID(), GetThreadState(thread)); + EscapeXML(*thread_name), GetThreadState(thread)); } buffer += "</threads>"; - SendReply(buffer); + + const auto offset{command.substr(19)}; + const auto amount{command.substr(command.find(',') + 1)}; + + const auto offset_val{static_cast<u64>(strtoll(offset.data(), nullptr, 16))}; + const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))}; + + if (offset_val + amount_val > buffer.size()) { + SendReply("l" + buffer.substr(offset_val)); + } else { + SendReply("m" + buffer.substr(offset_val, amount_val)); + } } else if (command.starts_with("Attached")) { SendReply("0"); } else if (command.starts_with("StartNoAckMode")) { @@ -438,14 +587,10 @@ std::optional<std::string> GDBStub::DetachCommand() { return data.substr(1, data.size() - 4); } -u8 GDBStub::CalculateChecksum(std::string_view data) { - return std::accumulate(data.begin(), data.end(), u8{0}, - [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); }); -} - void GDBStub::SendReply(std::string_view data) { - const auto output{ - fmt::format("{}{}{}{:02x}", GDB_STUB_START, data, GDB_STUB_END, CalculateChecksum(data))}; + const auto escaped{EscapeGDB(data)}; + const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END, + CalculateChecksum(escaped))}; LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output); // C++ string support is complete rubbish diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h index aa1f7de6c..1bb638187 100644 --- a/src/core/debugger/gdbstub.h +++ b/src/core/debugger/gdbstub.h @@ -34,7 +34,6 @@ private: std::optional<std::string> DetachCommand(); Kernel::KThread* GetThreadByID(u64 thread_id); - static u8 CalculateChecksum(std::string_view data); void SendReply(std::string_view data); void SendStatus(char status); |