kern/dmnt2: allow retrieval of process info via extension

This also fixes ctrl-c break in gdbstub, and fixes crash on unknown monitor cmd.
This commit is contained in:
Michael Scire 2021-11-01 12:05:47 -07:00
parent 9011384dbe
commit d2b024f19f
5 changed files with 214 additions and 78 deletions

View file

@ -40,6 +40,12 @@
/* of the right side, and so this does not actually cost any space. */ /* of the right side, and so this does not actually cost any space. */
#define MESOSPHERE_ENABLE_DEVIRTUALIZED_DYNAMIC_CAST #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 */ /* NOTE: This uses currently-reserved bits inside the MapRange capability */
/* in order to support large physical addresses (40-bit instead of 36). */ /* 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. */ /* This is toggleable in order to disable it if N ever uses those bits. */

View file

@ -38,36 +38,7 @@ namespace ams::kern::svc {
return ResultSuccess(); return ResultSuccess();
} }
Result GetInfo(u64 *out, ams::svc::InfoType info_type, ams::svc::Handle handle, u64 info_subtype) { Result GetInfoImpl(u64 *out, ams::svc::InfoType info_type, KProcess *process) {
switch (info_type) {
case ams::svc::InfoType_CoreMask:
case ams::svc::InfoType_PriorityMask:
case ams::svc::InfoType_AliasRegionAddress:
case ams::svc::InfoType_AliasRegionSize:
case ams::svc::InfoType_HeapRegionAddress:
case ams::svc::InfoType_HeapRegionSize:
case ams::svc::InfoType_TotalMemorySize:
case ams::svc::InfoType_UsedMemorySize:
case ams::svc::InfoType_AslrRegionAddress:
case ams::svc::InfoType_AslrRegionSize:
case ams::svc::InfoType_StackRegionAddress:
case ams::svc::InfoType_StackRegionSize:
case ams::svc::InfoType_SystemResourceSizeTotal:
case ams::svc::InfoType_SystemResourceSizeUsed:
case ams::svc::InfoType_ProgramId:
case ams::svc::InfoType_UserExceptionContextAddress:
case ams::svc::InfoType_TotalNonSystemMemorySize:
case ams::svc::InfoType_UsedNonSystemMemorySize:
case ams::svc::InfoType_IsApplication:
case ams::svc::InfoType_FreeThreadCount:
{
/* These info types don't support non-zero subtypes. */
R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination());
/* Get the process from its handle. */
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(handle);
R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle());
switch (info_type) { switch (info_type) {
case ams::svc::InfoType_CoreMask: case ams::svc::InfoType_CoreMask:
*out = process->GetCoreMask(); *out = process->GetCoreMask();
@ -137,6 +108,66 @@ namespace ams::kern::svc {
break; break;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE(); 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:
case ams::svc::InfoType_PriorityMask:
case ams::svc::InfoType_AliasRegionAddress:
case ams::svc::InfoType_AliasRegionSize:
case ams::svc::InfoType_HeapRegionAddress:
case ams::svc::InfoType_HeapRegionSize:
case ams::svc::InfoType_TotalMemorySize:
case ams::svc::InfoType_UsedMemorySize:
case ams::svc::InfoType_AslrRegionAddress:
case ams::svc::InfoType_AslrRegionSize:
case ams::svc::InfoType_StackRegionAddress:
case ams::svc::InfoType_StackRegionSize:
case ams::svc::InfoType_SystemResourceSizeTotal:
case ams::svc::InfoType_SystemResourceSizeUsed:
case ams::svc::InfoType_ProgramId:
case ams::svc::InfoType_UserExceptionContextAddress:
case ams::svc::InfoType_TotalNonSystemMemorySize:
case ams::svc::InfoType_UsedNonSystemMemorySize:
case ams::svc::InfoType_IsApplication:
case ams::svc::InfoType_FreeThreadCount:
{
/* These info types don't support non-zero subtypes. */
R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination());
/* Get the process from its handle. */
KScopedAutoObject process = GetCurrentProcess().GetHandleTable().GetObject<KProcess>(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<KDebug>(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());
/* Return the relevant info. */
return GetInfoImpl(out, info_type, process.GetPointerUnsafe());
#endif
} }
break; break;
case ams::svc::InfoType_DebuggerAttached: case ams::svc::InfoType_DebuggerAttached:

View file

@ -47,6 +47,10 @@ namespace ams::dmnt {
svc::GetProcessId(std::addressof(pid_value), m_debug_handle); svc::GetProcessId(std::addressof(pid_value), m_debug_handle);
m_process_id = { pid_value }; m_process_id = { pid_value };
/* Get process info. */
this->CollectProcessInfo();
return ResultSuccess(); return ResultSuccess();
} }
@ -227,6 +231,53 @@ namespace ams::dmnt {
return ResultSuccess(); 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) { Result DebugProcess::GetThreadContext(svc::ThreadContext *out, u64 thread_id, u32 flags) {
return svc::GetDebugThreadContext(out, m_debug_handle, thread_id, flags); return svc::GetDebugThreadContext(out, m_debug_handle, thread_id, flags);
} }

View file

@ -64,6 +64,17 @@ namespace ams::dmnt {
ModuleDefinition m_module_definitions[ModuleCountMax]{}; ModuleDefinition m_module_definitions[ModuleCountMax]{};
size_t m_module_count{}; size_t m_module_count{};
size_t m_main_module{}; 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: public:
DebugProcess() : m_software_breakpoints(this), m_hardware_breakpoints(this), m_hardware_watchpoints(this), m_step_breakpoints(m_software_breakpoints) { DebugProcess() : m_software_breakpoints(this), m_hardware_breakpoints(this), m_hardware_watchpoints(this), m_step_breakpoints(m_software_breakpoints) {
if (svc::IsKernelMesosphere()) { if (svc::IsKernelMesosphere()) {
@ -108,6 +119,20 @@ namespace ams::dmnt {
void SetDebugBreaked() { void SetDebugBreaked() {
m_status = ProcessStatus_DebugBreak; 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: public:
Result Attach(os::ProcessId process_id, bool start_process = false); Result Attach(os::ProcessId process_id, bool start_process = false);
void Detach(); void Detach();
@ -152,6 +177,8 @@ namespace ams::dmnt {
private: private:
Result Start(); Result Start();
void CollectProcessInfo();
s32 ThreadCreate(u64 thread_id); s32 ThreadCreate(u64 thread_id);
void ThreadExit(u64 thread_id); void ThreadExit(u64 thread_id);
}; };

View file

@ -1209,6 +1209,11 @@ namespace ams::dmnt {
/* Send packet. */ /* Send packet. */
this->SendPacket(std::addressof(do_break), reply_buffer); 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() { void GdbServerImpl::qRcmd() {
/* Decode the command. */ /* 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. */ /* Convert our response to hex, on complete. */
ON_SCOPE_EXIT { ON_SCOPE_EXIT {
@ -1866,26 +1873,39 @@ namespace ams::dmnt {
/* Parse the command. */ /* Parse the command. */
char *command = reinterpret_cast<char *>(m_buffer); char *command = reinterpret_cast<char *>(m_buffer);
if (ParsePrefix(command, "help")) { if (ParsePrefix(command, "help")) {
SetReply(m_buffer, "get base\n" SetReply(m_buffer, "get info\n"
"wait application\n" "wait application\n"
"wait {program id}\n" "wait {program id}\n"
"wait homebrew\n"); "wait homebrew\n");
} else if (ParsePrefix(command, "get base") || ParsePrefix(command, "get modules")) { } else if (ParsePrefix(command, "get base") || ParsePrefix(command, "get info") || ParsePrefix(command, "get modules")) {
SetReply(m_buffer, "Modules:\n");
if (!this->HasDebugProcess()) { if (!this->HasDebugProcess()) {
AppendReply(m_buffer, " [Not Attached]\n"); SetReply(m_buffer, "Not attached.\n");
return; 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. */ /* Get the module list. */
for (size_t i = 0; i < m_debug_process.GetModuleCount(); ++i) { for (size_t i = 0; i < m_debug_process.GetModuleCount(); ++i) {
const char *module_name = m_debug_process.GetModuleName(i); const char *module_name = m_debug_process.GetModuleName(i);
const auto name_len = std::strlen(module_name); 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)) { 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<void *>(m_debug_process.GetModuleBaseAddress(i)), reinterpret_cast<void *>(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 { } else {
AppendReply(m_buffer, " %p - %p %s\n", reinterpret_cast<void *>(m_debug_process.GetModuleBaseAddress(i)), reinterpret_cast<void *>(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")) { } 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); SetReply(m_buffer, "[TODO] wait for program id 0x%lx\n", program_id);
} else { } 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));
} }
} }