gdbstub.cpp 28 KB

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