gdbstub.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718
  1. // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <atomic>
  4. #include <numeric>
  5. #include <optional>
  6. #include <thread>
  7. #include <boost/algorithm/string.hpp>
  8. #include "common/hex_util.h"
  9. #include "common/logging/log.h"
  10. #include "common/scope_exit.h"
  11. #include "core/arm/arm_interface.h"
  12. #include "core/core.h"
  13. #include "core/debugger/gdbstub.h"
  14. #include "core/debugger/gdbstub_arch.h"
  15. #include "core/hle/kernel/k_page_table.h"
  16. #include "core/hle/kernel/k_process.h"
  17. #include "core/hle/kernel/k_thread.h"
  18. #include "core/loader/loader.h"
  19. #include "core/memory.h"
  20. namespace Core {
  21. constexpr char GDB_STUB_START = '$';
  22. constexpr char GDB_STUB_END = '#';
  23. constexpr char GDB_STUB_ACK = '+';
  24. constexpr char GDB_STUB_NACK = '-';
  25. constexpr char GDB_STUB_INT3 = 0x03;
  26. constexpr int GDB_STUB_SIGTRAP = 5;
  27. constexpr char GDB_STUB_REPLY_ERR[] = "E01";
  28. constexpr char GDB_STUB_REPLY_OK[] = "OK";
  29. constexpr char GDB_STUB_REPLY_EMPTY[] = "";
  30. static u8 CalculateChecksum(std::string_view data) {
  31. return std::accumulate(data.begin(), data.end(), u8{0},
  32. [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); });
  33. }
  34. static std::string EscapeGDB(std::string_view data) {
  35. std::string escaped;
  36. escaped.reserve(data.size());
  37. for (char c : data) {
  38. switch (c) {
  39. case '#':
  40. escaped += "}\x03";
  41. break;
  42. case '$':
  43. escaped += "}\x04";
  44. break;
  45. case '*':
  46. escaped += "}\x0a";
  47. break;
  48. case '}':
  49. escaped += "}\x5d";
  50. break;
  51. default:
  52. escaped += c;
  53. break;
  54. }
  55. }
  56. return escaped;
  57. }
  58. static std::string EscapeXML(std::string_view data) {
  59. std::string escaped;
  60. escaped.reserve(data.size());
  61. for (char c : data) {
  62. switch (c) {
  63. case '&':
  64. escaped += "&amp;";
  65. break;
  66. case '"':
  67. escaped += "&quot;";
  68. break;
  69. case '<':
  70. escaped += "&lt;";
  71. break;
  72. case '>':
  73. escaped += "&gt;";
  74. break;
  75. default:
  76. escaped += c;
  77. break;
  78. }
  79. }
  80. return escaped;
  81. }
  82. GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
  83. : DebuggerFrontend(backend_), system{system_} {
  84. if (system.CurrentProcess()->Is64BitProcess()) {
  85. arch = std::make_unique<GDBStubA64>();
  86. } else {
  87. arch = std::make_unique<GDBStubA32>();
  88. }
  89. }
  90. GDBStub::~GDBStub() = default;
  91. void GDBStub::Connected() {}
  92. void GDBStub::ShuttingDown() {}
  93. void GDBStub::Stopped(Kernel::KThread* thread) {
  94. SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
  95. }
  96. void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) {
  97. const auto status{arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)};
  98. switch (watch.type) {
  99. case Kernel::DebugWatchpointType::Read:
  100. SendReply(fmt::format("{}rwatch:{:x};", status, watch.start_address));
  101. break;
  102. case Kernel::DebugWatchpointType::Write:
  103. SendReply(fmt::format("{}watch:{:x};", status, watch.start_address));
  104. break;
  105. case Kernel::DebugWatchpointType::ReadOrWrite:
  106. default:
  107. SendReply(fmt::format("{}awatch:{:x};", status, watch.start_address));
  108. break;
  109. }
  110. }
  111. std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
  112. std::vector<DebuggerAction> actions;
  113. current_command.insert(current_command.end(), data.begin(), data.end());
  114. while (current_command.size() != 0) {
  115. ProcessData(actions);
  116. }
  117. return actions;
  118. }
  119. void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) {
  120. const char c{current_command[0]};
  121. // Acknowledgement
  122. if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) {
  123. current_command.erase(current_command.begin());
  124. return;
  125. }
  126. // Interrupt
  127. if (c == GDB_STUB_INT3) {
  128. LOG_INFO(Debug_GDBStub, "Received interrupt");
  129. current_command.erase(current_command.begin());
  130. actions.push_back(DebuggerAction::Interrupt);
  131. SendStatus(GDB_STUB_ACK);
  132. return;
  133. }
  134. // Otherwise, require the data to be the start of a command
  135. if (c != GDB_STUB_START) {
  136. LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data());
  137. current_command.clear();
  138. SendStatus(GDB_STUB_NACK);
  139. return;
  140. }
  141. // Continue reading until command is complete
  142. while (CommandEnd() == current_command.end()) {
  143. const auto new_data{backend.ReadFromClient()};
  144. current_command.insert(current_command.end(), new_data.begin(), new_data.end());
  145. }
  146. // Execute and respond to GDB
  147. const auto command{DetachCommand()};
  148. if (command) {
  149. SendStatus(GDB_STUB_ACK);
  150. ExecuteCommand(*command, actions);
  151. } else {
  152. SendStatus(GDB_STUB_NACK);
  153. }
  154. }
  155. void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions) {
  156. LOG_TRACE(Debug_GDBStub, "Executing command: {}", packet);
  157. if (packet.length() == 0) {
  158. SendReply(GDB_STUB_REPLY_ERR);
  159. return;
  160. }
  161. if (packet.starts_with("vCont")) {
  162. HandleVCont(packet.substr(5), actions);
  163. return;
  164. }
  165. std::string_view command{packet.substr(1, packet.size())};
  166. switch (packet[0]) {
  167. case 'H': {
  168. Kernel::KThread* thread{nullptr};
  169. s64 thread_id{strtoll(command.data() + 1, nullptr, 16)};
  170. if (thread_id >= 1) {
  171. thread = GetThreadByID(thread_id);
  172. } else {
  173. thread = backend.GetActiveThread();
  174. }
  175. if (thread) {
  176. SendReply(GDB_STUB_REPLY_OK);
  177. backend.SetActiveThread(thread);
  178. } else {
  179. SendReply(GDB_STUB_REPLY_ERR);
  180. }
  181. break;
  182. }
  183. case 'T': {
  184. s64 thread_id{strtoll(command.data(), nullptr, 16)};
  185. if (GetThreadByID(thread_id)) {
  186. SendReply(GDB_STUB_REPLY_OK);
  187. } else {
  188. SendReply(GDB_STUB_REPLY_ERR);
  189. }
  190. break;
  191. }
  192. case 'Q':
  193. case 'q':
  194. HandleQuery(command);
  195. break;
  196. case '?':
  197. SendReply(arch->ThreadStatus(backend.GetActiveThread(), GDB_STUB_SIGTRAP));
  198. break;
  199. case 'k':
  200. LOG_INFO(Debug_GDBStub, "Shutting down emulation");
  201. actions.push_back(DebuggerAction::ShutdownEmulation);
  202. break;
  203. case 'g':
  204. SendReply(arch->ReadRegisters(backend.GetActiveThread()));
  205. break;
  206. case 'G':
  207. arch->WriteRegisters(backend.GetActiveThread(), command);
  208. SendReply(GDB_STUB_REPLY_OK);
  209. break;
  210. case 'p': {
  211. const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
  212. SendReply(arch->RegRead(backend.GetActiveThread(), reg));
  213. break;
  214. }
  215. case 'P': {
  216. const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1};
  217. const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
  218. arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep));
  219. SendReply(GDB_STUB_REPLY_OK);
  220. break;
  221. }
  222. case 'm': {
  223. const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
  224. const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
  225. const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
  226. if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
  227. std::vector<u8> mem(size);
  228. system.Memory().ReadBlock(addr, mem.data(), size);
  229. SendReply(Common::HexToString(mem));
  230. } else {
  231. SendReply(GDB_STUB_REPLY_ERR);
  232. }
  233. break;
  234. }
  235. case 'M': {
  236. const auto size_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
  237. const auto mem_sep{std::find(command.begin(), command.end(), ':') - command.begin() + 1};
  238. const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
  239. const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
  240. const auto mem_substr{std::string_view(command).substr(mem_sep)};
  241. const auto mem{Common::HexStringToVector(mem_substr, false)};
  242. if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
  243. system.Memory().WriteBlock(addr, mem.data(), size);
  244. system.InvalidateCpuInstructionCacheRange(addr, size);
  245. SendReply(GDB_STUB_REPLY_OK);
  246. } else {
  247. SendReply(GDB_STUB_REPLY_ERR);
  248. }
  249. break;
  250. }
  251. case 's':
  252. actions.push_back(DebuggerAction::StepThreadLocked);
  253. break;
  254. case 'C':
  255. case 'c':
  256. actions.push_back(DebuggerAction::Continue);
  257. break;
  258. case 'Z':
  259. HandleBreakpointInsert(command);
  260. break;
  261. case 'z':
  262. HandleBreakpointRemove(command);
  263. break;
  264. default:
  265. SendReply(GDB_STUB_REPLY_EMPTY);
  266. break;
  267. }
  268. }
  269. enum class BreakpointType {
  270. Software = 0,
  271. Hardware = 1,
  272. WriteWatch = 2,
  273. ReadWatch = 3,
  274. AccessWatch = 4,
  275. };
  276. void GDBStub::HandleBreakpointInsert(std::string_view command) {
  277. const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
  278. const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
  279. const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
  280. command.begin() + 1};
  281. const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
  282. const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
  283. if (!system.Memory().IsValidVirtualAddressRange(addr, size)) {
  284. SendReply(GDB_STUB_REPLY_ERR);
  285. return;
  286. }
  287. bool success{};
  288. switch (type) {
  289. case BreakpointType::Software:
  290. replaced_instructions[addr] = system.Memory().Read32(addr);
  291. system.Memory().Write32(addr, arch->BreakpointInstruction());
  292. system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
  293. success = true;
  294. break;
  295. case BreakpointType::WriteWatch:
  296. success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
  297. Kernel::DebugWatchpointType::Write);
  298. break;
  299. case BreakpointType::ReadWatch:
  300. success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
  301. Kernel::DebugWatchpointType::Read);
  302. break;
  303. case BreakpointType::AccessWatch:
  304. success = system.CurrentProcess()->InsertWatchpoint(
  305. system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
  306. break;
  307. case BreakpointType::Hardware:
  308. default:
  309. SendReply(GDB_STUB_REPLY_EMPTY);
  310. return;
  311. }
  312. if (success) {
  313. SendReply(GDB_STUB_REPLY_OK);
  314. } else {
  315. SendReply(GDB_STUB_REPLY_ERR);
  316. }
  317. }
  318. void GDBStub::HandleBreakpointRemove(std::string_view command) {
  319. const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
  320. const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
  321. const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
  322. command.begin() + 1};
  323. const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
  324. const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
  325. if (!system.Memory().IsValidVirtualAddressRange(addr, size)) {
  326. SendReply(GDB_STUB_REPLY_ERR);
  327. return;
  328. }
  329. bool success{};
  330. switch (type) {
  331. case BreakpointType::Software: {
  332. const auto orig_insn{replaced_instructions.find(addr)};
  333. if (orig_insn != replaced_instructions.end()) {
  334. system.Memory().Write32(addr, orig_insn->second);
  335. system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
  336. replaced_instructions.erase(addr);
  337. success = true;
  338. }
  339. break;
  340. }
  341. case BreakpointType::WriteWatch:
  342. success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
  343. Kernel::DebugWatchpointType::Write);
  344. break;
  345. case BreakpointType::ReadWatch:
  346. success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
  347. Kernel::DebugWatchpointType::Read);
  348. break;
  349. case BreakpointType::AccessWatch:
  350. success = system.CurrentProcess()->RemoveWatchpoint(
  351. system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
  352. break;
  353. case BreakpointType::Hardware:
  354. default:
  355. SendReply(GDB_STUB_REPLY_EMPTY);
  356. return;
  357. }
  358. if (success) {
  359. SendReply(GDB_STUB_REPLY_OK);
  360. } else {
  361. SendReply(GDB_STUB_REPLY_ERR);
  362. }
  363. }
  364. // Structure offsets are from Atmosphere
  365. // See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp
  366. static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory,
  367. const Kernel::KThread* thread) {
  368. // Read thread type from TLS
  369. const VAddr tls_thread_type{memory.Read32(thread->GetTLSAddress() + 0x1fc)};
  370. const VAddr argument_thread_type{thread->GetArgument()};
  371. if (argument_thread_type && tls_thread_type != argument_thread_type) {
  372. // Probably not created by nnsdk, no name available.
  373. return std::nullopt;
  374. }
  375. if (!tls_thread_type) {
  376. return std::nullopt;
  377. }
  378. const u16 version{memory.Read16(tls_thread_type + 0x26)};
  379. VAddr name_pointer{};
  380. if (version == 1) {
  381. name_pointer = memory.Read32(tls_thread_type + 0xe4);
  382. } else {
  383. name_pointer = memory.Read32(tls_thread_type + 0xe8);
  384. }
  385. if (!name_pointer) {
  386. // No name provided.
  387. return std::nullopt;
  388. }
  389. return memory.ReadCString(name_pointer, 256);
  390. }
  391. static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory,
  392. const Kernel::KThread* thread) {
  393. // Read thread type from TLS
  394. const VAddr tls_thread_type{memory.Read64(thread->GetTLSAddress() + 0x1f8)};
  395. const VAddr argument_thread_type{thread->GetArgument()};
  396. if (argument_thread_type && tls_thread_type != argument_thread_type) {
  397. // Probably not created by nnsdk, no name available.
  398. return std::nullopt;
  399. }
  400. if (!tls_thread_type) {
  401. return std::nullopt;
  402. }
  403. const u16 version{memory.Read16(tls_thread_type + 0x46)};
  404. VAddr name_pointer{};
  405. if (version == 1) {
  406. name_pointer = memory.Read64(tls_thread_type + 0x1a0);
  407. } else {
  408. name_pointer = memory.Read64(tls_thread_type + 0x1a8);
  409. }
  410. if (!name_pointer) {
  411. // No name provided.
  412. return std::nullopt;
  413. }
  414. return memory.ReadCString(name_pointer, 256);
  415. }
  416. static std::optional<std::string> GetThreadName(Core::System& system,
  417. const Kernel::KThread* thread) {
  418. if (system.CurrentProcess()->Is64BitProcess()) {
  419. return GetNameFromThreadType64(system.Memory(), thread);
  420. } else {
  421. return GetNameFromThreadType32(system.Memory(), thread);
  422. }
  423. }
  424. static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
  425. switch (thread->GetWaitReasonForDebugging()) {
  426. case Kernel::ThreadWaitReasonForDebugging::Sleep:
  427. return "Sleep";
  428. case Kernel::ThreadWaitReasonForDebugging::IPC:
  429. return "IPC";
  430. case Kernel::ThreadWaitReasonForDebugging::Synchronization:
  431. return "Synchronization";
  432. case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
  433. return "ConditionVar";
  434. case Kernel::ThreadWaitReasonForDebugging::Arbitration:
  435. return "Arbitration";
  436. case Kernel::ThreadWaitReasonForDebugging::Suspended:
  437. return "Suspended";
  438. default:
  439. return "Unknown";
  440. }
  441. }
  442. static std::string GetThreadState(const Kernel::KThread* thread) {
  443. switch (thread->GetState()) {
  444. case Kernel::ThreadState::Initialized:
  445. return "Initialized";
  446. case Kernel::ThreadState::Waiting:
  447. return fmt::format("Waiting ({})", GetThreadWaitReason(thread));
  448. case Kernel::ThreadState::Runnable:
  449. return "Runnable";
  450. case Kernel::ThreadState::Terminated:
  451. return "Terminated";
  452. default:
  453. return "Unknown";
  454. }
  455. }
  456. static std::string PaginateBuffer(std::string_view buffer, std::string_view request) {
  457. const auto amount{request.substr(request.find(',') + 1)};
  458. const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))};
  459. const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
  460. if (offset_val + amount_val > buffer.size()) {
  461. return fmt::format("l{}", buffer.substr(offset_val));
  462. } else {
  463. return fmt::format("m{}", buffer.substr(offset_val, amount_val));
  464. }
  465. }
  466. void GDBStub::HandleQuery(std::string_view command) {
  467. if (command.starts_with("TStatus")) {
  468. // no tracepoint support
  469. SendReply("T0");
  470. } else if (command.starts_with("Supported")) {
  471. SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
  472. "vContSupported+;QStartNoAckMode+");
  473. } else if (command.starts_with("Xfer:features:read:target.xml:")) {
  474. const auto target_xml{arch->GetTargetXML()};
  475. SendReply(PaginateBuffer(target_xml, command.substr(30)));
  476. } else if (command.starts_with("Offsets")) {
  477. Loader::AppLoader::Modules modules;
  478. system.GetAppLoader().ReadNSOModules(modules);
  479. const auto main = std::find_if(modules.begin(), modules.end(),
  480. [](const auto& key) { return key.second == "main"; });
  481. if (main != modules.end()) {
  482. SendReply(fmt::format("TextSeg={:x}", main->first));
  483. } else {
  484. SendReply(fmt::format("TextSeg={:x}",
  485. system.CurrentProcess()->PageTable().GetCodeRegionStart()));
  486. }
  487. } else if (command.starts_with("Xfer:libraries:read::")) {
  488. Loader::AppLoader::Modules modules;
  489. system.GetAppLoader().ReadNSOModules(modules);
  490. std::string buffer;
  491. buffer += R"(<?xml version="1.0"?>)";
  492. buffer += "<library-list>";
  493. for (const auto& [base, name] : modules) {
  494. buffer += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)",
  495. EscapeXML(name), base);
  496. }
  497. buffer += "</library-list>";
  498. SendReply(PaginateBuffer(buffer, command.substr(21)));
  499. } else if (command.starts_with("fThreadInfo")) {
  500. // beginning of list
  501. const auto& threads = system.GlobalSchedulerContext().GetThreadList();
  502. std::vector<std::string> thread_ids;
  503. for (const auto& thread : threads) {
  504. thread_ids.push_back(fmt::format("{:x}", thread->GetThreadID()));
  505. }
  506. SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
  507. } else if (command.starts_with("sThreadInfo")) {
  508. // end of list
  509. SendReply("l");
  510. } else if (command.starts_with("Xfer:threads:read::")) {
  511. std::string buffer;
  512. buffer += R"(<?xml version="1.0"?>)";
  513. buffer += "<threads>";
  514. const auto& threads = system.GlobalSchedulerContext().GetThreadList();
  515. for (const auto* thread : threads) {
  516. auto thread_name{GetThreadName(system, thread)};
  517. if (!thread_name) {
  518. thread_name = fmt::format("Thread {:d}", thread->GetThreadID());
  519. }
  520. buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)",
  521. thread->GetThreadID(), thread->GetActiveCore(),
  522. EscapeXML(*thread_name), GetThreadState(thread));
  523. }
  524. buffer += "</threads>";
  525. SendReply(PaginateBuffer(buffer, command.substr(19)));
  526. } else if (command.starts_with("Attached")) {
  527. SendReply("0");
  528. } else if (command.starts_with("StartNoAckMode")) {
  529. no_ack = true;
  530. SendReply(GDB_STUB_REPLY_OK);
  531. } else {
  532. SendReply(GDB_STUB_REPLY_EMPTY);
  533. }
  534. }
  535. void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) {
  536. if (command == "?") {
  537. // Continuing and stepping are supported
  538. // (signal is ignored, but required for GDB to use vCont)
  539. SendReply("vCont;c;C;s;S");
  540. return;
  541. }
  542. Kernel::KThread* stepped_thread{nullptr};
  543. bool lock_execution{true};
  544. std::vector<std::string> entries;
  545. boost::split(entries, command.substr(1), boost::is_any_of(";"));
  546. for (const auto& thread_action : entries) {
  547. std::vector<std::string> parts;
  548. boost::split(parts, thread_action, boost::is_any_of(":"));
  549. if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) {
  550. lock_execution = false;
  551. }
  552. if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) {
  553. stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
  554. }
  555. }
  556. if (stepped_thread) {
  557. backend.SetActiveThread(stepped_thread);
  558. actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked
  559. : DebuggerAction::StepThreadUnlocked);
  560. } else {
  561. actions.push_back(DebuggerAction::Continue);
  562. }
  563. }
  564. Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
  565. const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
  566. for (auto* thread : threads) {
  567. if (thread->GetThreadID() == thread_id) {
  568. return thread;
  569. }
  570. }
  571. return nullptr;
  572. }
  573. std::vector<char>::const_iterator GDBStub::CommandEnd() const {
  574. // Find the end marker
  575. const auto end{std::find(current_command.begin(), current_command.end(), GDB_STUB_END)};
  576. // Require the checksum to be present
  577. return std::min(end + 2, current_command.end());
  578. }
  579. std::optional<std::string> GDBStub::DetachCommand() {
  580. // Slice the string part from the beginning to the end marker
  581. const auto end{CommandEnd()};
  582. // Extract possible command data
  583. std::string data(current_command.data(), end - current_command.begin() + 1);
  584. // Shift over the remaining contents
  585. current_command.erase(current_command.begin(), end + 1);
  586. // Validate received command
  587. if (data[0] != GDB_STUB_START) {
  588. LOG_ERROR(Debug_GDBStub, "Invalid start data: {}", data[0]);
  589. return std::nullopt;
  590. }
  591. u8 calculated = CalculateChecksum(std::string_view(data).substr(1, data.size() - 4));
  592. u8 received = static_cast<u8>(strtoll(data.data() + data.size() - 2, nullptr, 16));
  593. // Verify checksum
  594. if (calculated != received) {
  595. LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}",
  596. calculated, received);
  597. return std::nullopt;
  598. }
  599. return data.substr(1, data.size() - 4);
  600. }
  601. void GDBStub::SendReply(std::string_view data) {
  602. const auto escaped{EscapeGDB(data)};
  603. const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END,
  604. CalculateChecksum(escaped))};
  605. LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output);
  606. // C++ string support is complete rubbish
  607. const u8* output_begin = reinterpret_cast<const u8*>(output.data());
  608. const u8* output_end = output_begin + output.size();
  609. backend.WriteToClient(std::span<const u8>(output_begin, output_end));
  610. }
  611. void GDBStub::SendStatus(char status) {
  612. if (no_ack) {
  613. return;
  614. }
  615. std::array<u8, 1> buf = {static_cast<u8>(status)};
  616. LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
  617. backend.WriteToClient(buf);
  618. }
  619. } // namespace Core