diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp index 1b0d8f9f2..31efdb5cb 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.cpp @@ -155,6 +155,9 @@ namespace ams::dmnt { } Result DebugProcess::CollectModules() { + /* Reset our module count. */ + m_module_count = 0; + /* Traverse the address space, looking for modules. */ uintptr_t address = 0; diff --git a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp index 2dc29a87f..0c5acac22 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_debug_process.hpp @@ -137,13 +137,13 @@ namespace ams::dmnt { Result GetProcessDebugEvent(svc::DebugEventInfo *out); void GetBranchTarget(svc::ThreadContext &ctx, u64 thread_id, u64 ¤t_pc, u64 &target); + + Result CollectModules(); private: Result Start(); s32 ThreadCreate(u64 thread_id); void ThreadExit(u64 thread_id); - - Result CollectModules(); }; } \ No newline at end of file diff --git a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp index 6596dcaa9..f01b64e4c 100644 --- a/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp +++ b/stratosphere/dmnt.gen2/source/dmnt2_gdb_server_impl.cpp @@ -638,6 +638,42 @@ namespace ams::dmnt { reply = true; } break; + case svc::DebugException_UserBreak: + { + const uintptr_t address = d.info.exception.address; + const auto &info = d.info.exception.specific.user_break; + AMS_DMNT2_GDB_LOG_DEBUG("UserBreak %lx, addr=%lx, reason=%x, data=0x%lx, size=0x%lx\n", thread_id, address, info.break_reason, info.address, info.size); + + /* Check reason. */ + /* TODO: libnx/Nintendo provide addresses in different ways, but we could optimize to avoid iterating all memory repeatedly. */ + if ((info.break_reason & svc::BreakReason_NotificationOnlyFlag) != 0) { + const auto reason = info.break_reason & ~svc::BreakReason_NotificationOnlyFlag; + if (reason == svc::BreakReason_PostLoadDll || reason == svc::BreakReason_PostUnloadDll) { + /* Re-collect the process's modules. */ + m_debug_process.CollectModules(); + } + } + + /* Check if we should automatically continue. */ + svc::ThreadContext ctx; + if (R_SUCCEEDED(m_debug_process.GetThreadContext(std::addressof(ctx), thread_id, svc::ThreadContextFlag_Control))) { + u32 insn = 0; + if (R_SUCCEEDED(m_debug_process.ReadMemory(std::addressof(insn), ctx.pc, sizeof(insn)))) { + constexpr u32 Aarch64SvcBreakValue = 0xD4000001 | (svc::SvcId_Break << 5); + constexpr u32 Aarch32SvcBreakValue = 0xEF000000 | (svc::SvcId_Break); + bool is_svc_break = m_debug_process.Is64Bit() ? (insn == Aarch64SvcBreakValue) : (insn == Aarch32SvcBreakValue); + + if (!is_svc_break) { + AMS_DMNT2_GDB_LOG_ERROR("UserBreak from non-SvcBreak (%08x)\n", insn); + m_debug_process.Continue(); + continue; + } + } + } + + signal = GdbSignal_BreakpointTrap; + } + break; case svc::DebugException_DebuggerBreak: { AMS_DMNT2_GDB_LOG_DEBUG("DebuggerBreak %lx, last=%lx\n", thread_id, m_debug_process.GetLastThreadId());