From e3d2af6b3fc7e04fb489672e857a7abfac657d20 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 1 Nov 2021 21:43:29 -0700 Subject: [PATCH] dmnt2: fix module name detection, add auto-break on hb nro launch --- .../creport/source/creport_modules.cpp | 10 ++--- .../dmnt.gen2/source/dmnt2_debug_process.cpp | 8 ++-- .../source/dmnt2_gdb_server_impl.cpp | 45 ++++++++++++++++--- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/stratosphere/creport/source/creport_modules.cpp b/stratosphere/creport/source/creport_modules.cpp index c7ceecdd9..84d87171b 100644 --- a/stratosphere/creport/source/creport_modules.cpp +++ b/stratosphere/creport/source/creport_modules.cpp @@ -27,7 +27,7 @@ namespace ams::creport { struct ModulePath { u32 zero; - u32 path_length; + s32 path_length; char path[ModulePathLengthMax]; }; static_assert(sizeof(ModulePath) == 0x208, "ModulePath definition!"); @@ -210,7 +210,7 @@ namespace ams::creport { } /* Also validate that we're looking at a valid name. */ - if (rodata_start.module_path.zero != 0 || rodata_start.module_path.path_length != strnlen(rodata_start.module_path.path, sizeof(rodata_start.module_path.path))) { + if (rodata_start.module_path.zero != 0 || rodata_start.module_path.path_length <= 0)) { return; } } @@ -219,7 +219,7 @@ namespace ams::creport { /* Start after last slash in path. */ const char *path = rodata_start.module_path.path; int ofs; - for (ofs = rodata_start.module_path.path_length; ofs >= 0; ofs--) { + for (ofs = std::min(rodata_start.module_path.path_length, sizeof(rodata_start.module_path.path)); ofs >= 0; ofs--) { if (path[ofs] == '/' || path[ofs] == '\\') { break; } @@ -227,8 +227,8 @@ namespace ams::creport { ofs++; /* Copy name to output. */ - const size_t name_size = std::min(ModuleNameLengthMax, sizeof(rodata_start.module_path.path) - ofs); - std::strncpy(out_name, path + ofs, name_size); + const size_t name_size = std::min(ModuleNameLengthMax, std::min(rodata_start.module_path.path_length, sizeof(rodata_start.module_path.path)) - ofs); + std::memcpy(out_name, path + ofs, name_size); out_name[ModuleNameLengthMax - 1] = '\x00'; } diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp index cd590efa3..09b28b57f 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp @@ -196,9 +196,11 @@ namespace ams::dmnt { char path[ModuleDefinition::PathLengthMax]; } module_path; if (R_SUCCEEDED(this->ReadMemory(std::addressof(module_path), memory_info.base_address + memory_info.size, sizeof(module_path)))) { - if (module_path.zero == 0 && module_path.path_length == util::Strnlen(module_path.path, sizeof(module_path.path))) { - std::memcpy(module_name, module_path.path, ModuleDefinition::PathLengthMax); + if (module_path.zero == 0 && module_path.path_length > 0) { + std::memcpy(module_name, module_path.path, std::min(ModuleDefinition::PathLengthMax, module_path.path_length)); } + } else { + module_path.path_length = 0; } /* Truncate module name. */ @@ -208,7 +210,7 @@ namespace ams::dmnt { module.SetNameStart(0); /* Ignore leading directories. */ - for (size_t i = 0; i < static_cast(module_path.path_length); ++i) { + for (size_t i = 0; i < std::min(ModuleDefinition::PathLengthMax, module_path.path_length) && module_name[i] != 0; ++i) { if (module_name[i] == '/' || module_name[i] == '\\') { module.SetNameStart(i + 1); } diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp index 0fa255fec..90de1cdc2 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp @@ -947,6 +947,9 @@ namespace ams::dmnt { void GdbServerImpl::ProcessDebugEvents() { AMS_DMNT2_GDB_LOG_DEBUG("Processing debug events for %016lx\n", m_process_id.value); + u64 new_hb_nro_addr = 0; + u32 new_hb_nro_insn = 0; + while (true) { /* Wait for an event to come in. */ const Result wait_result = [&] ALWAYS_INLINE_LAMBDA { @@ -1028,6 +1031,25 @@ namespace ams::dmnt { /* Re-collect the process's modules. */ m_debug_process.CollectModules(); } + + if (m_debug_process.GetOverrideStatus().IsHbl() && reason == svc::BreakReason_PostLoadDll) { + if (R_SUCCEEDED(m_debug_process.ReadMemory(std::addressof(new_hb_nro_insn), info.address, sizeof(new_hb_nro_insn)))) { + const u32 break_insn = SdkBreakPoint; + if (R_SUCCEEDED(m_debug_process.WriteMemory(std::addressof(break_insn), info.address, sizeof(break_insn)))) { + AMS_DMNT2_GDB_LOG_DEBUG("Set automatic break on new homebrew NRO (%lx, %lx)\n", info.address, info.size); + + new_hb_nro_addr = info.address; + } else { + AMS_DMNT2_GDB_LOG_DEBUG("Failed to set automatic break on new homebrew NRO (%lx, %lx)\n", info.address, info.size); + } + } else { + AMS_DMNT2_GDB_LOG_DEBUG("Failed to read first insn on new homebrew NRO (%lx, %lx)\n", info.address, info.size); + } + } + + /* This was just a notification, so we should continue. */ + m_debug_process.Continue(); + continue; } /* Check if we should automatically continue. */ @@ -1074,6 +1096,8 @@ namespace ams::dmnt { const u32 insn = d.info.exception.specific.undefined_instruction.insn; u32 new_insn = 0; + bool is_new_hb_nro = false; + svc::ThreadContext ctx; if (R_SUCCEEDED(m_debug_process.GetThreadContext(std::addressof(ctx), thread_id, svc::ThreadContextFlag_Control))) { bool insn_changed = false; @@ -1115,6 +1139,19 @@ namespace ams::dmnt { { signal = GdbSignal_BreakpointTrap; } + + if (m_debug_process.GetOverrideStatus().IsHbl() && address == new_hb_nro_addr && insn == SdkBreakPoint) { + if (R_SUCCEEDED(m_debug_process.WriteMemory(std::addressof(new_hb_nro_insn), new_hb_nro_addr, sizeof(new_hb_nro_insn)))) { + AMS_DMNT2_GDB_LOG_DEBUG("Did automatic break on new homebrew NRO (%lx)\n", address); + + new_hb_nro_addr = 0; + new_hb_nro_insn = 0; + + is_new_hb_nro = true; + } else { + AMS_DMNT2_GDB_LOG_ERROR("Failed to restore instruction for new homebrew NRO (%lx)!\n", address); + } + } } if (insn_changed) { @@ -1128,7 +1165,7 @@ namespace ams::dmnt { AMS_DMNT2_GDB_LOG_DEBUG("Non-SDK BreakPoint %lx, address=%p, insn=%08x\n", thread_id, reinterpret_cast(address), insn); } - if (signal == GdbSignal_BreakpointTrap) { + if (signal == GdbSignal_BreakpointTrap && !is_new_hb_nro) { SetReply(send_buffer, "T%02Xthread:p%lx.%lx;swbreak:;", static_cast(signal), m_process_id.value, thread_id); reply = true; } @@ -2049,8 +2086,6 @@ namespace ams::dmnt { /* Note that we're attaching. */ SetReply(m_buffer, "Attach to 0x%lx.\n", m_wait_process_id.value); - } else if (ParsePrefix(command, "wait homebrew") || ParsePrefix(command, "wait hb")) { - SetReply(m_buffer, "[TODO] wait for next homebrew\n"); } else if (ParsePrefix(command, "wait ")) { /* Allow optional "0x" prefix. */ ParsePrefix(command, "0x"); @@ -2074,8 +2109,8 @@ namespace ams::dmnt { AppendReply(m_reply_packet, ";qXfer:osdata:read+"); AppendReply(m_reply_packet, ";qXfer:features:read+"); AppendReply(m_reply_packet, ";qXfer:libraries:read+"); - AppendReply(m_reply_packet, ";qXfer:libraries-svr4:read+"); - AppendReply(m_reply_packet, ";augmented-libraries-svr4-read+"); + // TODO: AppendReply(m_reply_packet, ";qXfer:libraries-svr4:read+"); + // TODO: AppendReply(m_reply_packet, ";augmented-libraries-svr4-read+"); AppendReply(m_reply_packet, ";qXfer:threads:read+"); AppendReply(m_reply_packet, ";qXfer:exec-file:read+"); AppendReply(m_reply_packet, ";swbreak+");