diff options
| author | 2023-11-11 20:01:53 -0500 | |
|---|---|---|
| committer | 2023-11-11 20:01:53 -0500 | |
| commit | 5f6666a7cdf8ebd6b19cb465d072948f585230d8 (patch) | |
| tree | 0d68e4f021a312b4049fd0718dff5e05d1c33821 /src/core/debugger/gdbstub.cpp | |
| parent | Merge pull request #11992 from t895/frame-check (diff) | |
| parent | gdbstub: read module information from memory layout (diff) | |
| download | yuzu-5f6666a7cdf8ebd6b19cb465d072948f585230d8.tar.gz yuzu-5f6666a7cdf8ebd6b19cb465d072948f585230d8.tar.xz yuzu-5f6666a7cdf8ebd6b19cb465d072948f585230d8.zip | |
Merge pull request #12003 from liamwhite/read-modules
gdbstub: read module information from memory layout
Diffstat (limited to 'src/core/debugger/gdbstub.cpp')
| -rw-r--r-- | src/core/debugger/gdbstub.cpp | 165 |
1 files changed, 118 insertions, 47 deletions
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index e9bf57895..148dd3e39 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp | |||
| @@ -562,6 +562,120 @@ static std::string PaginateBuffer(std::string_view buffer, std::string_view requ | |||
| 562 | } | 562 | } |
| 563 | } | 563 | } |
| 564 | 564 | ||
| 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 | |||
| 565 | void GDBStub::HandleQuery(std::string_view command) { | 679 | void GDBStub::HandleQuery(std::string_view command) { |
| 566 | if (command.starts_with("TStatus")) { | 680 | if (command.starts_with("TStatus")) { |
| 567 | // no tracepoint support | 681 | // no tracepoint support |
| @@ -573,21 +687,10 @@ void GDBStub::HandleQuery(std::string_view command) { | |||
| 573 | const auto target_xml{arch->GetTargetXML()}; | 687 | const auto target_xml{arch->GetTargetXML()}; |
| 574 | SendReply(PaginateBuffer(target_xml, command.substr(30))); | 688 | SendReply(PaginateBuffer(target_xml, command.substr(30))); |
| 575 | } else if (command.starts_with("Offsets")) { | 689 | } else if (command.starts_with("Offsets")) { |
| 576 | Loader::AppLoader::Modules modules; | 690 | const auto main_offset = FindMainModuleEntrypoint(system); |
| 577 | system.GetAppLoader().ReadNSOModules(modules); | 691 | SendReply(fmt::format("TextSeg={:x}", main_offset)); |
| 578 | |||
| 579 | const auto main = std::find_if(modules.begin(), modules.end(), | ||
| 580 | [](const auto& key) { return key.second == "main"; }); | ||
| 581 | if (main != modules.end()) { | ||
| 582 | SendReply(fmt::format("TextSeg={:x}", main->first)); | ||
| 583 | } else { | ||
| 584 | SendReply(fmt::format( | ||
| 585 | "TextSeg={:x}", | ||
| 586 | GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart()))); | ||
| 587 | } | ||
| 588 | } else if (command.starts_with("Xfer:libraries:read::")) { | 692 | } else if (command.starts_with("Xfer:libraries:read::")) { |
| 589 | Loader::AppLoader::Modules modules; | 693 | auto modules = FindModules(system); |
| 590 | system.GetAppLoader().ReadNSOModules(modules); | ||
| 591 | 694 | ||
| 592 | std::string buffer; | 695 | std::string buffer; |
| 593 | buffer += R"(<?xml version="1.0"?>)"; | 696 | buffer += R"(<?xml version="1.0"?>)"; |
| @@ -727,37 +830,6 @@ static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::Memory | |||
| 727 | } | 830 | } |
| 728 | } | 831 | } |
| 729 | 832 | ||
| 730 | static VAddr GetModuleEnd(Kernel::KProcessPageTable& page_table, VAddr base) { | ||
| 731 | Kernel::KMemoryInfo mem_info; | ||
| 732 | Kernel::Svc::MemoryInfo svc_mem_info; | ||
| 733 | Kernel::Svc::PageInfo page_info; | ||
| 734 | VAddr cur_addr{base}; | ||
| 735 | |||
| 736 | // Expect: r-x Code (.text) | ||
| 737 | R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); | ||
| 738 | svc_mem_info = mem_info.GetSvcMemoryInfo(); | ||
| 739 | cur_addr = svc_mem_info.base_address + svc_mem_info.size; | ||
| 740 | if (svc_mem_info.state != Kernel::Svc::MemoryState::Code || | ||
| 741 | svc_mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) { | ||
| 742 | return cur_addr - 1; | ||
| 743 | } | ||
| 744 | |||
| 745 | // Expect: r-- Code (.rodata) | ||
| 746 | R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); | ||
| 747 | svc_mem_info = mem_info.GetSvcMemoryInfo(); | ||
| 748 | cur_addr = svc_mem_info.base_address + svc_mem_info.size; | ||
| 749 | if (svc_mem_info.state != Kernel::Svc::MemoryState::Code || | ||
| 750 | svc_mem_info.permission != Kernel::Svc::MemoryPermission::Read) { | ||
| 751 | return cur_addr - 1; | ||
| 752 | } | ||
| 753 | |||
| 754 | // Expect: rw- CodeData (.data) | ||
| 755 | R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr)); | ||
| 756 | svc_mem_info = mem_info.GetSvcMemoryInfo(); | ||
| 757 | cur_addr = svc_mem_info.base_address + svc_mem_info.size; | ||
| 758 | return cur_addr - 1; | ||
| 759 | } | ||
| 760 | |||
| 761 | void GDBStub::HandleRcmd(const std::vector<u8>& command) { | 833 | void GDBStub::HandleRcmd(const std::vector<u8>& command) { |
| 762 | std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; | 834 | std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; |
| 763 | std::string reply; | 835 | std::string reply; |
| @@ -784,8 +856,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { | |||
| 784 | reply = "Fastmem is not enabled.\n"; | 856 | reply = "Fastmem is not enabled.\n"; |
| 785 | } | 857 | } |
| 786 | } else if (command_str == "get info") { | 858 | } else if (command_str == "get info") { |
| 787 | Loader::AppLoader::Modules modules; | 859 | auto modules = FindModules(system); |
| 788 | system.GetAppLoader().ReadNSOModules(modules); | ||
| 789 | 860 | ||
| 790 | reply = fmt::format("Process: {:#x} ({})\n" | 861 | reply = fmt::format("Process: {:#x} ({})\n" |
| 791 | "Program Id: {:#018x}\n", | 862 | "Program Id: {:#018x}\n", |