diff options
Diffstat (limited to 'src/core/debugger/gdbstub.cpp')
| -rw-r--r-- | src/core/debugger/gdbstub.cpp | 233 |
1 files changed, 159 insertions, 74 deletions
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 6f5f5156b..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,32 +830,6 @@ static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::Memory | |||
| 727 | } | 830 | } |
| 728 | } | 831 | } |
| 729 | 832 | ||
| 730 | static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) { | ||
| 731 | Kernel::Svc::MemoryInfo mem_info; | ||
| 732 | VAddr cur_addr{base}; | ||
| 733 | |||
| 734 | // Expect: r-x Code (.text) | ||
| 735 | mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); | ||
| 736 | cur_addr = mem_info.base_address + mem_info.size; | ||
| 737 | if (mem_info.state != Kernel::Svc::MemoryState::Code || | ||
| 738 | mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) { | ||
| 739 | return cur_addr - 1; | ||
| 740 | } | ||
| 741 | |||
| 742 | // Expect: r-- Code (.rodata) | ||
| 743 | mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); | ||
| 744 | cur_addr = mem_info.base_address + mem_info.size; | ||
| 745 | if (mem_info.state != Kernel::Svc::MemoryState::Code || | ||
| 746 | mem_info.permission != Kernel::Svc::MemoryPermission::Read) { | ||
| 747 | return cur_addr - 1; | ||
| 748 | } | ||
| 749 | |||
| 750 | // Expect: rw- CodeData (.data) | ||
| 751 | mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); | ||
| 752 | cur_addr = mem_info.base_address + mem_info.size; | ||
| 753 | return cur_addr - 1; | ||
| 754 | } | ||
| 755 | |||
| 756 | void GDBStub::HandleRcmd(const std::vector<u8>& command) { | 833 | void GDBStub::HandleRcmd(const std::vector<u8>& command) { |
| 757 | 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()}; |
| 758 | std::string reply; | 835 | std::string reply; |
| @@ -767,7 +844,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { | |||
| 767 | 844 | ||
| 768 | if (command_str == "get fastmem") { | 845 | if (command_str == "get fastmem") { |
| 769 | if (Settings::IsFastmemEnabled()) { | 846 | if (Settings::IsFastmemEnabled()) { |
| 770 | const auto& impl = page_table.PageTableImpl(); | 847 | const auto& impl = page_table.GetImpl(); |
| 771 | const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena); | 848 | const auto region = reinterpret_cast<uintptr_t>(impl.fastmem_arena); |
| 772 | const auto region_bits = impl.current_address_space_width_in_bits; | 849 | const auto region_bits = impl.current_address_space_width_in_bits; |
| 773 | const auto region_size = 1ULL << region_bits; | 850 | const auto region_size = 1ULL << region_bits; |
| @@ -779,26 +856,27 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { | |||
| 779 | reply = "Fastmem is not enabled.\n"; | 856 | reply = "Fastmem is not enabled.\n"; |
| 780 | } | 857 | } |
| 781 | } else if (command_str == "get info") { | 858 | } else if (command_str == "get info") { |
| 782 | Loader::AppLoader::Modules modules; | 859 | auto modules = FindModules(system); |
| 783 | system.GetAppLoader().ReadNSOModules(modules); | ||
| 784 | 860 | ||
| 785 | reply = fmt::format("Process: {:#x} ({})\n" | 861 | reply = fmt::format("Process: {:#x} ({})\n" |
| 786 | "Program Id: {:#018x}\n", | 862 | "Program Id: {:#018x}\n", |
| 787 | process->GetProcessId(), process->GetName(), process->GetProgramId()); | 863 | process->GetProcessId(), process->GetName(), process->GetProgramId()); |
| 788 | reply += fmt::format("Layout:\n" | 864 | reply += fmt::format( |
| 789 | " Alias: {:#012x} - {:#012x}\n" | 865 | "Layout:\n" |
| 790 | " Heap: {:#012x} - {:#012x}\n" | 866 | " Alias: {:#012x} - {:#012x}\n" |
| 791 | " Aslr: {:#012x} - {:#012x}\n" | 867 | " Heap: {:#012x} - {:#012x}\n" |
| 792 | " Stack: {:#012x} - {:#012x}\n" | 868 | " Aslr: {:#012x} - {:#012x}\n" |
| 793 | "Modules:\n", | 869 | " Stack: {:#012x} - {:#012x}\n" |
| 794 | GetInteger(page_table.GetAliasRegionStart()), | 870 | "Modules:\n", |
| 795 | GetInteger(page_table.GetAliasRegionEnd()), | 871 | GetInteger(page_table.GetAliasRegionStart()), |
| 796 | GetInteger(page_table.GetHeapRegionStart()), | 872 | GetInteger(page_table.GetAliasRegionStart()) + page_table.GetAliasRegionSize() - 1, |
| 797 | GetInteger(page_table.GetHeapRegionEnd()), | 873 | GetInteger(page_table.GetHeapRegionStart()), |
| 798 | GetInteger(page_table.GetAliasCodeRegionStart()), | 874 | GetInteger(page_table.GetHeapRegionStart()) + page_table.GetHeapRegionSize() - 1, |
| 799 | GetInteger(page_table.GetAliasCodeRegionEnd()), | 875 | GetInteger(page_table.GetAliasCodeRegionStart()), |
| 800 | GetInteger(page_table.GetStackRegionStart()), | 876 | GetInteger(page_table.GetAliasCodeRegionStart()) + page_table.GetAliasCodeRegionSize() - |
| 801 | GetInteger(page_table.GetStackRegionEnd())); | 877 | 1, |
| 878 | GetInteger(page_table.GetStackRegionStart()), | ||
| 879 | GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1); | ||
| 802 | 880 | ||
| 803 | for (const auto& [vaddr, name] : modules) { | 881 | for (const auto& [vaddr, name] : modules) { |
| 804 | reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, | 882 | reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, |
| @@ -811,27 +889,34 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { | |||
| 811 | while (true) { | 889 | while (true) { |
| 812 | using MemoryAttribute = Kernel::Svc::MemoryAttribute; | 890 | using MemoryAttribute = Kernel::Svc::MemoryAttribute; |
| 813 | 891 | ||
| 814 | auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); | 892 | Kernel::KMemoryInfo mem_info{}; |
| 815 | 893 | Kernel::Svc::PageInfo page_info{}; | |
| 816 | if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible || | 894 | R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), |
| 817 | mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) { | 895 | cur_addr)); |
| 818 | const char* state = GetMemoryStateName(mem_info.state); | 896 | auto svc_mem_info = mem_info.GetSvcMemoryInfo(); |
| 819 | const char* perm = GetMemoryPermissionString(mem_info); | 897 | |
| 820 | 898 | if (svc_mem_info.state != Kernel::Svc::MemoryState::Inaccessible || | |
| 821 | const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-'; | 899 | svc_mem_info.base_address + svc_mem_info.size - 1 != |
| 822 | const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-'; | 900 | std::numeric_limits<u64>::max()) { |
| 823 | const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-'; | 901 | const char* state = GetMemoryStateName(svc_mem_info.state); |
| 824 | const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-'; | 902 | const char* perm = GetMemoryPermissionString(svc_mem_info); |
| 903 | |||
| 904 | const char l = True(svc_mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-'; | ||
| 905 | const char i = | ||
| 906 | True(svc_mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-'; | ||
| 907 | const char d = | ||
| 908 | True(svc_mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-'; | ||
| 909 | const char u = True(svc_mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-'; | ||
| 825 | const char p = | 910 | const char p = |
| 826 | True(mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-'; | 911 | True(svc_mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-'; |
| 827 | 912 | ||
| 828 | reply += fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", | 913 | reply += fmt::format( |
| 829 | mem_info.base_address, | 914 | " {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n", svc_mem_info.base_address, |
| 830 | mem_info.base_address + mem_info.size - 1, perm, state, l, i, | 915 | svc_mem_info.base_address + svc_mem_info.size - 1, perm, state, l, i, d, u, p, |
| 831 | d, u, p, mem_info.ipc_count, mem_info.device_count); | 916 | svc_mem_info.ipc_count, svc_mem_info.device_count); |
| 832 | } | 917 | } |
| 833 | 918 | ||
| 834 | const uintptr_t next_address = mem_info.base_address + mem_info.size; | 919 | const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size; |
| 835 | if (next_address <= cur_addr) { | 920 | if (next_address <= cur_addr) { |
| 836 | break; | 921 | break; |
| 837 | } | 922 | } |