summaryrefslogtreecommitdiff
path: root/src/core/debugger/gdbstub.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/debugger/gdbstub.cpp')
-rw-r--r--src/core/debugger/gdbstub.cpp171
1 files changed, 158 insertions, 13 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";
34constexpr char GDB_STUB_REPLY_OK[] = "OK"; 34constexpr char GDB_STUB_REPLY_OK[] = "OK";
35constexpr char GDB_STUB_REPLY_EMPTY[] = ""; 35constexpr char GDB_STUB_REPLY_EMPTY[] = "";
36 36
37static u8 CalculateChecksum(std::string_view data) {
38 return std::accumulate(data.begin(), data.end(), u8{0},
39 [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); });
40}
41
42static std::string EscapeGDB(std::string_view data) {
43 std::string escaped;
44 escaped.reserve(data.size());
45
46 for (char c : data) {
47 switch (c) {
48 case '#':
49 escaped += "}\x03";
50 break;
51 case '$':
52 escaped += "}\x04";
53 break;
54 case '*':
55 escaped += "}\x0a";
56 break;
57 case '}':
58 escaped += "}\x5d";
59 break;
60 default:
61 escaped += c;
62 break;
63 }
64 }
65
66 return escaped;
67}
68
69static std::string EscapeXML(std::string_view data) {
70 std::string escaped;
71 escaped.reserve(data.size());
72
73 for (char c : data) {
74 switch (c) {
75 case '&':
76 escaped += "&amp;";
77 break;
78 case '"':
79 escaped += "&quot;";
80 break;
81 case '<':
82 escaped += "&lt;";
83 break;
84 case '>':
85 escaped += "&gt;";
86 break;
87 default:
88 escaped += c;
89 break;
90 }
91 }
92
93 return escaped;
94}
95
37GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_) 96GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
38 : DebuggerFrontend(backend_), system{system_} { 97 : DebuggerFrontend(backend_), system{system_} {
39 if (system.CurrentProcess()->Is64BitProcess()) { 98 if (system.CurrentProcess()->Is64BitProcess()) {
@@ -255,6 +314,80 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
255 } 314 }
256} 315}
257 316
317// Structure offsets are from Atmosphere
318// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp
319
320static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory,
321 const Kernel::KThread* thread) {
322 // Read thread type from TLS
323 const VAddr tls_thread_type{memory.Read32(thread->GetTLSAddress() + 0x1fc)};
324 const VAddr argument_thread_type{thread->GetArgument()};
325
326 if (argument_thread_type && tls_thread_type != argument_thread_type) {
327 // Probably not created by nnsdk, no name available.
328 return std::nullopt;
329 }
330
331 if (!tls_thread_type) {
332 return std::nullopt;
333 }
334
335 const u16 version{memory.Read16(tls_thread_type + 0x26)};
336 VAddr name_pointer{};
337 if (version == 1) {
338 name_pointer = memory.Read32(tls_thread_type + 0xe4);
339 } else {
340 name_pointer = memory.Read32(tls_thread_type + 0xe8);
341 }
342
343 if (!name_pointer) {
344 // No name provided.
345 return std::nullopt;
346 }
347
348 return memory.ReadCString(name_pointer, 256);
349}
350
351static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory,
352 const Kernel::KThread* thread) {
353 // Read thread type from TLS
354 const VAddr tls_thread_type{memory.Read64(thread->GetTLSAddress() + 0x1f8)};
355 const VAddr argument_thread_type{thread->GetArgument()};
356
357 if (argument_thread_type && tls_thread_type != argument_thread_type) {
358 // Probably not created by nnsdk, no name available.
359 return std::nullopt;
360 }
361
362 if (!tls_thread_type) {
363 return std::nullopt;
364 }
365
366 const u16 version{memory.Read16(tls_thread_type + 0x46)};
367 VAddr name_pointer{};
368 if (version == 1) {
369 name_pointer = memory.Read64(tls_thread_type + 0x1a0);
370 } else {
371 name_pointer = memory.Read64(tls_thread_type + 0x1a8);
372 }
373
374 if (!name_pointer) {
375 // No name provided.
376 return std::nullopt;
377 }
378
379 return memory.ReadCString(name_pointer, 256);
380}
381
382static std::optional<std::string> GetThreadName(Core::System& system,
383 const Kernel::KThread* thread) {
384 if (system.CurrentProcess()->Is64BitProcess()) {
385 return GetNameFromThreadType64(system.Memory(), thread);
386 } else {
387 return GetNameFromThreadType32(system.Memory(), thread);
388 }
389}
390
258static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) { 391static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
259 switch (thread->GetWaitReasonForDebugging()) { 392 switch (thread->GetWaitReasonForDebugging()) {
260 case Kernel::ThreadWaitReasonForDebugging::Sleep: 393 case Kernel::ThreadWaitReasonForDebugging::Sleep:
@@ -332,20 +465,36 @@ void GDBStub::HandleQuery(std::string_view command) {
332 } else if (command.starts_with("sThreadInfo")) { 465 } else if (command.starts_with("sThreadInfo")) {
333 // end of list 466 // end of list
334 SendReply("l"); 467 SendReply("l");
335 } else if (command.starts_with("Xfer:threads:read")) { 468 } else if (command.starts_with("Xfer:threads:read::")) {
336 std::string buffer; 469 std::string buffer;
337 buffer += R"(l<?xml version="1.0"?>)"; 470 buffer += R"(<?xml version="1.0"?>)";
338 buffer += "<threads>"; 471 buffer += "<threads>";
339 472
340 const auto& threads = system.GlobalSchedulerContext().GetThreadList(); 473 const auto& threads = system.GlobalSchedulerContext().GetThreadList();
341 for (const auto& thread : threads) { 474 for (const auto* thread : threads) {
342 buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="Thread {:d}">{}</thread>)", 475 auto thread_name{GetThreadName(system, thread)};
476 if (!thread_name) {
477 thread_name = fmt::format("Thread {:d}", thread->GetThreadID());
478 }
479
480 buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)",
343 thread->GetThreadID(), thread->GetActiveCore(), 481 thread->GetThreadID(), thread->GetActiveCore(),
344 thread->GetThreadID(), GetThreadState(thread)); 482 EscapeXML(*thread_name), GetThreadState(thread));
345 } 483 }
346 484
347 buffer += "</threads>"; 485 buffer += "</threads>";
348 SendReply(buffer); 486
487 const auto offset{command.substr(19)};
488 const auto amount{command.substr(command.find(',') + 1)};
489
490 const auto offset_val{static_cast<u64>(strtoll(offset.data(), nullptr, 16))};
491 const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
492
493 if (offset_val + amount_val > buffer.size()) {
494 SendReply("l" + buffer.substr(offset_val));
495 } else {
496 SendReply("m" + buffer.substr(offset_val, amount_val));
497 }
349 } else if (command.starts_with("Attached")) { 498 } else if (command.starts_with("Attached")) {
350 SendReply("0"); 499 SendReply("0");
351 } else if (command.starts_with("StartNoAckMode")) { 500 } else if (command.starts_with("StartNoAckMode")) {
@@ -438,14 +587,10 @@ std::optional<std::string> GDBStub::DetachCommand() {
438 return data.substr(1, data.size() - 4); 587 return data.substr(1, data.size() - 4);
439} 588}
440 589
441u8 GDBStub::CalculateChecksum(std::string_view data) {
442 return std::accumulate(data.begin(), data.end(), u8{0},
443 [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); });
444}
445
446void GDBStub::SendReply(std::string_view data) { 590void GDBStub::SendReply(std::string_view data) {
447 const auto output{ 591 const auto escaped{EscapeGDB(data)};
448 fmt::format("{}{}{}{:02x}", GDB_STUB_START, data, GDB_STUB_END, CalculateChecksum(data))}; 592 const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END,
593 CalculateChecksum(escaped))};
449 LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output); 594 LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output);
450 595
451 // C++ string support is complete rubbish 596 // C++ string support is complete rubbish