diff --git a/sysmodules/rosalina/include/gdb.h b/sysmodules/rosalina/include/gdb.h index 4b92f9d..731d175 100644 --- a/sysmodules/rosalina/include/gdb.h +++ b/sysmodules/rosalina/include/gdb.h @@ -72,7 +72,7 @@ typedef enum GDBState GDB_STATE_CONNECTED, GDB_STATE_NOACK_SENT, GDB_STATE_NOACK, - GDB_STATE_CLOSING + GDB_STATE_DETACHING } GDBState; typedef struct ThreadInfo @@ -81,9 +81,12 @@ typedef struct ThreadInfo u32 tls; } ThreadInfo; +struct GDBServer; + typedef struct GDBContext { sock_ctx super; + struct GDBServer *parent; RecursiveLock lock; u16 localPort; @@ -98,7 +101,7 @@ typedef struct GDBContext u32 currentThreadId, selectedThreadId, selectedThreadIdForContinuing; u32 totalNbCreatedThreads; - Handle clientAcceptedEvent, continuedEvent; + Handle processAttachedEvent, continuedEvent; Handle eventToWaitFor; bool catchThreadEvents; @@ -130,4 +133,8 @@ typedef int (*GDBCommandHandler)(GDBContext *ctx); void GDB_InitializeContext(GDBContext *ctx); void GDB_FinalizeContext(GDBContext *ctx); + +Result GDB_AttachToProcess(GDBContext *ctx); +void GDB_DetachFromProcess(GDBContext *ctx); + GDB_DECLARE_HANDLER(Unsupported); diff --git a/sysmodules/rosalina/source/gdb.c b/sysmodules/rosalina/source/gdb.c index 20367ea..1b75c94 100644 --- a/sysmodules/rosalina/source/gdb.c +++ b/sysmodules/rosalina/source/gdb.c @@ -27,6 +27,12 @@ #include "gdb.h" #include "gdb/net.h" +#include "gdb/debug.h" + +#include "gdb/watchpoints.h" +#include "gdb/breakpoints.h" +#include "gdb/stop_point.h" + void GDB_InitializeContext(GDBContext *ctx) { memset(ctx, 0, sizeof(GDBContext)); @@ -35,9 +41,9 @@ void GDB_InitializeContext(GDBContext *ctx) RecursiveLock_Lock(&ctx->lock); svcCreateEvent(&ctx->continuedEvent, RESET_ONESHOT); - svcCreateEvent(&ctx->clientAcceptedEvent, RESET_STICKY); + svcCreateEvent(&ctx->processAttachedEvent, RESET_STICKY); - ctx->eventToWaitFor = ctx->clientAcceptedEvent; + ctx->eventToWaitFor = ctx->processAttachedEvent; ctx->continueFlags = (DebugFlags)(DBG_SIGNAL_FAULT_EXCEPTION_EVENTS | DBG_INHIBIT_USER_CPU_EXCEPTION_HANDLERS); RecursiveLock_Unlock(&ctx->lock); @@ -47,14 +53,134 @@ void GDB_FinalizeContext(GDBContext *ctx) { RecursiveLock_Lock(&ctx->lock); - svcClearEvent(ctx->clientAcceptedEvent); + svcClearEvent(ctx->processAttachedEvent); - svcCloseHandle(ctx->clientAcceptedEvent); + svcCloseHandle(ctx->processAttachedEvent); svcCloseHandle(ctx->continuedEvent); RecursiveLock_Unlock(&ctx->lock); } +Result GDB_AttachToProcess(GDBContext *ctx) +{ + Result r; + + // Two cases: attached during execution, or started attached + // The second case will have, after RunQueuedProcess: attach process, debugger break, attach thread (with creator = 0) + + if (!(ctx->flags & GDB_FLAG_ATTACHED_AT_START)) + svcDebugActiveProcess(&ctx->debug, ctx->pid); + else + { + r = 0; + } + if(R_SUCCEEDED(r)) + { + // Note: ctx->pid will be (re)set while processing 'attach process' + DebugEventInfo *info = &ctx->latestDebugEvent; + ctx->state = GDB_STATE_CONNECTED; + ctx->processExited = ctx->processEnded = false; + ctx->latestSentPacketSize = 0; + if (!(ctx->flags & GDB_FLAG_ATTACHED_AT_START)) + { + while(R_SUCCEEDED(svcGetProcessDebugEvent(info, ctx->debug)) && + info->type != DBGEVENT_EXCEPTION && + info->exception.type != EXCEVENT_ATTACH_BREAK) + { + GDB_PreprocessDebugEvent(ctx, info); + svcContinueDebugEvent(ctx->debug, ctx->continueFlags); + } + } + else + { + // Attach process, debugger break + for(u32 i = 0; i < 2; i++) + { + if (R_FAILED(r = svcGetProcessDebugEvent(info, ctx->debug))) + return r; + GDB_PreprocessDebugEvent(ctx, info); + if (R_FAILED(r = svcContinueDebugEvent(ctx->debug, ctx->continueFlags))) + return r; + } + + if(R_FAILED(r = svcWaitSynchronization(ctx->debug, -1LL))) + return r; + if (R_FAILED(r = svcGetProcessDebugEvent(info, ctx->debug))) + return r; + // Attach thread + GDB_PreprocessDebugEvent(ctx, info); + } + } + else + return r; + + return svcSignalEvent(ctx->processAttachedEvent); +} + +void GDB_DetachFromProcess(GDBContext *ctx) +{ + DebugEventInfo dummy; + for(u32 i = 0; i < ctx->nbBreakpoints; i++) + { + if(!ctx->breakpoints[i].persistent) + GDB_DisableBreakpointById(ctx, i); + } + memset(&ctx->breakpoints, 0, sizeof(ctx->breakpoints)); + ctx->nbBreakpoints = 0; + + for(u32 i = 0; i < ctx->nbWatchpoints; i++) + { + GDB_RemoveWatchpoint(ctx, ctx->watchpoints[i], WATCHPOINT_DISABLED); + ctx->watchpoints[i] = 0; + } + ctx->nbWatchpoints = 0; + + svcKernelSetState(0x10002, ctx->pid, false); + memset(ctx->svcMask, 0, 32); + + memset(ctx->memoryOsInfoXmlData, 0, sizeof(ctx->memoryOsInfoXmlData)); + memset(ctx->processesOsInfoXmlData, 0, sizeof(ctx->processesOsInfoXmlData)); + memset(ctx->threadListData, 0, sizeof(ctx->threadListData)); + ctx->threadListDataPos = 0; + + svcClearEvent(ctx->processAttachedEvent); + ctx->eventToWaitFor = ctx->processAttachedEvent; + + //svcSignalEvent(server->statusUpdated); + + /* + There's a possibility of a race condition with a possible user exception handler, but you shouldn't + use 'kill' on APPLICATION titles in the first place (reboot hanging because the debugger is still running, etc). + */ + + ctx->continueFlags = (DebugFlags)0; + + while(R_SUCCEEDED(svcGetProcessDebugEvent(&dummy, ctx->debug))); + while(R_SUCCEEDED(svcContinueDebugEvent(ctx->debug, ctx->continueFlags))); + if(ctx->flags & GDB_FLAG_TERMINATE_PROCESS) + { + svcTerminateDebugProcess(ctx->debug); + ctx->processEnded = true; + ctx->processExited = false; + } + + while(R_SUCCEEDED(svcGetProcessDebugEvent(&dummy, ctx->debug))); + while(R_SUCCEEDED(svcContinueDebugEvent(ctx->debug, ctx->continueFlags))); + + svcCloseHandle(ctx->debug); + ctx->debug = 0; + + + ctx->eventToWaitFor = ctx->processAttachedEvent; + ctx->continueFlags = (DebugFlags)(DBG_SIGNAL_FAULT_EXCEPTION_EVENTS | DBG_INHIBIT_USER_CPU_EXCEPTION_HANDLERS); + ctx->pid = 0; + ctx->currentThreadId = ctx->selectedThreadId = ctx->selectedThreadIdForContinuing = 0; + ctx->nbThreads = 0; + ctx->totalNbCreatedThreads = 0; + memset(ctx->threadInfos, 0, sizeof(ctx->threadInfos)); + ctx->catchThreadEvents = false; +} + GDB_DECLARE_HANDLER(Unsupported) { return GDB_ReplyEmpty(ctx); diff --git a/sysmodules/rosalina/source/gdb/debug.c b/sysmodules/rosalina/source/gdb/debug.c index 3d47e97..70fe448 100644 --- a/sysmodules/rosalina/source/gdb/debug.c +++ b/sysmodules/rosalina/source/gdb/debug.c @@ -43,13 +43,13 @@ GDB_DECLARE_HANDLER(Detach) { - ctx->state = GDB_STATE_CLOSING; + ctx->state = GDB_STATE_DETACHING; return GDB_ReplyOk(ctx); } GDB_DECLARE_HANDLER(Kill) { - ctx->state = GDB_STATE_CLOSING; + ctx->state = GDB_STATE_DETACHING; ctx->flags |= GDB_FLAG_TERMINATE_PROCESS; return 0; } @@ -480,7 +480,7 @@ int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info) */ int GDB_HandleDebugEvents(GDBContext *ctx) { - if(ctx->state == GDB_STATE_CLOSING) + if(ctx->state == GDB_STATE_DETACHING) return -1; DebugEventInfo info; diff --git a/sysmodules/rosalina/source/gdb/monitor.c b/sysmodules/rosalina/source/gdb/monitor.c index 4683886..72d48bf 100644 --- a/sysmodules/rosalina/source/gdb/monitor.c +++ b/sysmodules/rosalina/source/gdb/monitor.c @@ -42,11 +42,13 @@ void GDB_RunMonitor(GDBServer *server) do { + GDB_LockAllContexts(server); for(int i = 0; i < MAX_DEBUG; i++) { GDBContext *ctx = &server->ctxs[i]; handles[3 + i] = ctx->eventToWaitFor; } + GDB_UnlockAllContexts(server); s32 idx = -1; r = svcWaitSynchronizationN(&idx, handles, 3 + MAX_DEBUG, false, -1LL); @@ -60,15 +62,15 @@ void GDB_RunMonitor(GDBServer *server) GDBContext *ctx = &server->ctxs[idx - 3]; RecursiveLock_Lock(&ctx->lock); - if(ctx->state == GDB_STATE_DISCONNECTED || ctx->state == GDB_STATE_CLOSING) + if(ctx->state == GDB_STATE_DISCONNECTED || ctx->state == GDB_STATE_DETACHING) { - svcClearEvent(ctx->clientAcceptedEvent); - ctx->eventToWaitFor = ctx->clientAcceptedEvent; + svcClearEvent(ctx->processAttachedEvent); + ctx->eventToWaitFor = ctx->processAttachedEvent; RecursiveLock_Unlock(&ctx->lock); continue; } - if(ctx->eventToWaitFor == ctx->clientAcceptedEvent) + if(ctx->eventToWaitFor == ctx->processAttachedEvent) ctx->eventToWaitFor = ctx->continuedEvent; else if(ctx->eventToWaitFor == ctx->continuedEvent) ctx->eventToWaitFor = ctx->debug; @@ -82,8 +84,8 @@ void GDB_RunMonitor(GDBServer *server) while(GDB_HandleDebugEvents(ctx) != -1) // until we've got all the remaining debug events svcSleepThread(1 * 1000 * 1000LL); // sleep just in case - svcClearEvent(ctx->clientAcceptedEvent); - ctx->eventToWaitFor = ctx->clientAcceptedEvent; + svcClearEvent(ctx->processAttachedEvent); + ctx->eventToWaitFor = ctx->processAttachedEvent; } } diff --git a/sysmodules/rosalina/source/gdb/net.c b/sysmodules/rosalina/source/gdb/net.c index 46ca1b9..e5a334e 100644 --- a/sysmodules/rosalina/source/gdb/net.c +++ b/sysmodules/rosalina/source/gdb/net.c @@ -291,7 +291,7 @@ int GDB_SendStreamData(GDBContext *ctx, const char *streamData, u32 offset, u32 int GDB_SendDebugString(GDBContext *ctx, const char *fmt, ...) // unsecure { - if(ctx->state == GDB_STATE_CLOSING || !(ctx->flags & GDB_FLAG_PROCESS_CONTINUING)) + if(ctx->state == GDB_STATE_DETACHING || !(ctx->flags & GDB_FLAG_PROCESS_CONTINUING)) return 0; char formatted[(GDB_BUF_LEN - 1) / 2 + 1]; diff --git a/sysmodules/rosalina/source/gdb/server.c b/sysmodules/rosalina/source/gdb/server.c index 4e55e35..f98c0fc 100644 --- a/sysmodules/rosalina/source/gdb/server.c +++ b/sysmodules/rosalina/source/gdb/server.c @@ -154,98 +154,22 @@ GDBContext *GDB_SelectAvailableContext(GDBServer *server, u16 minPort, u16 maxPo int GDB_AcceptClient(GDBContext *ctx) { - RecursiveLock_Lock(&ctx->lock); Result r; - // Two cases: attached during execution, or started attached - // The second case will have, after RunQueuedProcess: attach process, debugger break, attach thread (with creator = 0) - - if (!(ctx->flags & GDB_FLAG_ATTACHED_AT_START)) - svcDebugActiveProcess(&ctx->debug, ctx->pid); - else - { - r = 0; - } - if(R_SUCCEEDED(r)) - { - // Note: ctx->pid will be (re)set while processing 'attach process' - DebugEventInfo *info = &ctx->latestDebugEvent; - ctx->state = GDB_STATE_CONNECTED; - ctx->processExited = ctx->processEnded = false; - ctx->latestSentPacketSize = 0; - if (!(ctx->flags & GDB_FLAG_ATTACHED_AT_START)) - { - while(R_SUCCEEDED(svcGetProcessDebugEvent(info, ctx->debug)) && - info->type != DBGEVENT_EXCEPTION && - info->exception.type != EXCEVENT_ATTACH_BREAK) - { - GDB_PreprocessDebugEvent(ctx, info); - svcContinueDebugEvent(ctx->debug, ctx->continueFlags); - } - } - else - { - // Attach process, debugger break - for(u32 i = 0; i < 2; i++) - { - if (R_FAILED(svcGetProcessDebugEvent(info, ctx->debug))) - return -1; - GDB_PreprocessDebugEvent(ctx, info); - if (R_FAILED(svcContinueDebugEvent(ctx->debug, ctx->continueFlags))) - return -1; - } - - svcWaitSynchronization(ctx->debug, -1LL); - if (R_FAILED(svcGetProcessDebugEvent(info, ctx->debug))) - return -1; //svcBreak(0); - // Attach thread - GDB_PreprocessDebugEvent(ctx, info); - } - } - else - { - RecursiveLock_Unlock(&ctx->lock); - return -1; - } - - svcSignalEvent(ctx->clientAcceptedEvent); + RecursiveLock_Lock(&ctx->lock); + r = GDB_AttachToProcess(ctx); RecursiveLock_Unlock(&ctx->lock); - return 0; + return R_SUCCEEDED(r) ? 0 : -1; } int GDB_CloseClient(GDBContext *ctx) { RecursiveLock_Lock(&ctx->lock); - - for(u32 i = 0; i < ctx->nbBreakpoints; i++) - { - if(!ctx->breakpoints[i].persistent) - GDB_DisableBreakpointById(ctx, i); - } - memset(&ctx->breakpoints, 0, sizeof(ctx->breakpoints)); - ctx->nbBreakpoints = 0; - - for(u32 i = 0; i < ctx->nbWatchpoints; i++) - { - GDB_RemoveWatchpoint(ctx, ctx->watchpoints[i], WATCHPOINT_DISABLED); - ctx->watchpoints[i] = 0; - } - ctx->nbWatchpoints = 0; - - svcKernelSetState(0x10002, ctx->pid, false); - memset(ctx->svcMask, 0, 32); - - memset(ctx->memoryOsInfoXmlData, 0, sizeof(ctx->memoryOsInfoXmlData)); - memset(ctx->processesOsInfoXmlData, 0, sizeof(ctx->processesOsInfoXmlData)); - memset(ctx->threadListData, 0, sizeof(ctx->threadListData)); - ctx->threadListDataPos = 0; - - svcClearEvent(ctx->clientAcceptedEvent); - ctx->eventToWaitFor = ctx->clientAcceptedEvent; - - ctx->localPort = 0; + svcSignalEvent(ctx->parent->statusUpdated); // note: monitor will be waiting for lock + GDB_DetachFromProcess(ctx); RecursiveLock_Unlock(&ctx->lock); + return 0; } @@ -266,6 +190,7 @@ GDBContext *GDB_GetClient(GDBServer *server, u16 port) { ctx->flags |= GDB_FLAG_USED; ctx->state = GDB_STATE_CONNECTED; + ctx->parent = server; } GDB_UnlockAllContexts(server); @@ -274,46 +199,12 @@ GDBContext *GDB_GetClient(GDBServer *server, u16 port) void GDB_ReleaseClient(GDBServer *server, GDBContext *ctx) { - DebugEventInfo dummy; - - svcSignalEvent(server->statusUpdated); - + (void)server; RecursiveLock_Lock(&ctx->lock); - - /* - There's a possibility of a race condition with a possible user exception handler, but you shouldn't - use 'kill' on APPLICATION titles in the first place (reboot hanging because the debugger is still running, etc). - */ - - ctx->continueFlags = (DebugFlags)0; - - while(R_SUCCEEDED(svcGetProcessDebugEvent(&dummy, ctx->debug))); - while(R_SUCCEEDED(svcContinueDebugEvent(ctx->debug, ctx->continueFlags))); - if(ctx->flags & GDB_FLAG_TERMINATE_PROCESS) - { - svcTerminateDebugProcess(ctx->debug); - ctx->processEnded = true; - ctx->processExited = false; - } - - while(R_SUCCEEDED(svcGetProcessDebugEvent(&dummy, ctx->debug))); - while(R_SUCCEEDED(svcContinueDebugEvent(ctx->debug, ctx->continueFlags))); - - svcCloseHandle(ctx->debug); - ctx->debug = 0; - + ctx->localPort = 0; + ctx->enableExternalMemoryAccess = false; ctx->flags = (GDBFlags)0; ctx->state = GDB_STATE_DISCONNECTED; - - ctx->eventToWaitFor = ctx->clientAcceptedEvent; - ctx->continueFlags = (DebugFlags)(DBG_SIGNAL_FAULT_EXCEPTION_EVENTS | DBG_INHIBIT_USER_CPU_EXCEPTION_HANDLERS); - ctx->pid = 0; - ctx->currentThreadId = ctx->selectedThreadId = ctx->selectedThreadIdForContinuing = 0; - ctx->nbThreads = 0; - ctx->totalNbCreatedThreads = 0; - memset(ctx->threadInfos, 0, sizeof(ctx->threadInfos)); - ctx->catchThreadEvents = false; - ctx->enableExternalMemoryAccess = false; RecursiveLock_Unlock(&ctx->lock); } @@ -384,7 +275,7 @@ int GDB_DoPacket(GDBContext *ctx) ret = 0; RecursiveLock_Unlock(&ctx->lock); - if(ctx->state == GDB_STATE_CLOSING) + if(ctx->state == GDB_STATE_DETACHING) return -1; if((oldFlags & GDB_FLAG_PROCESS_CONTINUING) && !(ctx->flags & GDB_FLAG_PROCESS_CONTINUING)) diff --git a/sysmodules/rosalina/source/menus/process_list.c b/sysmodules/rosalina/source/menus/process_list.c index e6dcd5e..08cbfc5 100644 --- a/sysmodules/rosalina/source/menus/process_list.c +++ b/sysmodules/rosalina/source/menus/process_list.c @@ -71,7 +71,7 @@ static inline int ProcessListMenu_FormatInfoLine(char *out, const ProcessInfo *i else if(gdbServer.super.running && id < MAX_DEBUG) { - if(gdbServer.ctxs[id].state >= GDB_STATE_CONNECTED && gdbServer.ctxs[id].state < GDB_STATE_CLOSING) + if(gdbServer.ctxs[id].state >= GDB_STATE_CONNECTED && gdbServer.ctxs[id].state < GDB_STATE_DETACHING) { u8 *addr = (u8 *)&gdbServer.ctxs[id].super.addr_in.sin_addr; checkbox = "(A) ";