From 4e9c7ef048e3dacaaa17fe1aef2fa0cbcd6deb8d Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Fri, 8 Apr 2022 23:47:31 +0100 Subject: [PATCH] Restore extended-remote support & map -><1 + 3DS PID> (breaking change) Once more, the "official" gdb client is the one than is the least compliant to its very own stub specs (compared to, say, IDA) --- sysmodules/rosalina/include/gdb.h | 1 + sysmodules/rosalina/include/gdb/debug.h | 1 + sysmodules/rosalina/include/gdb/thread.h | 14 +++ sysmodules/rosalina/source/gdb/debug.c | 82 +++++++++++++--- sysmodules/rosalina/source/gdb/query.c | 15 ++- sysmodules/rosalina/source/gdb/server.c | 1 + sysmodules/rosalina/source/gdb/thread.c | 120 +++++++++++++++++++---- sysmodules/rosalina/source/gdb/verbose.c | 1 + sysmodules/rosalina/source/gdb/xfer.c | 3 +- 9 files changed, 199 insertions(+), 39 deletions(-) diff --git a/sysmodules/rosalina/include/gdb.h b/sysmodules/rosalina/include/gdb.h index 4a2704747..cbc7d7cfd 100644 --- a/sysmodules/rosalina/include/gdb.h +++ b/sysmodules/rosalina/include/gdb.h @@ -124,6 +124,7 @@ typedef struct GDBContext Handle processAttachedEvent, continuedEvent; Handle eventToWaitFor; + bool multiprocessExtEnabled; bool catchThreadEvents; bool processEnded, processExited; diff --git a/sysmodules/rosalina/include/gdb/debug.h b/sysmodules/rosalina/include/gdb/debug.h index 0bf01a9a3..1c3ffd33b 100644 --- a/sysmodules/rosalina/include/gdb/debug.h +++ b/sysmodules/rosalina/include/gdb/debug.h @@ -14,6 +14,7 @@ GDB_DECLARE_HANDLER(Restart); GDB_DECLARE_VERBOSE_HANDLER(Attach); GDB_DECLARE_HANDLER(Detach); GDB_DECLARE_HANDLER(Kill); +GDB_DECLARE_VERBOSE_HANDLER(Kill); GDB_DECLARE_HANDLER(Break); GDB_DECLARE_HANDLER(Continue); GDB_DECLARE_VERBOSE_HANDLER(Continue); diff --git a/sysmodules/rosalina/include/gdb/thread.h b/sysmodules/rosalina/include/gdb/thread.h index 4986933de..b411394bc 100644 --- a/sysmodules/rosalina/include/gdb/thread.h +++ b/sysmodules/rosalina/include/gdb/thread.h @@ -9,6 +9,20 @@ #include "gdb.h" +static inline u32 GDB_ConvertFromRealPid(u32 pid) +{ + return pid + 1; +} + +static inline u32 GDB_ConvertToRealPid(u32 pid) +{ + return pid - 1; +} + +const char *GDB_ParseThreadId(GDBContext *ctx, u32 *outPid, u32 *outTid, const char *str, char lastSep); +u32 GDB_ParseDecodeSingleThreadId(GDBContext *ctx, const char *str, char lastSep); +int GDB_EncodeThreadId(GDBContext *ctx, char *outbuf, u32 tid); + u32 GDB_GetCurrentThreadFromList(GDBContext *ctx, u32 *threadIds, u32 nbThreads); u32 GDB_GetCurrentThread(GDBContext *ctx); diff --git a/sysmodules/rosalina/source/gdb/debug.c b/sysmodules/rosalina/source/gdb/debug.c index 35456d5f1..e07235f0c 100644 --- a/sysmodules/rosalina/source/gdb/debug.c +++ b/sysmodules/rosalina/source/gdb/debug.c @@ -138,7 +138,7 @@ GDB_DECLARE_VERBOSE_HANDLER(Attach) return GDB_ReplyErrno(ctx, EILSEQ); RecursiveLock_Lock(&ctx->lock); - ctx->pid = pid; + ctx->pid = GDB_ConvertToRealPid(pid); Result r = GDB_AttachToProcess(ctx); if(R_FAILED(r)) GDB_DetachImmediatelyExtended(ctx); @@ -154,6 +154,16 @@ GDB_DECLARE_VERBOSE_HANDLER(Attach) GDB_DECLARE_HANDLER(Detach) { + if (ctx->multiprocessExtEnabled) + { + //;pid + u32 pid; + if(ctx->commandData[0] != ';' || GDB_ParseHexIntegerList(&pid, ctx->commandData + 1, 1, 0) == NULL) + return GDB_ReplyErrno(ctx, EILSEQ); + pid = GDB_ConvertToRealPid(pid); + if (pid != ctx->pid) + return GDB_ReplyErrno(ctx, EPERM); + } ctx->state = GDB_STATE_DETACHING; if (ctx->flags & GDB_FLAG_EXTENDED_REMOTE) GDB_DetachImmediatelyExtended(ctx); @@ -170,6 +180,29 @@ GDB_DECLARE_HANDLER(Kill) return 0; } +GDB_DECLARE_VERBOSE_HANDLER(Kill) +{ + if (ctx->multiprocessExtEnabled) + { + //;pid . ';' is already consumed by our caller + u32 pid; + if(GDB_ParseHexIntegerList(&pid, ctx->commandData, 1, 0) == NULL) + return GDB_ReplyErrno(ctx, EILSEQ); + pid = GDB_ConvertToRealPid(pid); + if (pid != ctx->pid) + return GDB_ReplyErrno(ctx, EPERM); + } + + int ret = GDB_ReplyOk(ctx); + + ctx->state = GDB_STATE_DETACHING; + ctx->flags |= GDB_FLAG_TERMINATE_PROCESS; + if (ctx->flags & GDB_FLAG_EXTENDED_REMOTE) + GDB_DetachImmediatelyExtended(ctx); + + return ret; +} + GDB_DECLARE_HANDLER(Break) { if(!(ctx->flags & GDB_FLAG_PROCESS_CONTINUING)) @@ -232,7 +265,7 @@ GDB_DECLARE_HANDLER(Continue) GDB_DECLARE_VERBOSE_HANDLER(Continue) { - char *pos = ctx->commandData; + const char *pos = ctx->commandData; bool currentThreadFound = false; while(pos != NULL && *pos != 0 && !currentThreadFound) { @@ -247,18 +280,21 @@ GDB_DECLARE_VERBOSE_HANDLER(Continue) break; } - char *nextpos = (char *)strchr(pos, ';'); - if(strncmp(pos, "-1", 2) == 0) + u32 pid, tid; + + const char *nextpos = GDB_ParseThreadId(ctx, &pid, &tid, pos, ';'); + if (nextpos == NULL) + return GDB_ReplyErrno(ctx, EILSEQ); + + if (tid == 0) currentThreadFound = true; + if (pid != ctx->pid) + return GDB_ReplyErrno(ctx, EPERM); else - { - u32 threadId; - if(GDB_ParseHexIntegerList(&threadId, pos, 1, ';') == NULL) - return GDB_ReplyErrno(ctx, EILSEQ); - currentThreadFound = currentThreadFound || threadId == ctx->currentThreadId; - } + currentThreadFound = currentThreadFound || tid == ctx->currentThreadId; - pos = nextpos; + if (nextpos != NULL && *nextpos != '\0') + pos = nextpos + 1; } if(ctx->currentThreadId == 0 || currentThreadFound) @@ -269,12 +305,18 @@ GDB_DECLARE_VERBOSE_HANDLER(Continue) GDB_DECLARE_HANDLER(GetStopReason) { + char pidbuf[32]; + if (ctx->multiprocessExtEnabled && ctx->state == GDB_STATE_ATTACHED) + sprintf(pidbuf, ";process:%lx", GDB_ConvertFromRealPid(ctx->pid)); + else + pidbuf[0] = '\0'; + if (ctx->processEnded && ctx->processExited) { - return GDB_SendPacket(ctx, "W00", 3); + return GDB_SendFormattedPacket(ctx, "W00%s", pidbuf); } else if (ctx->processEnded && !ctx->processExited) { - return GDB_SendPacket(ctx, "X0f", 3); + return GDB_SendFormattedPacket(ctx, "X0f%s", pidbuf); } else if (ctx->debug == 0) { - return GDB_SendPacket(ctx, "W00", 3); + return GDB_SendFormattedPacket(ctx, "W00%s", pidbuf); } else { return GDB_SendStopReply(ctx, &ctx->latestDebugEvent); } @@ -287,7 +329,10 @@ static int GDB_ParseCommonThreadInfo(char *out, GDBContext *ctx, int sig) s64 dummy; u32 core; Result r = svcGetDebugThreadContext(®s, ctx->debug, threadId, THREADCONTEXT_CONTROL_ALL); - int n = sprintf(out, "T%02xthread:%lx;", sig, threadId); + + char tidbuf[32]; + GDB_EncodeThreadId(ctx, tidbuf, ctx->currentThreadId); + int n = sprintf(out, "T%02xthread:%s;", sig, tidbuf); if(R_FAILED(r)) return n; @@ -450,7 +495,12 @@ int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info) { // exited (no error / unhandled exception), SIGTERM (process terminated) * 2 static const char *processExitReplies[] = { "W00", "X0f", "X0f" }; - return GDB_SendPacket(ctx, processExitReplies[(u32)info->exit_process.reason], 3); + char pidbuf[32]; + if (ctx->multiprocessExtEnabled && ctx->state == GDB_STATE_ATTACHED) + sprintf(pidbuf, ";process:%lx", GDB_ConvertFromRealPid(ctx->pid)); + else + pidbuf[0] = '\0'; + return GDB_SendFormattedPacket(ctx, "%s%s", processExitReplies[(u32)info->exit_process.reason], pidbuf); } case DBGEVENT_EXCEPTION: diff --git a/sysmodules/rosalina/source/gdb/query.c b/sysmodules/rosalina/source/gdb/query.c index f070170ef..593087b26 100644 --- a/sysmodules/rosalina/source/gdb/query.c +++ b/sysmodules/rosalina/source/gdb/query.c @@ -5,6 +5,7 @@ * SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) */ +#define _GNU_SOURCE #include "gdb/query.h" #include "gdb/xfer.h" #include "gdb/thread.h" @@ -89,11 +90,23 @@ int GDB_HandleWriteQuery(GDBContext *ctx) GDB_DECLARE_QUERY_HANDLER(Supported) { + const char *pos = ctx->commandData, *nextpos = pos; + do + { + pos = nextpos; + nextpos = strchrnul(pos, ';'); + if (*nextpos != ';' && *nextpos != '\0') + return GDB_ReplyErrno(ctx, EILSEQ); + + if (strncmp(pos, "multiprocess+", nextpos - pos) == 0) + ctx->multiprocessExtEnabled = true; + } while (*nextpos++ != '\0'); + return GDB_SendFormattedPacket(ctx, "PacketSize=%x;" "qXfer:features:read+;qXfer:osdata:read+;" "QStartNoAckMode+;QThreadEvents+;QCatchSyscalls+;" - "vContSupported+;swbreak+", + "vContSupported+;swbreak+;multiprocess+", GDB_BUF_LEN // should have been sizeof(ctx->buffer) but GDB memory functions are bugged ); diff --git a/sysmodules/rosalina/source/gdb/server.c b/sysmodules/rosalina/source/gdb/server.c index 847040a20..b437ce95a 100644 --- a/sysmodules/rosalina/source/gdb/server.c +++ b/sysmodules/rosalina/source/gdb/server.c @@ -199,6 +199,7 @@ int GDB_CloseClient(GDBContext *ctx) ctx->state = GDB_STATE_DISCONNECTED; ctx->catchThreadEvents = false; + ctx->multiprocessExtEnabled = false; memset(&ctx->latestDebugEvent, 0, sizeof(DebugEventInfo)); memset(ctx->memoryOsInfoXmlData, 0, sizeof(ctx->memoryOsInfoXmlData)); diff --git a/sysmodules/rosalina/source/gdb/thread.c b/sysmodules/rosalina/source/gdb/thread.c index 985a9c1c8..4fa272bc0 100644 --- a/sysmodules/rosalina/source/gdb/thread.c +++ b/sysmodules/rosalina/source/gdb/thread.c @@ -10,6 +10,79 @@ #include "fmt.h" #include +const char *GDB_ParseThreadId(GDBContext *ctx, u32 *outPid, u32 *outTid, const char *str, char lastSep) +{ + const char *pos = str; + u32 pid = ctx->pid; + u32 tid = 0; + + // pPID.TID + if (ctx->multiprocessExtEnabled) + { + // Check for 'p' + if (*pos++ != 'p') + return NULL; + // -1 pid? + if (strncmp(pos, "-1.", 3) == 0) + { + pid = (u32)-1; // Should encode as 0 but oh well + pos += 3; + } + else + { + pos = GDB_ParseHexIntegerList(&pid, pos, 1, '.'); + if (pos == NULL) + return NULL; + pid = GDB_ConvertToRealPid(pid); + ++pos; + } + } + + // Fallthrough + // TID + if (strncmp(pos, "-1", 2) == 0) + { + tid = 0; // TID 0 is always invalid + pos += 2; + } + else + { + if (pid == (u32)-1) + return NULL; // this is never allowed + + pos = GDB_ParseHexIntegerList(&tid, pos, 1, lastSep); + if (pos == NULL) + return NULL; + } + + if (pos != NULL) + { + *outPid = pid; + *outTid = tid; + } + return pos; +} + +u32 GDB_ParseDecodeSingleThreadId(GDBContext *ctx, const char *str, char lastSep) +{ + u32 pid, tid; + if (GDB_ParseThreadId(ctx, &pid, &tid, str, lastSep) == NULL) + return 0; + + if (pid != ctx->pid) + return 0; + + return tid; +} + +int GDB_EncodeThreadId(GDBContext *ctx, char *outbuf, u32 tid) +{ + if (ctx->multiprocessExtEnabled) + return sprintf(outbuf, "p%lx.%lx", GDB_ConvertFromRealPid(ctx->pid), tid); + else + return sprintf(outbuf, "%lx", tid); +} + static s32 GDB_GetDynamicThreadPriority(GDBContext *ctx, u32 threadId) { Handle process, thread; @@ -95,27 +168,25 @@ GDB_DECLARE_HANDLER(SetThreadId) { if(ctx->commandData[0] == 'g') { - if(strncmp(ctx->commandData + 1, "-1", 2) == 0) - return GDB_ReplyErrno(ctx, EILSEQ); // a thread must be specified - - u32 id; - if(GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL) + u32 tid = GDB_ParseDecodeSingleThreadId(ctx, ctx->commandData + 1, 0); + if (tid == 0) return GDB_ReplyErrno(ctx, EILSEQ); - ctx->selectedThreadId = id; + + ctx->selectedThreadId = tid; return GDB_ReplyOk(ctx); } else if(ctx->commandData[0] == 'c') { // We can't stop/continue particular threads (uncompliant behavior) - if(strncmp(ctx->commandData + 1, "-1", 2) == 0) - ctx->selectedThreadIdForContinuing = 0; - else - { - u32 id; - if(GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL) - return GDB_ReplyErrno(ctx, EILSEQ); - ctx->selectedThreadIdForContinuing = id; - } + + u32 pid, tid; + if (GDB_ParseThreadId(ctx, &pid, &tid, ctx->commandData + 1, 0) == NULL) + return GDB_ReplyErrno(ctx, EILSEQ); + + if (pid != ctx->pid) + return GDB_ReplyErrno(ctx, EPERM); + + ctx->selectedThreadIdForContinuing = tid; return GDB_ReplyOk(ctx); } @@ -125,14 +196,14 @@ GDB_DECLARE_HANDLER(SetThreadId) GDB_DECLARE_HANDLER(IsThreadAlive) { - u32 threadId; s64 dummy; u32 mask; - if(GDB_ParseHexIntegerList(&threadId, ctx->commandData, 1, 0) == NULL) + u32 tid = GDB_ParseDecodeSingleThreadId(ctx, ctx->commandData, 0); + if (tid == 0) return GDB_ReplyErrno(ctx, EILSEQ); - Result r = svcGetDebugThreadParam(&dummy, &mask, ctx->debug, threadId, DBGTHREAD_PARAMETER_SCHEDULING_MASK_LOW); + Result r = svcGetDebugThreadParam(&dummy, &mask, ctx->debug, tid, DBGTHREAD_PARAMETER_SCHEDULING_MASK_LOW); if(R_SUCCEEDED(r) && mask != 2) return GDB_ReplyOk(ctx); else @@ -144,7 +215,9 @@ GDB_DECLARE_QUERY_HANDLER(CurrentThreadId) if(ctx->currentThreadId == 0) ctx->currentThreadId = GDB_GetCurrentThread(ctx); - return ctx->currentThreadId != 0 ? GDB_SendFormattedPacket(ctx, "QC%lx", ctx->currentThreadId) : GDB_ReplyErrno(ctx, EPERM); + char buf[32]; + GDB_EncodeThreadId(ctx, buf, ctx->currentThreadId); + return ctx->currentThreadId != 0 ? GDB_SendFormattedPacket(ctx, "QC%s", buf) : GDB_ReplyErrno(ctx, EPERM); } static void GDB_GenerateThreadListData(GDBContext *ctx) @@ -168,7 +241,11 @@ static void GDB_GenerateThreadListData(GDBContext *ctx) char *bufptr = ctx->threadListData; for(u32 i = 0; i < nbAliveThreads; i++) - bufptr += sprintf(bufptr, i == (nbAliveThreads - 1) ? "%lx" : "%lx,", aliveThreadIds[i]); + { + bufptr += GDB_EncodeThreadId(ctx, bufptr, aliveThreadIds[i]); + if (i < nbAliveThreads - 1) + *bufptr++ = ','; + } } static int GDB_SendThreadData(GDBContext *ctx) @@ -242,7 +319,8 @@ GDB_DECLARE_QUERY_HANDLER(ThreadExtraInfo) u32 tls = 0; - if(GDB_ParseHexIntegerList(&id, ctx->commandData, 1, 0) == NULL) + id = GDB_ParseDecodeSingleThreadId(ctx, ctx->commandData, 0); + if (id == 0) return GDB_ReplyErrno(ctx, EILSEQ); for(u32 i = 0; i < MAX_DEBUG_THREAD; i++) diff --git a/sysmodules/rosalina/source/gdb/verbose.c b/sysmodules/rosalina/source/gdb/verbose.c index a4a3edb1d..d251a1c31 100644 --- a/sysmodules/rosalina/source/gdb/verbose.c +++ b/sysmodules/rosalina/source/gdb/verbose.c @@ -22,6 +22,7 @@ static const struct { "File", GDB_VERBOSE_HANDLER(File) }, { "MustReplyEmpty", GDB_HANDLER(Unsupported) }, { "Run", GDB_VERBOSE_HANDLER(Run) }, + { "Kill", GDB_VERBOSE_HANDLER(Kill) }, }; GDB_DECLARE_HANDLER(VerboseCommand) diff --git a/sysmodules/rosalina/source/gdb/xfer.c b/sysmodules/rosalina/source/gdb/xfer.c index 2c3102d1e..bb3b7c8cb 100644 --- a/sysmodules/rosalina/source/gdb/xfer.c +++ b/sysmodules/rosalina/source/gdb/xfer.c @@ -9,6 +9,7 @@ #include "gdb/xfer.h" #include "gdb/net.h" +#include "gdb/thread.h" #include "fmt.h" #include "osdata_cfw_version_template_xml.h" @@ -161,7 +162,7 @@ GDB_DECLARE_XFER_OSDATA_HANDLER(Processes) memcpy(name, &out, 8); svcCloseHandle(processHandle); - n = sprintf(ctx->processesOsInfoXmlData + pos, item, pid, name); + n = sprintf(ctx->processesOsInfoXmlData + pos, item, GDB_ConvertFromRealPid(pid), name); pos += (u32)n; }