diff options
Diffstat (limited to 'src/core/debugger/gdbstub.cpp')
| -rw-r--r-- | src/core/debugger/gdbstub.cpp | 243 |
1 files changed, 11 insertions, 232 deletions
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 148dd3e39..66e46c4ba 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include "common/settings.h" | 16 | #include "common/settings.h" |
| 17 | #include "common/string_util.h" | 17 | #include "common/string_util.h" |
| 18 | #include "core/arm/arm_interface.h" | 18 | #include "core/arm/arm_interface.h" |
| 19 | #include "core/arm/debug.h" | ||
| 19 | #include "core/core.h" | 20 | #include "core/core.h" |
| 20 | #include "core/debugger/gdbstub.h" | 21 | #include "core/debugger/gdbstub.h" |
| 21 | #include "core/debugger/gdbstub_arch.h" | 22 | #include "core/debugger/gdbstub_arch.h" |
| @@ -310,7 +311,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction | |||
| 310 | const auto mem{Common::HexStringToVector(mem_substr, false)}; | 311 | const auto mem{Common::HexStringToVector(mem_substr, false)}; |
| 311 | 312 | ||
| 312 | if (system.ApplicationMemory().WriteBlock(addr, mem.data(), size)) { | 313 | if (system.ApplicationMemory().WriteBlock(addr, mem.data(), size)) { |
| 313 | system.InvalidateCpuInstructionCacheRange(addr, size); | 314 | Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, size); |
| 314 | SendReply(GDB_STUB_REPLY_OK); | 315 | SendReply(GDB_STUB_REPLY_OK); |
| 315 | } else { | 316 | } else { |
| 316 | SendReply(GDB_STUB_REPLY_ERR); | 317 | SendReply(GDB_STUB_REPLY_ERR); |
| @@ -363,7 +364,7 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) { | |||
| 363 | case BreakpointType::Software: | 364 | case BreakpointType::Software: |
| 364 | replaced_instructions[addr] = system.ApplicationMemory().Read32(addr); | 365 | replaced_instructions[addr] = system.ApplicationMemory().Read32(addr); |
| 365 | system.ApplicationMemory().Write32(addr, arch->BreakpointInstruction()); | 366 | system.ApplicationMemory().Write32(addr, arch->BreakpointInstruction()); |
| 366 | system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); | 367 | Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32)); |
| 367 | success = true; | 368 | success = true; |
| 368 | break; | 369 | break; |
| 369 | case BreakpointType::WriteWatch: | 370 | case BreakpointType::WriteWatch: |
| @@ -411,7 +412,7 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) { | |||
| 411 | const auto orig_insn{replaced_instructions.find(addr)}; | 412 | const auto orig_insn{replaced_instructions.find(addr)}; |
| 412 | if (orig_insn != replaced_instructions.end()) { | 413 | if (orig_insn != replaced_instructions.end()) { |
| 413 | system.ApplicationMemory().Write32(addr, orig_insn->second); | 414 | system.ApplicationMemory().Write32(addr, orig_insn->second); |
| 414 | system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); | 415 | Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32)); |
| 415 | replaced_instructions.erase(addr); | 416 | replaced_instructions.erase(addr); |
| 416 | success = true; | 417 | success = true; |
| 417 | } | 418 | } |
| @@ -442,114 +443,6 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) { | |||
| 442 | } | 443 | } |
| 443 | } | 444 | } |
| 444 | 445 | ||
| 445 | // Structure offsets are from Atmosphere | ||
| 446 | // See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp | ||
| 447 | |||
| 448 | static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory, | ||
| 449 | const Kernel::KThread& thread) { | ||
| 450 | // Read thread type from TLS | ||
| 451 | const VAddr tls_thread_type{memory.Read32(thread.GetTlsAddress() + 0x1fc)}; | ||
| 452 | const VAddr argument_thread_type{thread.GetArgument()}; | ||
| 453 | |||
| 454 | if (argument_thread_type && tls_thread_type != argument_thread_type) { | ||
| 455 | // Probably not created by nnsdk, no name available. | ||
| 456 | return std::nullopt; | ||
| 457 | } | ||
| 458 | |||
| 459 | if (!tls_thread_type) { | ||
| 460 | return std::nullopt; | ||
| 461 | } | ||
| 462 | |||
| 463 | const u16 version{memory.Read16(tls_thread_type + 0x26)}; | ||
| 464 | VAddr name_pointer{}; | ||
| 465 | if (version == 1) { | ||
| 466 | name_pointer = memory.Read32(tls_thread_type + 0xe4); | ||
| 467 | } else { | ||
| 468 | name_pointer = memory.Read32(tls_thread_type + 0xe8); | ||
| 469 | } | ||
| 470 | |||
| 471 | if (!name_pointer) { | ||
| 472 | // No name provided. | ||
| 473 | return std::nullopt; | ||
| 474 | } | ||
| 475 | |||
| 476 | return memory.ReadCString(name_pointer, 256); | ||
| 477 | } | ||
| 478 | |||
| 479 | static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory, | ||
| 480 | const Kernel::KThread& thread) { | ||
| 481 | // Read thread type from TLS | ||
| 482 | const VAddr tls_thread_type{memory.Read64(thread.GetTlsAddress() + 0x1f8)}; | ||
| 483 | const VAddr argument_thread_type{thread.GetArgument()}; | ||
| 484 | |||
| 485 | if (argument_thread_type && tls_thread_type != argument_thread_type) { | ||
| 486 | // Probably not created by nnsdk, no name available. | ||
| 487 | return std::nullopt; | ||
| 488 | } | ||
| 489 | |||
| 490 | if (!tls_thread_type) { | ||
| 491 | return std::nullopt; | ||
| 492 | } | ||
| 493 | |||
| 494 | const u16 version{memory.Read16(tls_thread_type + 0x46)}; | ||
| 495 | VAddr name_pointer{}; | ||
| 496 | if (version == 1) { | ||
| 497 | name_pointer = memory.Read64(tls_thread_type + 0x1a0); | ||
| 498 | } else { | ||
| 499 | name_pointer = memory.Read64(tls_thread_type + 0x1a8); | ||
| 500 | } | ||
| 501 | |||
| 502 | if (!name_pointer) { | ||
| 503 | // No name provided. | ||
| 504 | return std::nullopt; | ||
| 505 | } | ||
| 506 | |||
| 507 | return memory.ReadCString(name_pointer, 256); | ||
| 508 | } | ||
| 509 | |||
| 510 | static std::optional<std::string> GetThreadName(Core::System& system, | ||
| 511 | const Kernel::KThread& thread) { | ||
| 512 | if (system.ApplicationProcess()->Is64Bit()) { | ||
| 513 | return GetNameFromThreadType64(system.ApplicationMemory(), thread); | ||
| 514 | } else { | ||
| 515 | return GetNameFromThreadType32(system.ApplicationMemory(), thread); | ||
| 516 | } | ||
| 517 | } | ||
| 518 | |||
| 519 | static std::string_view GetThreadWaitReason(const Kernel::KThread& thread) { | ||
| 520 | switch (thread.GetWaitReasonForDebugging()) { | ||
| 521 | case Kernel::ThreadWaitReasonForDebugging::Sleep: | ||
| 522 | return "Sleep"; | ||
| 523 | case Kernel::ThreadWaitReasonForDebugging::IPC: | ||
| 524 | return "IPC"; | ||
| 525 | case Kernel::ThreadWaitReasonForDebugging::Synchronization: | ||
| 526 | return "Synchronization"; | ||
| 527 | case Kernel::ThreadWaitReasonForDebugging::ConditionVar: | ||
| 528 | return "ConditionVar"; | ||
| 529 | case Kernel::ThreadWaitReasonForDebugging::Arbitration: | ||
| 530 | return "Arbitration"; | ||
| 531 | case Kernel::ThreadWaitReasonForDebugging::Suspended: | ||
| 532 | return "Suspended"; | ||
| 533 | default: | ||
| 534 | return "Unknown"; | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | static std::string GetThreadState(const Kernel::KThread& thread) { | ||
| 539 | switch (thread.GetState()) { | ||
| 540 | case Kernel::ThreadState::Initialized: | ||
| 541 | return "Initialized"; | ||
| 542 | case Kernel::ThreadState::Waiting: | ||
| 543 | return fmt::format("Waiting ({})", GetThreadWaitReason(thread)); | ||
| 544 | case Kernel::ThreadState::Runnable: | ||
| 545 | return "Runnable"; | ||
| 546 | case Kernel::ThreadState::Terminated: | ||
| 547 | return "Terminated"; | ||
| 548 | default: | ||
| 549 | return "Unknown"; | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | static std::string PaginateBuffer(std::string_view buffer, std::string_view request) { | 446 | static std::string PaginateBuffer(std::string_view buffer, std::string_view request) { |
| 554 | const auto amount{request.substr(request.find(',') + 1)}; | 447 | const auto amount{request.substr(request.find(',') + 1)}; |
| 555 | const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))}; | 448 | const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))}; |
| @@ -562,120 +455,6 @@ static std::string PaginateBuffer(std::string_view buffer, std::string_view requ | |||
| 562 | } | 455 | } |
| 563 | } | 456 | } |
| 564 | 457 | ||
| 565 | static VAddr GetModuleEnd(Kernel::KProcessPageTable& page_table, VAddr base) { | ||
| 566 | Kernel::KMemoryInfo mem_info; | ||
| 567 | Kernel::Svc::MemoryInfo svc_mem_info; | ||
| 568 | Kernel::Svc::PageInfo page_info; | ||
| 569 | VAddr cur_addr{base}; | ||
| 570 | |||
| 571 | // Expect: r-x Code (.text) | ||
| 572 | R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); | ||
| 573 | svc_mem_info = mem_info.GetSvcMemoryInfo(); | ||
| 574 | cur_addr = svc_mem_info.base_address + svc_mem_info.size; | ||
| 575 | if (svc_mem_info.state != Kernel::Svc::MemoryState::Code || | ||
| 576 | svc_mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) { | ||
| 577 | return cur_addr - 1; | ||
| 578 | } | ||
| 579 | |||
| 580 | // Expect: r-- Code (.rodata) | ||
| 581 | R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); | ||
| 582 | svc_mem_info = mem_info.GetSvcMemoryInfo(); | ||
| 583 | cur_addr = svc_mem_info.base_address + svc_mem_info.size; | ||
| 584 | if (svc_mem_info.state != Kernel::Svc::MemoryState::Code || | ||
| 585 | svc_mem_info.permission != Kernel::Svc::MemoryPermission::Read) { | ||
| 586 | return cur_addr - 1; | ||
| 587 | } | ||
| 588 | |||
| 589 | // Expect: rw- CodeData (.data) | ||
| 590 | R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); | ||
| 591 | svc_mem_info = mem_info.GetSvcMemoryInfo(); | ||
| 592 | cur_addr = svc_mem_info.base_address + svc_mem_info.size; | ||
| 593 | return cur_addr - 1; | ||
| 594 | } | ||
| 595 | |||
| 596 | static Loader::AppLoader::Modules FindModules(Core::System& system) { | ||
| 597 | Loader::AppLoader::Modules modules; | ||
| 598 | |||
| 599 | auto& page_table = system.ApplicationProcess()->GetPageTable(); | ||
| 600 | auto& memory = system.ApplicationMemory(); | ||
| 601 | VAddr cur_addr = 0; | ||
| 602 | |||
| 603 | // Look for executable sections in Code or AliasCode regions. | ||
| 604 | while (true) { | ||
| 605 | Kernel::KMemoryInfo mem_info{}; | ||
| 606 | Kernel::Svc::PageInfo page_info{}; | ||
| 607 | R_ASSERT( | ||
| 608 | page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); | ||
| 609 | auto svc_mem_info = mem_info.GetSvcMemoryInfo(); | ||
| 610 | |||
| 611 | if (svc_mem_info.permission == Kernel::Svc::MemoryPermission::ReadExecute && | ||
| 612 | (svc_mem_info.state == Kernel::Svc::MemoryState::Code || | ||
| 613 | svc_mem_info.state == Kernel::Svc::MemoryState::AliasCode)) { | ||
| 614 | // Try to read the module name from its path. | ||
| 615 | constexpr s32 PathLengthMax = 0x200; | ||
| 616 | struct { | ||
| 617 | u32 zero; | ||
| 618 | s32 path_length; | ||
| 619 | std::array<char, PathLengthMax> path; | ||
| 620 | } module_path; | ||
| 621 | |||
| 622 | if (memory.ReadBlock(svc_mem_info.base_address + svc_mem_info.size, &module_path, | ||
| 623 | sizeof(module_path))) { | ||
| 624 | if (module_path.zero == 0 && module_path.path_length > 0) { | ||
| 625 | // Truncate module name. | ||
| 626 | module_path.path[PathLengthMax - 1] = '\0'; | ||
| 627 | |||
| 628 | // Ignore leading directories. | ||
| 629 | char* path_pointer = module_path.path.data(); | ||
| 630 | |||
| 631 | for (s32 i = 0; i < std::min(PathLengthMax, module_path.path_length) && | ||
| 632 | module_path.path[i] != '\0'; | ||
| 633 | i++) { | ||
| 634 | if (module_path.path[i] == '/' || module_path.path[i] == '\\') { | ||
| 635 | path_pointer = module_path.path.data() + i + 1; | ||
| 636 | } | ||
| 637 | } | ||
| 638 | |||
| 639 | // Insert output. | ||
| 640 | modules.emplace(svc_mem_info.base_address, path_pointer); | ||
| 641 | } | ||
| 642 | } | ||
| 643 | } | ||
| 644 | |||
| 645 | // Check if we're done. | ||
| 646 | const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size; | ||
| 647 | if (next_address <= cur_addr) { | ||
| 648 | break; | ||
| 649 | } | ||
| 650 | |||
| 651 | cur_addr = next_address; | ||
| 652 | } | ||
| 653 | |||
| 654 | return modules; | ||
| 655 | } | ||
| 656 | |||
| 657 | static VAddr FindMainModuleEntrypoint(Core::System& system) { | ||
| 658 | Loader::AppLoader::Modules modules; | ||
| 659 | system.GetAppLoader().ReadNSOModules(modules); | ||
| 660 | |||
| 661 | // Do we have a module named main? | ||
| 662 | const auto main = std::find_if(modules.begin(), modules.end(), | ||
| 663 | [](const auto& key) { return key.second == "main"; }); | ||
| 664 | |||
| 665 | if (main != modules.end()) { | ||
| 666 | return main->first; | ||
| 667 | } | ||
| 668 | |||
| 669 | // Do we have any loaded executable sections? | ||
| 670 | modules = FindModules(system); | ||
| 671 | if (!modules.empty()) { | ||
| 672 | return modules.begin()->first; | ||
| 673 | } | ||
| 674 | |||
| 675 | // As a last resort, use the start of the code region. | ||
| 676 | return GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart()); | ||
| 677 | } | ||
| 678 | |||
| 679 | void GDBStub::HandleQuery(std::string_view command) { | 458 | void GDBStub::HandleQuery(std::string_view command) { |
| 680 | if (command.starts_with("TStatus")) { | 459 | if (command.starts_with("TStatus")) { |
| 681 | // no tracepoint support | 460 | // no tracepoint support |
| @@ -687,10 +466,10 @@ void GDBStub::HandleQuery(std::string_view command) { | |||
| 687 | const auto target_xml{arch->GetTargetXML()}; | 466 | const auto target_xml{arch->GetTargetXML()}; |
| 688 | SendReply(PaginateBuffer(target_xml, command.substr(30))); | 467 | SendReply(PaginateBuffer(target_xml, command.substr(30))); |
| 689 | } else if (command.starts_with("Offsets")) { | 468 | } else if (command.starts_with("Offsets")) { |
| 690 | const auto main_offset = FindMainModuleEntrypoint(system); | 469 | const auto main_offset = Core::FindMainModuleEntrypoint(system.ApplicationProcess()); |
| 691 | SendReply(fmt::format("TextSeg={:x}", main_offset)); | 470 | SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset))); |
| 692 | } else if (command.starts_with("Xfer:libraries:read::")) { | 471 | } else if (command.starts_with("Xfer:libraries:read::")) { |
| 693 | auto modules = FindModules(system); | 472 | auto modules = Core::FindModules(system.ApplicationProcess()); |
| 694 | 473 | ||
| 695 | std::string buffer; | 474 | std::string buffer; |
| 696 | buffer += R"(<?xml version="1.0"?>)"; | 475 | buffer += R"(<?xml version="1.0"?>)"; |
| @@ -720,14 +499,14 @@ void GDBStub::HandleQuery(std::string_view command) { | |||
| 720 | 499 | ||
| 721 | const auto& threads = system.ApplicationProcess()->GetThreadList(); | 500 | const auto& threads = system.ApplicationProcess()->GetThreadList(); |
| 722 | for (const auto& thread : threads) { | 501 | for (const auto& thread : threads) { |
| 723 | auto thread_name{GetThreadName(system, thread)}; | 502 | auto thread_name{Core::GetThreadName(&thread)}; |
| 724 | if (!thread_name) { | 503 | if (!thread_name) { |
| 725 | thread_name = fmt::format("Thread {:d}", thread.GetThreadId()); | 504 | thread_name = fmt::format("Thread {:d}", thread.GetThreadId()); |
| 726 | } | 505 | } |
| 727 | 506 | ||
| 728 | buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)", | 507 | buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)", |
| 729 | thread.GetThreadId(), thread.GetActiveCore(), | 508 | thread.GetThreadId(), thread.GetActiveCore(), |
| 730 | EscapeXML(*thread_name), GetThreadState(thread)); | 509 | EscapeXML(*thread_name), GetThreadState(&thread)); |
| 731 | } | 510 | } |
| 732 | 511 | ||
| 733 | buffer += "</threads>"; | 512 | buffer += "</threads>"; |
| @@ -856,7 +635,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { | |||
| 856 | reply = "Fastmem is not enabled.\n"; | 635 | reply = "Fastmem is not enabled.\n"; |
| 857 | } | 636 | } |
| 858 | } else if (command_str == "get info") { | 637 | } else if (command_str == "get info") { |
| 859 | auto modules = FindModules(system); | 638 | auto modules = Core::FindModules(process); |
| 860 | 639 | ||
| 861 | reply = fmt::format("Process: {:#x} ({})\n" | 640 | reply = fmt::format("Process: {:#x} ({})\n" |
| 862 | "Program Id: {:#018x}\n", | 641 | "Program Id: {:#018x}\n", |
| @@ -880,7 +659,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { | |||
| 880 | 659 | ||
| 881 | for (const auto& [vaddr, name] : modules) { | 660 | for (const auto& [vaddr, name] : modules) { |
| 882 | reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, | 661 | reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, |
| 883 | GetModuleEnd(page_table, vaddr), name); | 662 | GetInteger(Core::GetModuleEnd(process, vaddr)), name); |
| 884 | } | 663 | } |
| 885 | } else if (command_str == "get mappings") { | 664 | } else if (command_str == "get mappings") { |
| 886 | reply = "Mappings:\n"; | 665 | reply = "Mappings:\n"; |