diff --git a/sysmodules/rosalina/include/gdb.h b/sysmodules/rosalina/include/gdb.h index cbb5a9f..d5c3ae9 100644 --- a/sysmodules/rosalina/include/gdb.h +++ b/sysmodules/rosalina/include/gdb.h @@ -30,6 +30,7 @@ #include <3ds/svc.h> #include <3ds/synchronization.h> #include <3ds/result.h> +#include "pmdbgext.h" #include "sock_util.h" #include "memory.h" @@ -64,17 +65,19 @@ enum GDB_FLAG_ALLOCATED_MASK = GDB_FLAG_SELECTED | GDB_FLAG_USED, GDB_FLAG_EXTENDED_REMOTE = 4, GDB_FLAG_NOACK = 8, + GDB_FLAG_PROC_RESTART_MASK = GDB_FLAG_NOACK | GDB_FLAG_EXTENDED_REMOTE | GDB_FLAG_USED, GDB_FLAG_PROCESS_CONTINUING = 16, GDB_FLAG_TERMINATE_PROCESS = 32, GDB_FLAG_ATTACHED_AT_START = 64, + GDB_FLAG_CREATED = 128, }; typedef enum GDBState { GDB_STATE_DISCONNECTED, GDB_STATE_CONNECTED, - GDB_STATE_NOACK_SENT, - GDB_STATE_DETACHING + GDB_STATE_ATTACHED, + GDB_STATE_DETACHING, } GDBState; typedef struct ThreadInfo @@ -95,9 +98,15 @@ typedef struct GDBContext u32 flags; GDBState state; + bool noAckSent; u32 pid; Handle debug; + + // vRun and R (restart) info: + FS_ProgramInfo launchedProgramInfo; + u32 launchedProgramLaunchFlags; + ThreadInfo threadInfos[MAX_DEBUG_THREAD]; u32 nbThreads; u32 currentThreadId, selectedThreadId, selectedThreadIdForContinuing; @@ -138,6 +147,7 @@ void GDB_FinalizeContext(GDBContext *ctx); Result GDB_AttachToProcess(GDBContext *ctx); void GDB_DetachFromProcess(GDBContext *ctx); +Result GDB_CreateProcess(GDBContext *ctx, const FS_ProgramInfo *progInfo, u32 launchFlags); GDB_DECLARE_HANDLER(Unsupported); GDB_DECLARE_HANDLER(EnableExtendedMode); diff --git a/sysmodules/rosalina/include/gdb/debug.h b/sysmodules/rosalina/include/gdb/debug.h index 66df51d..a7156c3 100644 --- a/sysmodules/rosalina/include/gdb/debug.h +++ b/sysmodules/rosalina/include/gdb/debug.h @@ -28,6 +28,8 @@ #include "gdb.h" +GDB_DECLARE_VERBOSE_HANDLER(Run); +GDB_DECLARE_HANDLER(Restart); GDB_DECLARE_VERBOSE_HANDLER(Attach); GDB_DECLARE_HANDLER(Detach); GDB_DECLARE_HANDLER(Kill); diff --git a/sysmodules/rosalina/include/gdb/net.h b/sysmodules/rosalina/include/gdb/net.h index 2ec2278..d7e05a8 100644 --- a/sysmodules/rosalina/include/gdb/net.h +++ b/sysmodules/rosalina/include/gdb/net.h @@ -36,6 +36,8 @@ u32 GDB_DecodeHex(void *dst, const char *src, u32 len); u32 GDB_UnescapeBinaryData(void *dst, const void *src, u32 len); const char *GDB_ParseIntegerList(u32 *dst, const char *src, u32 nb, char sep, char lastSep, u32 base, bool allowPrefix); const char *GDB_ParseHexIntegerList(u32 *dst, const char *src, u32 nb, char lastSep); +const char *GDB_ParseIntegerList64(u64 *dst, const char *src, u32 nb, char sep, char lastSep, u32 base, bool allowPrefix); +const char *GDB_ParseHexIntegerList64(u64 *dst, const char *src, u32 nb, char lastSep); int GDB_ReceivePacket(GDBContext *ctx); int GDB_SendPacket(GDBContext *ctx, const char *packetData, u32 len); int GDB_SendFormattedPacket(GDBContext *ctx, const char *packetDataFmt, ...); diff --git a/sysmodules/rosalina/include/memory.h b/sysmodules/rosalina/include/memory.h index faa0079..3852fc2 100644 --- a/sysmodules/rosalina/include/memory.h +++ b/sysmodules/rosalina/include/memory.h @@ -32,3 +32,4 @@ u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize); void hexItoa(u64 number, char *out, u32 digits, bool uppercase); unsigned long int xstrtoul(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok); +unsigned long long int xstrtoull(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok); diff --git a/sysmodules/rosalina/source/gdb.c b/sysmodules/rosalina/source/gdb.c index 5e36253..4885172 100644 --- a/sysmodules/rosalina/source/gdb.c +++ b/sysmodules/rosalina/source/gdb.c @@ -79,9 +79,7 @@ Result GDB_AttachToProcess(GDBContext *ctx) { // 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)) && @@ -115,7 +113,10 @@ Result GDB_AttachToProcess(GDBContext *ctx) else return r; - return svcSignalEvent(ctx->processAttachedEvent); + r = svcSignalEvent(ctx->processAttachedEvent); + if (R_SUCCEEDED(r)) + ctx->state = GDB_STATE_ATTACHED; + return r; } void GDB_DetachFromProcess(GDBContext *ctx) @@ -168,7 +169,8 @@ void GDB_DetachFromProcess(GDBContext *ctx) svcCloseHandle(ctx->debug); ctx->debug = 0; - + memset(&ctx->launchedProgramInfo, 0, sizeof(FS_ProgramInfo)); + ctx->launchedProgramLaunchFlags = 0; ctx->eventToWaitFor = ctx->processAttachedEvent; ctx->continueFlags = (DebugFlags)(DBG_SIGNAL_FAULT_EXCEPTION_EVENTS | DBG_INHIBIT_USER_CPU_EXCEPTION_HANDLERS); @@ -177,6 +179,24 @@ void GDB_DetachFromProcess(GDBContext *ctx) ctx->nbThreads = 0; ctx->totalNbCreatedThreads = 0; memset(ctx->threadInfos, 0, sizeof(ctx->threadInfos)); + + ctx->state = GDB_STATE_CONNECTED; +} + +Result GDB_CreateProcess(GDBContext *ctx, const FS_ProgramInfo *progInfo, u32 launchFlags) +{ + Handle debug = 0; + ctx->debug = 0; + Result r = PMDBG_LaunchTitleDebug(&debug, progInfo, launchFlags); + if(R_FAILED(r)) + return r; + + ctx->flags |= GDB_FLAG_CREATED | GDB_FLAG_ATTACHED_AT_START; + ctx->debug = debug; + ctx->launchedProgramInfo = *progInfo; + ctx->launchedProgramLaunchFlags = launchFlags; + r = GDB_AttachToProcess(ctx); + return r; } GDB_DECLARE_HANDLER(Unsupported) diff --git a/sysmodules/rosalina/source/gdb/debug.c b/sysmodules/rosalina/source/gdb/debug.c index 3421a99..bd515af 100644 --- a/sysmodules/rosalina/source/gdb/debug.c +++ b/sysmodules/rosalina/source/gdb/debug.c @@ -24,6 +24,7 @@ * reasonable ways as different from the original version. */ +#define _GNU_SOURCE // for strchrnul #include "gdb/debug.h" #include "gdb/server.h" #include "gdb/verbose.h" @@ -35,6 +36,105 @@ #include #include +#include "pmdbgext.h" + +static void GDB_DetachImmediatelyExtended(GDBContext *ctx) +{ + // detach immediately + RecursiveLock_Lock(&ctx->lock); + svcSignalEvent(ctx->parent->statusUpdated); // note: monitor will be waiting for lock + + ctx->state = GDB_STATE_DETACHING; + GDB_DetachFromProcess(ctx); + ctx->flags &= GDB_FLAG_PROC_RESTART_MASK; + RecursiveLock_Unlock(&ctx->lock); +} + +GDB_DECLARE_VERBOSE_HANDLER(Run) +{ + // Note: only titleId [mediaType [launchFlags]] is supported, and the launched title shouldn't rely on APT + // all 3 parameters should be hex-encoded. + + // Extended remote only + if (!(ctx->flags & GDB_FLAG_EXTENDED_REMOTE)) + return GDB_ReplyErrno(ctx, EPERM); + + u64 titleId; + u32 mediaType = MEDIATYPE_NAND; + u32 launchFlags = PMLAUNCHFLAG_LOAD_DEPENDENCIES; + + char args[3][32] = {{0}}; + char *pos = ctx->commandData; + for (u32 i = 0; i < 3 && *pos != 0; i++) + { + char *pos2 = strchrnul(pos, ';'); + u32 dist = pos2 - pos; + if (dist < 2) + return GDB_ReplyErrno(ctx, EILSEQ); + if (dist % 2 == 1) + return GDB_ReplyErrno(ctx, EILSEQ); + + if (dist / 2 > 16) // buffer overflow check + return GDB_ReplyErrno(ctx, EINVAL); + + u32 n = GDB_DecodeHex(args[i], pos, dist / 2); + if (n == 0) + return GDB_ReplyErrno(ctx, EILSEQ); + pos = *pos2 == 0 ? pos2 : pos2 + 1; + } + + if (args[0][0] == 0) + return GDB_ReplyErrno(ctx, EINVAL); // first arg mandatory + + if (GDB_ParseIntegerList64(&titleId, args[0], 1, 0, 0, 16, false) == NULL) + return GDB_ReplyErrno(ctx, EINVAL); + + if (args[1][0] != 0 && (GDB_ParseIntegerList(&mediaType, args[1], 1, 0, 0, 16, true) == NULL || mediaType >= 0x100)) + return GDB_ReplyErrno(ctx, EINVAL); + + if (args[2][0] != 0 && GDB_ParseIntegerList(&launchFlags, args[2], 1, 0, 0, 16, true) == NULL) + return GDB_ReplyErrno(ctx, EINVAL); + + FS_ProgramInfo progInfo; + progInfo.mediaType = (FS_MediaType)mediaType; + progInfo.programId = titleId; + + RecursiveLock_Lock(&ctx->lock); + Result r = GDB_CreateProcess(ctx, &progInfo, launchFlags); + + if (R_FAILED(r)) + { + if(ctx->debug != 0) + GDB_DetachImmediatelyExtended(ctx); + RecursiveLock_Unlock(&ctx->lock); + return GDB_ReplyErrno(ctx, EPERM); + } + + RecursiveLock_Unlock(&ctx->lock); + return R_SUCCEEDED(r) ? GDB_SendStopReply(ctx, &ctx->latestDebugEvent) : GDB_ReplyErrno(ctx, EPERM); +} + +GDB_DECLARE_HANDLER(Restart) +{ + // Note: removed from gdb + // Extended remote only & process must have been created + if (!(ctx->flags & GDB_FLAG_EXTENDED_REMOTE) || !(ctx->flags & GDB_FLAG_CREATED)) + return GDB_ReplyErrno(ctx, EPERM); + + FS_ProgramInfo progInfo = ctx->launchedProgramInfo; + u32 launchFlags = ctx->launchedProgramLaunchFlags; + + ctx->flags |= GDB_FLAG_TERMINATE_PROCESS; + if (ctx->flags & GDB_FLAG_EXTENDED_REMOTE) + GDB_DetachImmediatelyExtended(ctx); + + RecursiveLock_Lock(&ctx->lock); + Result r = GDB_CreateProcess(ctx, &progInfo, launchFlags); + if (R_FAILED(r) && ctx->debug != 0) + GDB_DetachImmediatelyExtended(ctx); + RecursiveLock_Unlock(&ctx->lock); + return 0; +} GDB_DECLARE_VERBOSE_HANDLER(Attach) { @@ -49,6 +149,8 @@ GDB_DECLARE_VERBOSE_HANDLER(Attach) RecursiveLock_Lock(&ctx->lock); ctx->pid = pid; Result r = GDB_AttachToProcess(ctx); + if(R_FAILED(r)) + GDB_DetachImmediatelyExtended(ctx); RecursiveLock_Unlock(&ctx->lock); return R_SUCCEEDED(r) ? GDB_SendStopReply(ctx, &ctx->latestDebugEvent) : GDB_ReplyErrno(ctx, EPERM); } @@ -63,14 +165,7 @@ GDB_DECLARE_HANDLER(Detach) { ctx->state = GDB_STATE_DETACHING; if (ctx->flags & GDB_FLAG_EXTENDED_REMOTE) - { - // detach immediately - RecursiveLock_Lock(&ctx->lock); - svcSignalEvent(ctx->parent->statusUpdated); // note: monitor will be waiting for lock - GDB_DetachFromProcess(ctx); - ctx->flags = GDB_FLAG_USED; - RecursiveLock_Unlock(&ctx->lock); - } + GDB_DetachImmediatelyExtended(ctx); return GDB_ReplyOk(ctx); } @@ -79,13 +174,8 @@ GDB_DECLARE_HANDLER(Kill) ctx->state = GDB_STATE_DETACHING; ctx->flags |= GDB_FLAG_TERMINATE_PROCESS; if (ctx->flags & GDB_FLAG_EXTENDED_REMOTE) - { - // detach & kill immediately - RecursiveLock_Lock(&ctx->lock); - svcSignalEvent(ctx->parent->statusUpdated); // note: monitor will be waiting for lock - GDB_DetachFromProcess(ctx); - RecursiveLock_Unlock(&ctx->lock); - } + GDB_DetachImmediatelyExtended(ctx); + return 0; } diff --git a/sysmodules/rosalina/source/gdb/net.c b/sysmodules/rosalina/source/gdb/net.c index 9508347..6c371ee 100644 --- a/sysmodules/rosalina/source/gdb/net.c +++ b/sysmodules/rosalina/source/gdb/net.c @@ -125,11 +125,47 @@ const char *GDB_ParseIntegerList(u32 *dst, const char *src, u32 nb, char sep, ch return pos; } +const char *GDB_ParseIntegerList64(u64 *dst, const char *src, u32 nb, char sep, char lastSep, u32 base, bool allowPrefix) +{ + const char *pos = src; + const char *endpos; + bool ok; + + for(u32 i = 0; i < nb; i++) + { + u64 n = xstrtoull(pos, (char **)&endpos, (int) base, allowPrefix, &ok); + if(!ok || endpos == pos) + return NULL; + + if(i != nb - 1) + { + if(*endpos != sep) + return NULL; + pos = endpos + 1; + } + else + { + if(*endpos != lastSep && *endpos != 0) + return NULL; + pos = endpos; + } + + dst[i] = n; + } + + return pos; +} + const char *GDB_ParseHexIntegerList(u32 *dst, const char *src, u32 nb, char lastSep) { return GDB_ParseIntegerList(dst, src, nb, ',', lastSep, 16, false); } +const char *GDB_ParseHexIntegerList64(u64 *dst, const char *src, u32 nb, char lastSep) +{ + return GDB_ParseIntegerList64(dst, src, nb, ',', lastSep, 16, false); +} + int GDB_ReceivePacket(GDBContext *ctx) { char backupbuf[GDB_BUF_LEN + 4]; @@ -200,8 +236,11 @@ int GDB_ReceivePacket(GDBContext *ctx) return -1; } - if(ctx->state == GDB_STATE_NOACK_SENT) + if(ctx->noAckSent) + { ctx->flags |= GDB_FLAG_NOACK; + ctx->noAckSent = false; + } return r; diff --git a/sysmodules/rosalina/source/gdb/query.c b/sysmodules/rosalina/source/gdb/query.c index ed8a654..d829ebb 100644 --- a/sysmodules/rosalina/source/gdb/query.c +++ b/sysmodules/rosalina/source/gdb/query.c @@ -120,13 +120,13 @@ GDB_DECLARE_QUERY_HANDLER(Supported) GDB_DECLARE_QUERY_HANDLER(StartNoAckMode) { - ctx->state = GDB_STATE_NOACK_SENT; + ctx->noAckSent = true; return GDB_ReplyOk(ctx); } GDB_DECLARE_QUERY_HANDLER(Attached) { - return GDB_SendPacket(ctx, "1", 1); + return GDB_SendPacket(ctx, (ctx->flags & GDB_FLAG_CREATED) ? "0" : "1", 1); } GDB_DECLARE_QUERY_HANDLER(CatchSyscalls) diff --git a/sysmodules/rosalina/source/gdb/server.c b/sysmodules/rosalina/source/gdb/server.c index 25242b4..73e3efb 100644 --- a/sysmodules/rosalina/source/gdb/server.c +++ b/sysmodules/rosalina/source/gdb/server.c @@ -158,7 +158,11 @@ GDBContext *GDB_FindAllocatedContextByPid(GDBServer *server, u32 pid) GDBContext *ctx = NULL; for(u32 i = 0; i < MAX_DEBUG; i++) { - if((server->ctxs[i].flags & GDB_FLAG_ALLOCATED_MASK) && server->ctxs[i].pid == pid) + if( + ((server->ctxs[i].flags & GDB_FLAG_SELECTED) || + (server->ctxs[i].state >= GDB_STATE_ATTACHED && server->ctxs[i].state < GDB_STATE_DETACHING)) + && server->ctxs[i].pid == pid + ) ctx = &server->ctxs[i]; } GDB_UnlockAllContexts(server); @@ -170,8 +174,12 @@ int GDB_AcceptClient(GDBContext *ctx) Result r = 0; RecursiveLock_Lock(&ctx->lock); + ctx->state = GDB_STATE_CONNECTED; + ctx->latestSentPacketSize = 0; + if (ctx->flags & GDB_FLAG_SELECTED) r = GDB_AttachToProcess(ctx); + RecursiveLock_Unlock(&ctx->lock); return R_SUCCEEDED(r) ? 0 : -1; @@ -182,6 +190,7 @@ int GDB_CloseClient(GDBContext *ctx) RecursiveLock_Lock(&ctx->lock); svcSignalEvent(ctx->parent->statusUpdated); // note: monitor will be waiting for lock GDB_DetachFromProcess(ctx); + ctx->state = GDB_STATE_DISCONNECTED; RecursiveLock_Unlock(&ctx->lock); return 0; } @@ -202,7 +211,6 @@ GDBContext *GDB_GetClient(GDBServer *server, u16 port) if (ctx != NULL) { // Context already tied to a port/selected - // Extended remote support disabled if (ctx->flags & GDB_FLAG_USED) { GDB_UnlockAllContexts(server); @@ -274,6 +282,7 @@ static const struct { 'P', GDB_HANDLER(WriteRegister) }, { 'q', GDB_HANDLER(ReadQuery) }, { 'Q', GDB_HANDLER(WriteQuery) }, + { 'R', GDB_HANDLER(Restart) }, { 'T', GDB_HANDLER(IsThreadAlive) }, { 'v', GDB_HANDLER(VerboseCommand) }, { 'X', GDB_HANDLER(WriteMemoryRaw) }, diff --git a/sysmodules/rosalina/source/gdb/verbose.c b/sysmodules/rosalina/source/gdb/verbose.c index 312c595..0822d2f 100644 --- a/sysmodules/rosalina/source/gdb/verbose.c +++ b/sysmodules/rosalina/source/gdb/verbose.c @@ -38,6 +38,7 @@ static const struct { "Cont?", GDB_VERBOSE_HANDLER(ContinueSupported) }, { "Cont", GDB_VERBOSE_HANDLER(Continue) }, { "MustReplyEmpty", GDB_HANDLER(Unsupported) }, + { "Run", GDB_VERBOSE_HANDLER(Run) }, }; GDB_DECLARE_HANDLER(VerboseCommand) diff --git a/sysmodules/rosalina/source/memory.c b/sysmodules/rosalina/source/memory.c index c149999..4d99bc0 100644 --- a/sysmodules/rosalina/source/memory.c +++ b/sysmodules/rosalina/source/memory.c @@ -152,3 +152,89 @@ unsigned long int xstrtoul(const char *nptr, char **endptr, int base, bool allow *endptr = (char *) (any ? (char *)s - 1 : nptr); return (acc); } + +// Copied from newlib, without the reent stuff + some other stuff +unsigned long long int xstrtoull(const char *nptr, char **endptr, int base, bool allowPrefix, bool *ok) +{ + register const unsigned char *s = (const unsigned char *)nptr; + register unsigned long long acc; + register int c; + register unsigned long long cutoff; + register int neg = 0, any, cutlim; + + if(ok != NULL) + *ok = true; + /* + * See strtol for comments as to the logic used. + */ + do { + c = *s++; + } while ((c >= 9 && c <= 13) || c == ' '); + if (c == '-') { + if(!allowPrefix) { + if(ok != NULL) + *ok = false; + return 0; + } + neg = 1; + c = *s++; + } else if (c == '+'){ + if(!allowPrefix) { + if(ok != NULL) + *ok = false; + return 0; + } + + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + + if(!allowPrefix) { + if(ok != NULL) + *ok = false; + return 0; + } + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) { + if(!allowPrefix) { + if(ok != NULL) + *ok = false; + return 0; + } + + base = c == '0' ? 8 : 10; + } + cutoff = (unsigned long long)(-1ull) / (unsigned long long)base; + cutlim = (unsigned long long)(-1ull) % (unsigned long long)base; + for (acc = 0, any = 0;; c = *s++) { + if (c >= '0' && c <= '9') + c -= '0'; + else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) + c -= c >= 'A' && c <= 'Z' ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = (unsigned long long)-1ull; + if(ok != NULL) + *ok = false; +// rptr->_errno = ERANGE; + } else if (neg) + acc = -acc; + if (endptr != 0) + *endptr = (char *) (any ? (char *)s - 1 : nptr); + return (acc); +} diff --git a/sysmodules/rosalina/source/menus/process_list.c b/sysmodules/rosalina/source/menus/process_list.c index 8a102f8..43c3c76 100644 --- a/sysmodules/rosalina/source/menus/process_list.c +++ b/sysmodules/rosalina/source/menus/process_list.c @@ -71,13 +71,13 @@ static inline int ProcessListMenu_FormatInfoLine(char *out, const ProcessInfo *i else if(gdbServer.super.running && ctx != NULL) { - if(ctx->state >= GDB_STATE_CONNECTED && ctx->state < GDB_STATE_DETACHING) + if(ctx->state >= GDB_STATE_ATTACHED && ctx->state < GDB_STATE_DETACHING) { u8 *addr = (u8 *)&ctx->super.addr_in.sin_addr; checkbox = "(A) "; sprintf(commentBuf, "Remote: %hhu.%hhu.%hhu.%hhu", addr[0], addr[1], addr[2], addr[3]); } - else if (ctx->localPort >= GDB_PORT_BASE && ctx->localPort < GDB_PORT_BASE + MAX_DEBUG) + else if ((ctx->flags & GDB_FLAG_SELECTED) && (ctx->localPort >= GDB_PORT_BASE && ctx->localPort < GDB_PORT_BASE + MAX_DEBUG)) { checkbox = "(W) "; sprintf(commentBuf, "Port: %hu", ctx->localPort); @@ -596,7 +596,7 @@ static inline void ProcessListMenu_HandleSelected(const ProcessInfo *info) if(ctx != NULL) { - if(ctx->flags & GDB_FLAG_USED) + if((ctx->flags & GDB_FLAG_USED) && (ctx->flags & GDB_FLAG_SELECTED)) { RecursiveLock_Lock(&ctx->lock); ctx->super.should_close = true; @@ -605,7 +605,7 @@ static inline void ProcessListMenu_HandleSelected(const ProcessInfo *info) while(ctx->super.should_close) svcSleepThread(12 * 1000 * 1000LL); } - else if (ctx->localPort >= GDB_PORT_BASE && ctx->localPort < GDB_PORT_BASE + MAX_DEBUG) + else if ((ctx->flags & GDB_FLAG_SELECTED) && (ctx->localPort >= GDB_PORT_BASE && ctx->localPort < GDB_PORT_BASE + MAX_DEBUG)) { RecursiveLock_Lock(&ctx->lock); ctx->flags &= ~GDB_FLAG_SELECTED;