gdbstub.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  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 "common/settings.h"
  12. #include "core/arm/arm_interface.h"
  13. #include "core/core.h"
  14. #include "core/debugger/gdbstub.h"
  15. #include "core/debugger/gdbstub_arch.h"
  16. #include "core/hle/kernel/k_page_table.h"
  17. #include "core/hle/kernel/k_process.h"
  18. #include "core/hle/kernel/k_thread.h"
  19. #include "core/loader/loader.h"
  20. #include "core/memory.h"
  21. namespace Core {
  22. constexpr char GDB_STUB_START = '$';
  23. constexpr char GDB_STUB_END = '#';
  24. constexpr char GDB_STUB_ACK = '+';
  25. constexpr char GDB_STUB_NACK = '-';
  26. constexpr char GDB_STUB_INT3 = 0x03;
  27. constexpr int GDB_STUB_SIGTRAP = 5;
  28. constexpr char GDB_STUB_REPLY_ERR[] = "E01";
  29. constexpr char GDB_STUB_REPLY_OK[] = "OK";
  30. constexpr char GDB_STUB_REPLY_EMPTY[] = "";
  31. static u8 CalculateChecksum(std::string_view data) {
  32. return std::accumulate(data.begin(), data.end(), u8{0},
  33. [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); });
  34. }
  35. static std::string EscapeGDB(std::string_view data) {
  36. std::string escaped;
  37. escaped.reserve(data.size());
  38. for (char c : data) {
  39. switch (c) {
  40. case '#':
  41. escaped += "}\x03";
  42. break;
  43. case '$':
  44. escaped += "}\x04";
  45. break;
  46. case '*':
  47. escaped += "}\x0a";
  48. break;
  49. case '}':
  50. escaped += "}\x5d";
  51. break;
  52. default:
  53. escaped += c;
  54. break;
  55. }
  56. }
  57. return escaped;
  58. }
  59. static std::string EscapeXML(std::string_view data) {
  60. std::string escaped;
  61. escaped.reserve(data.size());
  62. for (char c : data) {
  63. switch (c) {
  64. case '&':
  65. escaped += "&amp;";
  66. break;
  67. case '"':
  68. escaped += "&quot;";
  69. break;
  70. case '<':
  71. escaped += "&lt;";
  72. break;
  73. case '>':
  74. escaped += "&gt;";
  75. break;
  76. default:
  77. escaped += c;
  78. break;
  79. }
  80. }
  81. return escaped;
  82. }
  83. GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
  84. : DebuggerFrontend(backend_), system{system_} {
  85. if (system.ApplicationProcess()->Is64BitProcess()) {
  86. arch = std::make_unique<GDBStubA64>();
  87. } else {
  88. arch = std::make_unique<GDBStubA32>();
  89. }
  90. }
  91. GDBStub::~GDBStub() = default;
  92. void GDBStub::Connected() {}
  93. void GDBStub::ShuttingDown() {}
  94. void GDBStub::Stopped(Kernel::KThread* thread) {
  95. SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
  96. }
  97. void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) {
  98. const auto status{arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)};
  99. switch (watch.type) {
  100. case Kernel::DebugWatchpointType::Read:
  101. SendReply(fmt::format("{}rwatch:{:x};", status, GetInteger(watch.start_address)));
  102. break;
  103. case Kernel::DebugWatchpointType::Write:
  104. SendReply(fmt::format("{}watch:{:x};", status, GetInteger(watch.start_address)));
  105. break;
  106. case Kernel::DebugWatchpointType::ReadOrWrite:
  107. default:
  108. SendReply(fmt::format("{}awatch:{:x};", status, GetInteger(watch.start_address)));
  109. break;
  110. }
  111. }
  112. std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
  113. std::vector<DebuggerAction> actions;
  114. current_command.insert(current_command.end(), data.begin(), data.end());
  115. while (current_command.size() != 0) {
  116. ProcessData(actions);
  117. }
  118. return actions;
  119. }
  120. void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) {
  121. const char c{current_command[0]};
  122. // Acknowledgement
  123. if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) {
  124. current_command.erase(current_command.begin());
  125. return;
  126. }
  127. // Interrupt
  128. if (c == GDB_STUB_INT3) {
  129. LOG_INFO(Debug_GDBStub, "Received interrupt");
  130. current_command.erase(current_command.begin());
  131. actions.push_back(DebuggerAction::Interrupt);
  132. SendStatus(GDB_STUB_ACK);
  133. return;
  134. }
  135. // Otherwise, require the data to be the start of a command
  136. if (c != GDB_STUB_START) {
  137. LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data());
  138. current_command.clear();
  139. SendStatus(GDB_STUB_NACK);
  140. return;
  141. }
  142. // Continue reading until command is complete
  143. while (CommandEnd() == current_command.end()) {
  144. const auto new_data{backend.ReadFromClient()};
  145. current_command.insert(current_command.end(), new_data.begin(), new_data.end());
  146. }
  147. // Execute and respond to GDB
  148. const auto command{DetachCommand()};
  149. if (command) {
  150. SendStatus(GDB_STUB_ACK);
  151. ExecuteCommand(*command, actions);
  152. } else {
  153. SendStatus(GDB_STUB_NACK);
  154. }
  155. }
  156. void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions) {
  157. LOG_TRACE(Debug_GDBStub, "Executing command: {}", packet);
  158. if (packet.length() == 0) {
  159. SendReply(GDB_STUB_REPLY_ERR);
  160. return;
  161. }
  162. if (packet.starts_with("vCont")) {
  163. HandleVCont(packet.substr(5), actions);
  164. return;
  165. }
  166. std::string_view command{packet.substr(1, packet.size())};
  167. switch (packet[0]) {
  168. case 'H': {
  169. Kernel::KThread* thread{nullptr};
  170. s64 thread_id{strtoll(command.data() + 1, nullptr, 16)};
  171. if (thread_id >= 1) {
  172. thread = GetThreadByID(thread_id);
  173. } else {
  174. thread = backend.GetActiveThread();
  175. }
  176. if (thread) {
  177. SendReply(GDB_STUB_REPLY_OK);
  178. backend.SetActiveThread(thread);
  179. } else {
  180. SendReply(GDB_STUB_REPLY_ERR);
  181. }
  182. break;
  183. }
  184. case 'T': {
  185. s64 thread_id{strtoll(command.data(), nullptr, 16)};
  186. if (GetThreadByID(thread_id)) {
  187. SendReply(GDB_STUB_REPLY_OK);
  188. } else {
  189. SendReply(GDB_STUB_REPLY_ERR);
  190. }
  191. break;
  192. }
  193. case 'Q':
  194. case 'q':
  195. HandleQuery(command);
  196. break;
  197. case '?':
  198. SendReply(arch->ThreadStatus(backend.GetActiveThread(), GDB_STUB_SIGTRAP));
  199. break;
  200. case 'k':
  201. LOG_INFO(Debug_GDBStub, "Shutting down emulation");
  202. actions.push_back(DebuggerAction::ShutdownEmulation);
  203. break;
  204. case 'g':
  205. SendReply(arch->ReadRegisters(backend.GetActiveThread()));
  206. break;
  207. case 'G':
  208. arch->WriteRegisters(backend.GetActiveThread(), command);
  209. SendReply(GDB_STUB_REPLY_OK);
  210. break;
  211. case 'p': {
  212. const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
  213. SendReply(arch->RegRead(backend.GetActiveThread(), reg));
  214. break;
  215. }
  216. case 'P': {
  217. const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1};
  218. const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
  219. arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep));
  220. SendReply(GDB_STUB_REPLY_OK);
  221. break;
  222. }
  223. case 'm': {
  224. const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
  225. const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
  226. const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
  227. if (system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) {
  228. std::vector<u8> mem(size);
  229. system.ApplicationMemory().ReadBlock(addr, mem.data(), size);
  230. SendReply(Common::HexToString(mem));
  231. } else {
  232. SendReply(GDB_STUB_REPLY_ERR);
  233. }
  234. break;
  235. }
  236. case 'M': {
  237. const auto size_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
  238. const auto mem_sep{std::find(command.begin(), command.end(), ':') - command.begin() + 1};
  239. const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
  240. const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
  241. const auto mem_substr{std::string_view(command).substr(mem_sep)};
  242. const auto mem{Common::HexStringToVector(mem_substr, false)};
  243. if (system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) {
  244. system.ApplicationMemory().WriteBlock(addr, mem.data(), size);
  245. system.InvalidateCpuInstructionCacheRange(addr, size);
  246. SendReply(GDB_STUB_REPLY_OK);
  247. } else {
  248. SendReply(GDB_STUB_REPLY_ERR);
  249. }
  250. break;
  251. }
  252. case 's':
  253. actions.push_back(DebuggerAction::StepThreadLocked);
  254. break;
  255. case 'C':
  256. case 'c':
  257. actions.push_back(DebuggerAction::Continue);
  258. break;
  259. case 'Z':
  260. HandleBreakpointInsert(command);
  261. break;
  262. case 'z':
  263. HandleBreakpointRemove(command);
  264. break;
  265. default:
  266. SendReply(GDB_STUB_REPLY_EMPTY);
  267. break;
  268. }
  269. }
  270. enum class BreakpointType {
  271. Software = 0,
  272. Hardware = 1,
  273. WriteWatch = 2,
  274. ReadWatch = 3,
  275. AccessWatch = 4,
  276. };
  277. void GDBStub::HandleBreakpointInsert(std::string_view command) {
  278. const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
  279. const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
  280. const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
  281. command.begin() + 1};
  282. const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
  283. const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
  284. if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) {
  285. SendReply(GDB_STUB_REPLY_ERR);
  286. return;
  287. }
  288. bool success{};
  289. switch (type) {
  290. case BreakpointType::Software:
  291. replaced_instructions[addr] = system.ApplicationMemory().Read32(addr);
  292. system.ApplicationMemory().Write32(addr, arch->BreakpointInstruction());
  293. system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
  294. success = true;
  295. break;
  296. case BreakpointType::WriteWatch:
  297. success = system.ApplicationProcess()->InsertWatchpoint(addr, size,
  298. Kernel::DebugWatchpointType::Write);
  299. break;
  300. case BreakpointType::ReadWatch:
  301. success = system.ApplicationProcess()->InsertWatchpoint(addr, size,
  302. Kernel::DebugWatchpointType::Read);
  303. break;
  304. case BreakpointType::AccessWatch:
  305. success = system.ApplicationProcess()->InsertWatchpoint(
  306. addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
  307. break;
  308. case BreakpointType::Hardware:
  309. default:
  310. SendReply(GDB_STUB_REPLY_EMPTY);
  311. return;
  312. }
  313. if (success) {
  314. SendReply(GDB_STUB_REPLY_OK);
  315. } else {
  316. SendReply(GDB_STUB_REPLY_ERR);
  317. }
  318. }
  319. void GDBStub::HandleBreakpointRemove(std::string_view command) {
  320. const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
  321. const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
  322. const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
  323. command.begin() + 1};
  324. const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
  325. const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
  326. if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) {
  327. SendReply(GDB_STUB_REPLY_ERR);
  328. return;
  329. }
  330. bool success{};
  331. switch (type) {
  332. case BreakpointType::Software: {
  333. const auto orig_insn{replaced_instructions.find(addr)};
  334. if (orig_insn != replaced_instructions.end()) {
  335. system.ApplicationMemory().Write32(addr, orig_insn->second);
  336. system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
  337. replaced_instructions.erase(addr);
  338. success = true;
  339. }
  340. break;
  341. }
  342. case BreakpointType::WriteWatch:
  343. success = system.ApplicationProcess()->RemoveWatchpoint(addr, size,
  344. Kernel::DebugWatchpointType::Write);
  345. break;
  346. case BreakpointType::ReadWatch:
  347. success = system.ApplicationProcess()->RemoveWatchpoint(addr, size,
  348. Kernel::DebugWatchpointType::Read);
  349. break;
  350. case BreakpointType::AccessWatch:
  351. success = system.ApplicationProcess()->RemoveWatchpoint(
  352. addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
  353. break;
  354. case BreakpointType::Hardware:
  355. default:
  356. SendReply(GDB_STUB_REPLY_EMPTY);
  357. return;
  358. }
  359. if (success) {
  360. SendReply(GDB_STUB_REPLY_OK);
  361. } else {
  362. SendReply(GDB_STUB_REPLY_ERR);
  363. }
  364. }
  365. // Structure offsets are from Atmosphere
  366. // See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp
  367. static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory,
  368. const Kernel::KThread* thread) {
  369. // Read thread type from TLS
  370. const VAddr tls_thread_type{memory.Read32(thread->GetTlsAddress() + 0x1fc)};
  371. const VAddr argument_thread_type{thread->GetArgument()};
  372. if (argument_thread_type && tls_thread_type != argument_thread_type) {
  373. // Probably not created by nnsdk, no name available.
  374. return std::nullopt;
  375. }
  376. if (!tls_thread_type) {
  377. return std::nullopt;
  378. }
  379. const u16 version{memory.Read16(tls_thread_type + 0x26)};
  380. VAddr name_pointer{};
  381. if (version == 1) {
  382. name_pointer = memory.Read32(tls_thread_type + 0xe4);
  383. } else {
  384. name_pointer = memory.Read32(tls_thread_type + 0xe8);
  385. }
  386. if (!name_pointer) {
  387. // No name provided.
  388. return std::nullopt;
  389. }
  390. return memory.ReadCString(name_pointer, 256);
  391. }
  392. static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory,
  393. const Kernel::KThread* thread) {
  394. // Read thread type from TLS
  395. const VAddr tls_thread_type{memory.Read64(thread->GetTlsAddress() + 0x1f8)};
  396. const VAddr argument_thread_type{thread->GetArgument()};
  397. if (argument_thread_type && tls_thread_type != argument_thread_type) {
  398. // Probably not created by nnsdk, no name available.
  399. return std::nullopt;
  400. }
  401. if (!tls_thread_type) {
  402. return std::nullopt;
  403. }
  404. const u16 version{memory.Read16(tls_thread_type + 0x46)};
  405. VAddr name_pointer{};
  406. if (version == 1) {
  407. name_pointer = memory.Read64(tls_thread_type + 0x1a0);
  408. } else {
  409. name_pointer = memory.Read64(tls_thread_type + 0x1a8);
  410. }
  411. if (!name_pointer) {
  412. // No name provided.
  413. return std::nullopt;
  414. }
  415. return memory.ReadCString(name_pointer, 256);
  416. }
  417. static std::optional<std::string> GetThreadName(Core::System& system,
  418. const Kernel::KThread* thread) {
  419. if (system.ApplicationProcess()->Is64BitProcess()) {
  420. return GetNameFromThreadType64(system.ApplicationMemory(), thread);
  421. } else {
  422. return GetNameFromThreadType32(system.ApplicationMemory(), thread);
  423. }
  424. }
  425. static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
  426. switch (thread->GetWaitReasonForDebugging()) {
  427. case Kernel::ThreadWaitReasonForDebugging::Sleep:
  428. return "Sleep";
  429. case Kernel::ThreadWaitReasonForDebugging::IPC:
  430. return "IPC";
  431. case Kernel::ThreadWaitReasonForDebugging::Synchronization:
  432. return "Synchronization";
  433. case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
  434. return "ConditionVar";
  435. case Kernel::ThreadWaitReasonForDebugging::Arbitration:
  436. return "Arbitration";
  437. case Kernel::ThreadWaitReasonForDebugging::Suspended:
  438. return "Suspended";
  439. default:
  440. return "Unknown";
  441. }
  442. }
  443. static std::string GetThreadState(const Kernel::KThread* thread) {
  444. switch (thread->GetState()) {
  445. case Kernel::ThreadState::Initialized:
  446. return "Initialized";
  447. case Kernel::ThreadState::Waiting:
  448. return fmt::format("Waiting ({})", GetThreadWaitReason(thread));
  449. case Kernel::ThreadState::Runnable:
  450. return "Runnable";
  451. case Kernel::ThreadState::Terminated:
  452. return "Terminated";
  453. default:
  454. return "Unknown";
  455. }
  456. }
  457. static std::string PaginateBuffer(std::string_view buffer, std::string_view request) {
  458. const auto amount{request.substr(request.find(',') + 1)};
  459. const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))};
  460. const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
  461. if (offset_val + amount_val > buffer.size()) {
  462. return fmt::format("l{}", buffer.substr(offset_val));
  463. } else {
  464. return fmt::format("m{}", buffer.substr(offset_val, amount_val));
  465. }
  466. }
  467. void GDBStub::HandleQuery(std::string_view command) {
  468. if (command.starts_with("TStatus")) {
  469. // no tracepoint support
  470. SendReply("T0");
  471. } else if (command.starts_with("Supported")) {
  472. SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
  473. "vContSupported+;QStartNoAckMode+");
  474. } else if (command.starts_with("Xfer:features:read:target.xml:")) {
  475. const auto target_xml{arch->GetTargetXML()};
  476. SendReply(PaginateBuffer(target_xml, command.substr(30)));
  477. } else if (command.starts_with("Offsets")) {
  478. Loader::AppLoader::Modules modules;
  479. system.GetAppLoader().ReadNSOModules(modules);
  480. const auto main = std::find_if(modules.begin(), modules.end(),
  481. [](const auto& key) { return key.second == "main"; });
  482. if (main != modules.end()) {
  483. SendReply(fmt::format("TextSeg={:x}", main->first));
  484. } else {
  485. SendReply(fmt::format(
  486. "TextSeg={:x}",
  487. GetInteger(system.ApplicationProcess()->PageTable().GetCodeRegionStart())));
  488. }
  489. } else if (command.starts_with("Xfer:libraries:read::")) {
  490. Loader::AppLoader::Modules modules;
  491. system.GetAppLoader().ReadNSOModules(modules);
  492. std::string buffer;
  493. buffer += R"(<?xml version="1.0"?>)";
  494. buffer += "<library-list>";
  495. for (const auto& [base, name] : modules) {
  496. buffer += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)",
  497. EscapeXML(name), base);
  498. }
  499. buffer += "</library-list>";
  500. SendReply(PaginateBuffer(buffer, command.substr(21)));
  501. } else if (command.starts_with("fThreadInfo")) {
  502. // beginning of list
  503. const auto& threads = system.ApplicationProcess()->GetThreadList();
  504. std::vector<std::string> thread_ids;
  505. for (const auto& thread : threads) {
  506. thread_ids.push_back(fmt::format("{:x}", thread->GetThreadId()));
  507. }
  508. SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
  509. } else if (command.starts_with("sThreadInfo")) {
  510. // end of list
  511. SendReply("l");
  512. } else if (command.starts_with("Xfer:threads:read::")) {
  513. std::string buffer;
  514. buffer += R"(<?xml version="1.0"?>)";
  515. buffer += "<threads>";
  516. const auto& threads = system.ApplicationProcess()->GetThreadList();
  517. for (const auto* thread : threads) {
  518. auto thread_name{GetThreadName(system, thread)};
  519. if (!thread_name) {
  520. thread_name = fmt::format("Thread {:d}", thread->GetThreadId());
  521. }
  522. buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)",
  523. thread->GetThreadId(), thread->GetActiveCore(),
  524. EscapeXML(*thread_name), GetThreadState(thread));
  525. }
  526. buffer += "</threads>";
  527. SendReply(PaginateBuffer(buffer, command.substr(19)));
  528. } else if (command.starts_with("Attached")) {
  529. SendReply("0");
  530. } else if (command.starts_with("StartNoAckMode")) {
  531. no_ack = true;
  532. SendReply(GDB_STUB_REPLY_OK);
  533. } else if (command.starts_with("Rcmd,")) {
  534. HandleRcmd(Common::HexStringToVector(command.substr(5), false));
  535. } else {
  536. SendReply(GDB_STUB_REPLY_EMPTY);
  537. }
  538. }
  539. void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) {
  540. if (command == "?") {
  541. // Continuing and stepping are supported
  542. // (signal is ignored, but required for GDB to use vCont)
  543. SendReply("vCont;c;C;s;S");
  544. return;
  545. }
  546. Kernel::KThread* stepped_thread{nullptr};
  547. bool lock_execution{true};
  548. std::vector<std::string> entries;
  549. boost::split(entries, command.substr(1), boost::is_any_of(";"));
  550. for (const auto& thread_action : entries) {
  551. std::vector<std::string> parts;
  552. boost::split(parts, thread_action, boost::is_any_of(":"));
  553. if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) {
  554. lock_execution = false;
  555. }
  556. if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) {
  557. stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
  558. }
  559. }
  560. if (stepped_thread) {
  561. backend.SetActiveThread(stepped_thread);
  562. actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked
  563. : DebuggerAction::StepThreadUnlocked);
  564. } else {
  565. actions.push_back(DebuggerAction::Continue);
  566. }
  567. }
  568. constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
  569. {"----- Free -----", Kernel::Svc::MemoryState::Free},
  570. {"Io ", Kernel::Svc::MemoryState::Io},
  571. {"Static ", Kernel::Svc::MemoryState::Static},
  572. {"Code ", Kernel::Svc::MemoryState::Code},
  573. {"CodeData ", Kernel::Svc::MemoryState::CodeData},
  574. {"Normal ", Kernel::Svc::MemoryState::Normal},
  575. {"Shared ", Kernel::Svc::MemoryState::Shared},
  576. {"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
  577. {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
  578. {"Ipc ", Kernel::Svc::MemoryState::Ipc},
  579. {"Stack ", Kernel::Svc::MemoryState::Stack},
  580. {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
  581. {"Transfered ", Kernel::Svc::MemoryState::Transfered},
  582. {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered},
  583. {"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
  584. {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
  585. {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
  586. {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
  587. {"Kernel ", Kernel::Svc::MemoryState::Kernel},
  588. {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
  589. {"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
  590. {"Coverage ", Kernel::Svc::MemoryState::Coverage},
  591. }};
  592. static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
  593. for (size_t i = 0; i < MemoryStateNames.size(); i++) {
  594. if (std::get<1>(MemoryStateNames[i]) == state) {
  595. return std::get<0>(MemoryStateNames[i]);
  596. }
  597. }
  598. return "Unknown ";
  599. }
  600. static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) {
  601. if (info.state == Kernel::Svc::MemoryState::Free) {
  602. return " ";
  603. }
  604. switch (info.permission) {
  605. case Kernel::Svc::MemoryPermission::ReadExecute:
  606. return "r-x";
  607. case Kernel::Svc::MemoryPermission::Read:
  608. return "r--";
  609. case Kernel::Svc::MemoryPermission::ReadWrite:
  610. return "rw-";
  611. default:
  612. return "---";
  613. }
  614. }
  615. static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) {
  616. Kernel::Svc::MemoryInfo mem_info;
  617. VAddr cur_addr{base};
  618. // Expect: r-x Code (.text)
  619. mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
  620. cur_addr = mem_info.base_address + mem_info.size;
  621. if (mem_info.state != Kernel::Svc::MemoryState::Code ||
  622. mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
  623. return cur_addr - 1;
  624. }
  625. // Expect: r-- Code (.rodata)
  626. mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
  627. cur_addr = mem_info.base_address + mem_info.size;
  628. if (mem_info.state != Kernel::Svc::MemoryState::Code ||
  629. mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
  630. return cur_addr - 1;
  631. }
  632. // Expect: rw- CodeData (.data)
  633. mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
  634. cur_addr = mem_info.base_address + mem_info.size;
  635. return cur_addr - 1;
  636. }
  637. void GDBStub::HandleRcmd(const std::vector<u8>& command) {
  638. std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
  639. std::string reply;
  640. auto* process = system.ApplicationProcess();
  641. auto& page_table = process->PageTable();
  642. const char* commands = "Commands:\n"
  643. " get fastmem\n"
  644. " get info\n"
  645. " get mappings\n";
  646. if (command_str == "get fastmem") {
  647. if (Settings::IsFastmemEnabled()) {
  648. const auto& impl = page_table.PageTableImpl();
  649. const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena);
  650. const auto region_bits = impl.current_address_space_width_in_bits;
  651. const auto region_size = 1ULL << region_bits;
  652. reply = fmt::format("Region bits: {}\n"
  653. "Host address: {:#x} - {:#x}\n",
  654. region_bits, region, region + region_size - 1);
  655. } else {
  656. reply = "Fastmem is not enabled.\n";
  657. }
  658. } else if (command_str == "get info") {
  659. Loader::AppLoader::Modules modules;
  660. system.GetAppLoader().ReadNSOModules(modules);
  661. reply = fmt::format("Process: {:#x} ({})\n"
  662. "Program Id: {:#018x}\n",
  663. process->GetProcessId(), process->GetName(), process->GetProgramId());
  664. reply += fmt::format("Layout:\n"
  665. " Alias: {:#012x} - {:#012x}\n"
  666. " Heap: {:#012x} - {:#012x}\n"
  667. " Aslr: {:#012x} - {:#012x}\n"
  668. " Stack: {:#012x} - {:#012x}\n"
  669. "Modules:\n",
  670. GetInteger(page_table.GetAliasRegionStart()),
  671. GetInteger(page_table.GetAliasRegionEnd()),
  672. GetInteger(page_table.GetHeapRegionStart()),
  673. GetInteger(page_table.GetHeapRegionEnd()),
  674. GetInteger(page_table.GetAliasCodeRegionStart()),
  675. GetInteger(page_table.GetAliasCodeRegionEnd()),
  676. GetInteger(page_table.GetStackRegionStart()),
  677. GetInteger(page_table.GetStackRegionEnd()));
  678. for (const auto& [vaddr, name] : modules) {
  679. reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
  680. GetModuleEnd(page_table, vaddr), name);
  681. }
  682. } else if (command_str == "get mappings") {
  683. reply = "Mappings:\n";
  684. VAddr cur_addr = 0;
  685. while (true) {
  686. using MemoryAttribute = Kernel::Svc::MemoryAttribute;
  687. auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
  688. if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
  689. mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) {
  690. const char* state = GetMemoryStateName(mem_info.state);
  691. const char* perm = GetMemoryPermissionString(mem_info);
  692. const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
  693. const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
  694. const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
  695. const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
  696. reply +=
  697. fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
  698. mem_info.base_address, mem_info.base_address + mem_info.size - 1,
  699. perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
  700. }
  701. const uintptr_t next_address = mem_info.base_address + mem_info.size;
  702. if (next_address <= cur_addr) {
  703. break;
  704. }
  705. cur_addr = next_address;
  706. }
  707. } else if (command_str == "help") {
  708. reply = commands;
  709. } else {
  710. reply = "Unknown command.\n";
  711. reply += commands;
  712. }
  713. std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
  714. SendReply(Common::HexToString(reply_span, false));
  715. }
  716. Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
  717. const auto& threads{system.ApplicationProcess()->GetThreadList()};
  718. for (auto* thread : threads) {
  719. if (thread->GetThreadId() == thread_id) {
  720. return thread;
  721. }
  722. }
  723. return nullptr;
  724. }
  725. std::vector<char>::const_iterator GDBStub::CommandEnd() const {
  726. // Find the end marker
  727. const auto end{std::find(current_command.begin(), current_command.end(), GDB_STUB_END)};
  728. // Require the checksum to be present
  729. return std::min(end + 2, current_command.end());
  730. }
  731. std::optional<std::string> GDBStub::DetachCommand() {
  732. // Slice the string part from the beginning to the end marker
  733. const auto end{CommandEnd()};
  734. // Extract possible command data
  735. std::string data(current_command.data(), end - current_command.begin() + 1);
  736. // Shift over the remaining contents
  737. current_command.erase(current_command.begin(), end + 1);
  738. // Validate received command
  739. if (data[0] != GDB_STUB_START) {
  740. LOG_ERROR(Debug_GDBStub, "Invalid start data: {}", data[0]);
  741. return std::nullopt;
  742. }
  743. u8 calculated = CalculateChecksum(std::string_view(data).substr(1, data.size() - 4));
  744. u8 received = static_cast<u8>(strtoll(data.data() + data.size() - 2, nullptr, 16));
  745. // Verify checksum
  746. if (calculated != received) {
  747. LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}",
  748. calculated, received);
  749. return std::nullopt;
  750. }
  751. return data.substr(1, data.size() - 4);
  752. }
  753. void GDBStub::SendReply(std::string_view data) {
  754. const auto escaped{EscapeGDB(data)};
  755. const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END,
  756. CalculateChecksum(escaped))};
  757. LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output);
  758. // C++ string support is complete rubbish
  759. const u8* output_begin = reinterpret_cast<const u8*>(output.data());
  760. const u8* output_end = output_begin + output.size();
  761. backend.WriteToClient(std::span<const u8>(output_begin, output_end));
  762. }
  763. void GDBStub::SendStatus(char status) {
  764. if (no_ack) {
  765. return;
  766. }
  767. std::array<u8, 1> buf = {static_cast<u8>(status)};
  768. LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
  769. backend.WriteToClient(buf);
  770. }
  771. } // namespace Core