diff --git a/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp b/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp index cb0de2781..4cb6fed4b 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp @@ -40,6 +40,12 @@ /* of the right side, and so this does not actually cost any space. */ #define MESOSPHERE_ENABLE_DEVIRTUALIZED_DYNAMIC_CAST +/* NOTE: This enables usage of KDebug handles as parameter for svc::GetInfo */ +/* calls which require a process parameter. This enables a debugger to obtain */ +/* address space/layout information, for example. However, it changes abi, and so */ +/* this define allows toggling the extension. */ +#define MESOSPHERE_ENABLE_GET_INFO_OF_DEBUG_PROCESS + /* NOTE: This uses currently-reserved bits inside the MapRange capability */ /* in order to support large physical addresses (40-bit instead of 36). */ /* This is toggleable in order to disable it if N ever uses those bits. */ diff --git a/libraries/libmesosphere/source/svc/kern_svc_info.cpp b/libraries/libmesosphere/source/svc/kern_svc_info.cpp index 60edea1e3..33a2bb115 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_info.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_info.cpp @@ -38,6 +38,80 @@ namespace ams::kern::svc { return ResultSuccess(); } + Result GetInfoImpl(u64 *out, ams::svc::InfoType info_type, KProcess *process) { + switch (info_type) { + case ams::svc::InfoType_CoreMask: + *out = process->GetCoreMask(); + break; + case ams::svc::InfoType_PriorityMask: + *out = process->GetPriorityMask(); + break; + case ams::svc::InfoType_AliasRegionAddress: + *out = GetInteger(process->GetPageTable().GetAliasRegionStart()); + break; + case ams::svc::InfoType_AliasRegionSize: + *out = process->GetPageTable().GetAliasRegionSize(); + break; + case ams::svc::InfoType_HeapRegionAddress: + *out = GetInteger(process->GetPageTable().GetHeapRegionStart()); + break; + case ams::svc::InfoType_HeapRegionSize: + *out = process->GetPageTable().GetHeapRegionSize(); + break; + case ams::svc::InfoType_TotalMemorySize: + *out = process->GetTotalUserPhysicalMemorySize(); + break; + case ams::svc::InfoType_UsedMemorySize: + *out = process->GetUsedUserPhysicalMemorySize(); + break; + case ams::svc::InfoType_AslrRegionAddress: + *out = GetInteger(process->GetPageTable().GetAliasCodeRegionStart()); + break; + case ams::svc::InfoType_AslrRegionSize: + *out = process->GetPageTable().GetAliasCodeRegionSize(); + break; + case ams::svc::InfoType_StackRegionAddress: + *out = GetInteger(process->GetPageTable().GetStackRegionStart()); + break; + case ams::svc::InfoType_StackRegionSize: + *out = process->GetPageTable().GetStackRegionSize(); + break; + case ams::svc::InfoType_SystemResourceSizeTotal: + *out = process->GetTotalSystemResourceSize(); + break; + case ams::svc::InfoType_SystemResourceSizeUsed: + *out = process->GetUsedSystemResourceSize(); + break; + case ams::svc::InfoType_ProgramId: + *out = process->GetProgramId(); + break; + case ams::svc::InfoType_UserExceptionContextAddress: + *out = GetInteger(process->GetProcessLocalRegionAddress()); + break; + case ams::svc::InfoType_TotalNonSystemMemorySize: + *out = process->GetTotalNonSystemUserPhysicalMemorySize(); + break; + case ams::svc::InfoType_UsedNonSystemMemorySize: + *out = process->GetUsedNonSystemUserPhysicalMemorySize(); + break; + case ams::svc::InfoType_IsApplication: + *out = process->IsApplication(); + break; + case ams::svc::InfoType_FreeThreadCount: + if (KResourceLimit *resource_limit = process->GetResourceLimit(); resource_limit != nullptr) { + const auto current_value = resource_limit->GetCurrentValue(ams::svc::LimitableResource_ThreadCountMax); + const auto limit_value = resource_limit->GetLimitValue(ams::svc::LimitableResource_ThreadCountMax); + *out = limit_value - current_value; + } else { + *out = 0; + } + break; + MESOSPHERE_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + Result GetInfo(u64 *out, ams::svc::InfoType info_type, ams::svc::Handle handle, u64 info_subtype) { switch (info_type) { case ams::svc::InfoType_CoreMask: @@ -66,77 +140,34 @@ namespace ams::kern::svc { /* Get the process from its handle. */ KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject(handle); + + #if defined(MESOSPHERE_ENABLE_GET_INFO_OF_DEBUG_PROCESS) + /* If we the process is valid, use it. */ + if (process.IsNotNull()) { + return GetInfoImpl(out, info_type, process.GetPointerUnsafe()); + } + + /* Otherwise, as a mesosphere extension check if we were passed a usable KDebug. */ + KScopedAutoObject debug = GetCurrentProcess().GetHandleTable().GetObject(handle); + R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle()); + + /* Get the process from the debug object. */ + /* TODO: ResultInvalidHandle()? */ + R_UNLESS(debug->IsAttached(), svc::ResultProcessTerminated()); + R_UNLESS(debug->OpenProcess(), svc::ResultProcessTerminated()); + + /* Close the process when we're done. */ + ON_SCOPE_EXIT { debug->CloseProcess(); }; + + /* Return the info. */ + return GetInfoImpl(out, info_type, debug->GetProcessUnsafe()); + #else + /* Verify that the process is valid. */ R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle()); - switch (info_type) { - case ams::svc::InfoType_CoreMask: - *out = process->GetCoreMask(); - break; - case ams::svc::InfoType_PriorityMask: - *out = process->GetPriorityMask(); - break; - case ams::svc::InfoType_AliasRegionAddress: - *out = GetInteger(process->GetPageTable().GetAliasRegionStart()); - break; - case ams::svc::InfoType_AliasRegionSize: - *out = process->GetPageTable().GetAliasRegionSize(); - break; - case ams::svc::InfoType_HeapRegionAddress: - *out = GetInteger(process->GetPageTable().GetHeapRegionStart()); - break; - case ams::svc::InfoType_HeapRegionSize: - *out = process->GetPageTable().GetHeapRegionSize(); - break; - case ams::svc::InfoType_TotalMemorySize: - *out = process->GetTotalUserPhysicalMemorySize(); - break; - case ams::svc::InfoType_UsedMemorySize: - *out = process->GetUsedUserPhysicalMemorySize(); - break; - case ams::svc::InfoType_AslrRegionAddress: - *out = GetInteger(process->GetPageTable().GetAliasCodeRegionStart()); - break; - case ams::svc::InfoType_AslrRegionSize: - *out = process->GetPageTable().GetAliasCodeRegionSize(); - break; - case ams::svc::InfoType_StackRegionAddress: - *out = GetInteger(process->GetPageTable().GetStackRegionStart()); - break; - case ams::svc::InfoType_StackRegionSize: - *out = process->GetPageTable().GetStackRegionSize(); - break; - case ams::svc::InfoType_SystemResourceSizeTotal: - *out = process->GetTotalSystemResourceSize(); - break; - case ams::svc::InfoType_SystemResourceSizeUsed: - *out = process->GetUsedSystemResourceSize(); - break; - case ams::svc::InfoType_ProgramId: - *out = process->GetProgramId(); - break; - case ams::svc::InfoType_UserExceptionContextAddress: - *out = GetInteger(process->GetProcessLocalRegionAddress()); - break; - case ams::svc::InfoType_TotalNonSystemMemorySize: - *out = process->GetTotalNonSystemUserPhysicalMemorySize(); - break; - case ams::svc::InfoType_UsedNonSystemMemorySize: - *out = process->GetUsedNonSystemUserPhysicalMemorySize(); - break; - case ams::svc::InfoType_IsApplication: - *out = process->IsApplication(); - break; - case ams::svc::InfoType_FreeThreadCount: - if (KResourceLimit *resource_limit = process->GetResourceLimit(); resource_limit != nullptr) { - const auto current_value = resource_limit->GetCurrentValue(ams::svc::LimitableResource_ThreadCountMax); - const auto limit_value = resource_limit->GetLimitValue(ams::svc::LimitableResource_ThreadCountMax); - *out = limit_value - current_value; - } else { - *out = 0; - } - break; - MESOSPHERE_UNREACHABLE_DEFAULT_CASE(); - } + /* Return the relevant info. */ + return GetInfoImpl(out, info_type, process.GetPointerUnsafe()); + #endif } break; case ams::svc::InfoType_DebuggerAttached: diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp index ea73fdedc..036de75ce 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp @@ -47,6 +47,10 @@ namespace ams::dmnt { svc::GetProcessId(std::addressof(pid_value), m_debug_handle); m_process_id = { pid_value }; + + /* Get process info. */ + this->CollectProcessInfo(); + return ResultSuccess(); } @@ -227,6 +231,53 @@ namespace ams::dmnt { return ResultSuccess(); } + void DebugProcess::CollectProcessInfo() { + /* Define helper for getting process info. */ + auto CollectProcessInfoImpl = [&](os::NativeHandle handle) -> Result { + /* Collect all values. */ + R_TRY(svc::GetInfo(std::addressof(m_process_alias_address), svc::InfoType_AliasRegionAddress, handle, 0)); + R_TRY(svc::GetInfo(std::addressof(m_process_alias_size), svc::InfoType_AliasRegionSize, handle, 0)); + R_TRY(svc::GetInfo(std::addressof(m_process_heap_address), svc::InfoType_HeapRegionAddress, handle, 0)); + R_TRY(svc::GetInfo(std::addressof(m_process_heap_size), svc::InfoType_HeapRegionSize, handle, 0)); + R_TRY(svc::GetInfo(std::addressof(m_process_aslr_address), svc::InfoType_AslrRegionAddress, handle, 0)); + R_TRY(svc::GetInfo(std::addressof(m_process_aslr_size), svc::InfoType_AslrRegionSize, handle, 0)); + R_TRY(svc::GetInfo(std::addressof(m_process_stack_address), svc::InfoType_StackRegionAddress, handle, 0)); + R_TRY(svc::GetInfo(std::addressof(m_process_stack_size), svc::InfoType_StackRegionSize, handle, 0)); + + if (m_program_location.program_id == ncm::InvalidProgramId) { + R_TRY(svc::GetInfo(std::addressof(m_program_location.program_id.value), svc::InfoType_ProgramId, handle, 0)); + } + + u64 value; + R_TRY(svc::GetInfo(std::addressof(value), svc::InfoType_IsApplication, handle, 0)); + m_is_application = value != 0; + + return ResultSuccess(); + }; + + /* Get process info/status. */ + os::NativeHandle process_handle; + if (R_FAILED(pm::dmnt::AtmosphereGetProcessInfo(std::addressof(process_handle), std::addressof(m_program_location), std::addressof(m_process_override_status), m_process_id))) { + process_handle = os::InvalidNativeHandle; + m_program_location = { ncm::InvalidProgramId, }; + m_process_override_status = {}; + } + ON_SCOPE_EXIT { os::CloseNativeHandle(process_handle); }; + + /* Try collecting from our debug handle, then the process handle. */ + if (R_FAILED(CollectProcessInfoImpl(m_debug_handle)) && R_FAILED(CollectProcessInfoImpl(process_handle))) { + m_process_alias_address = 0; + m_process_alias_size = 0; + m_process_heap_address = 0; + m_process_heap_size = 0; + m_process_aslr_address = 0; + m_process_aslr_size = 0; + m_process_stack_address = 0; + m_process_stack_size = 0; + m_is_application = false; + } + } + Result DebugProcess::GetThreadContext(svc::ThreadContext *out, u64 thread_id, u32 flags) { return svc::GetDebugThreadContext(out, m_debug_handle, thread_id, flags); } diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp index 3c20fda47..d8a698f96 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp @@ -64,6 +64,17 @@ namespace ams::dmnt { ModuleDefinition m_module_definitions[ModuleCountMax]{}; size_t m_module_count{}; size_t m_main_module{}; + u64 m_process_alias_address{}; + u64 m_process_alias_size{}; + u64 m_process_heap_address{}; + u64 m_process_heap_size{}; + u64 m_process_aslr_address{}; + u64 m_process_aslr_size{}; + u64 m_process_stack_address{}; + u64 m_process_stack_size{}; + ncm::ProgramLocation m_program_location{}; + cfg::OverrideStatus m_process_override_status{}; + bool m_is_application{false}; public: DebugProcess() : m_software_breakpoints(this), m_hardware_breakpoints(this), m_hardware_watchpoints(this), m_step_breakpoints(m_software_breakpoints) { if (svc::IsKernelMesosphere()) { @@ -108,6 +119,20 @@ namespace ams::dmnt { void SetDebugBreaked() { m_status = ProcessStatus_DebugBreak; } + + u64 GetAliasRegionAddress() const { return m_process_alias_address; } + u64 GetAliasRegionSize() const { return m_process_alias_size; } + u64 GetHeapRegionAddress() const { return m_process_heap_address; } + u64 GetHeapRegionSize() const { return m_process_heap_size; } + u64 GetAslrRegionAddress() const { return m_process_aslr_address; } + u64 GetAslrRegionSize() const { return m_process_aslr_size; } + u64 GetStackRegionAddress() const { return m_process_stack_address; } + u64 GetStackRegionSize() const { return m_process_stack_size; } + + const ncm::ProgramLocation &GetProgramLocation() const { return m_program_location; } + const cfg::OverrideStatus &GetOverrideStatus() const { return m_process_override_status; } + + bool IsApplication() const { return m_is_application; } public: Result Attach(os::ProcessId process_id, bool start_process = false); void Detach(); @@ -152,6 +177,8 @@ namespace ams::dmnt { private: Result Start(); + void CollectProcessInfo(); + s32 ThreadCreate(u64 thread_id); void ThreadExit(u64 thread_id); }; diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp index 7a0246b8d..80743edbb 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp @@ -1209,6 +1209,11 @@ namespace ams::dmnt { /* Send packet. */ this->SendPacket(std::addressof(do_break), reply_buffer); } + + /* If we should, break the process. */ + if (do_break) { + m_debug_process.Break(); + } } } @@ -1855,7 +1860,9 @@ namespace ams::dmnt { void GdbServerImpl::qRcmd() { /* Decode the command. */ - HexToMemory(m_buffer, m_receive_packet, std::strlen(m_receive_packet)); + const auto packet_len = std::strlen(m_receive_packet); + HexToMemory(m_buffer, m_receive_packet, packet_len); + m_buffer[packet_len / 2] = '\x00'; /* Convert our response to hex, on complete. */ ON_SCOPE_EXIT { @@ -1866,26 +1873,39 @@ namespace ams::dmnt { /* Parse the command. */ char *command = reinterpret_cast(m_buffer); if (ParsePrefix(command, "help")) { - SetReply(m_buffer, "get base\n" + SetReply(m_buffer, "get info\n" "wait application\n" "wait {program id}\n" "wait homebrew\n"); - } else if (ParsePrefix(command, "get base") || ParsePrefix(command, "get modules")) { - SetReply(m_buffer, "Modules:\n"); - + } else if (ParsePrefix(command, "get base") || ParsePrefix(command, "get info") || ParsePrefix(command, "get modules")) { if (!this->HasDebugProcess()) { - AppendReply(m_buffer, " [Not Attached]\n"); + SetReply(m_buffer, "Not attached.\n"); return; } + SetReply(m_buffer, "Process: 0x%lx (%s)\n" + "Program Id: 0x%016lx\n" + "Application: %d\n" + "Hbl: %d\n" + "Layout:\n", m_process_id.value, m_debug_process.GetProcessName(), m_debug_process.GetProgramLocation().program_id.value, m_debug_process.IsApplication(), m_debug_process.GetOverrideStatus().IsHbl()); + + AppendReply(m_buffer, " Alias: 0x%010lx - 0x%010lx\n" + " Heap: 0x%010lx - 0x%010lx\n" + " Aslr: 0x%010lx - 0x%010lx\n" + " Stack: 0x%010lx - 0x%010lx\n" + "Modules:\n", m_debug_process.GetAliasRegionAddress(), m_debug_process.GetAliasRegionAddress() + m_debug_process.GetAliasRegionSize() - 1, + m_debug_process.GetHeapRegionAddress(), m_debug_process.GetHeapRegionAddress() + m_debug_process.GetHeapRegionSize() - 1, + m_debug_process.GetAslrRegionAddress(), m_debug_process.GetAslrRegionAddress() + m_debug_process.GetAslrRegionSize() - 1, + m_debug_process.GetStackRegionAddress(), m_debug_process.GetStackRegionAddress() + m_debug_process.GetStackRegionSize() - 1); + /* Get the module list. */ for (size_t i = 0; i < m_debug_process.GetModuleCount(); ++i) { const char *module_name = m_debug_process.GetModuleName(i); const auto name_len = std::strlen(module_name); if (name_len < 5 || (std::strcmp(module_name + name_len - 4, ".elf") != 0 && std::strcmp(module_name + name_len - 4, ".nss") != 0)) { - AppendReply(m_buffer, " %p - %p %s.elf\n", reinterpret_cast(m_debug_process.GetModuleBaseAddress(i)), reinterpret_cast(m_debug_process.GetModuleBaseAddress(i) + m_debug_process.GetModuleSize(i) - 1), module_name); + AppendReply(m_buffer, " 0x%010lx - 0x%010lx %s.elf\n", m_debug_process.GetModuleBaseAddress(i), m_debug_process.GetModuleBaseAddress(i) + m_debug_process.GetModuleSize(i) - 1, module_name); } else { - AppendReply(m_buffer, " %p - %p %s\n", reinterpret_cast(m_debug_process.GetModuleBaseAddress(i)), reinterpret_cast(m_debug_process.GetModuleBaseAddress(i) + m_debug_process.GetModuleSize(i) - 1), module_name); + AppendReply(m_buffer, " 0x%010lx - 0x%010lx %s\n", m_debug_process.GetModuleBaseAddress(i), m_debug_process.GetModuleBaseAddress(i) + m_debug_process.GetModuleSize(i) - 1, module_name); } } } else if (ParsePrefix(command, "wait application") || ParsePrefix(command, "wait app")) { @@ -1916,7 +1936,8 @@ namespace ams::dmnt { SetReply(m_buffer, "[TODO] wait for program id 0x%lx\n", program_id); } else { - SetReply(m_buffer, "Unknown command `%s`\n", command); + SetReply(m_reply_packet, "Unknown command `%s`\n", command); + std::memcpy(m_buffer, m_reply_packet, std::strlen(m_reply_packet)); } }