diff options
| -rw-r--r-- | src/core/debugger/gdbstub.cpp | 171 | ||||
| -rw-r--r-- | src/core/debugger/gdbstub.h | 1 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread.h | 10 | 
4 files changed, 172 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); diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index ab9ce6a86..940334f59 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -198,6 +198,10 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s      resource_limit_release_hint = false;      cpu_time = 0; +    // Set debug context. +    stack_top = user_stack_top; +    argument = arg; +      // Clear our stack parameters.      std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0,                  sizeof(StackParameters)); diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 60ae0da78..f4d83f99a 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -660,6 +660,14 @@ public:      void IfDummyThreadTryWait();      void IfDummyThreadEndWait(); +    [[nodiscard]] uintptr_t GetArgument() const { +        return argument; +    } + +    [[nodiscard]] VAddr GetUserStackTop() const { +        return stack_top; +    } +  private:      static constexpr size_t PriorityInheritanceCountMax = 10;      union SyncObjectBuffer { @@ -791,6 +799,8 @@ private:      std::vector<KSynchronizationObject*> wait_objects_for_debugging;      VAddr mutex_wait_address_for_debugging{};      ThreadWaitReasonForDebugging wait_reason_for_debugging{}; +    uintptr_t argument; +    VAddr stack_top;  public:      using ConditionVariableThreadTreeType = ConditionVariableThreadTree; | 
