Skip to content

Commit

Permalink
Restore extended-remote support & map <GDB PID>-><1 + 3DS PID> (break…
Browse files Browse the repository at this point in the history
…ing 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)
  • Loading branch information
TuxSH committed Apr 9, 2022
1 parent 2b5da40 commit 01ebbf1
Show file tree
Hide file tree
Showing 9 changed files with 227 additions and 38 deletions.
1 change: 1 addition & 0 deletions sysmodules/rosalina/include/gdb.h
Expand Up @@ -124,6 +124,7 @@ typedef struct GDBContext
Handle processAttachedEvent, continuedEvent;
Handle eventToWaitFor;

bool multiprocessExtEnabled;
bool catchThreadEvents;
bool processEnded, processExited;

Expand Down
1 change: 1 addition & 0 deletions sysmodules/rosalina/include/gdb/debug.h
Expand Up @@ -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);
Expand Down
14 changes: 14 additions & 0 deletions sysmodules/rosalina/include/gdb/thread.h
Expand Up @@ -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);

Expand Down
82 changes: 66 additions & 16 deletions sysmodules/rosalina/source/gdb/debug.c
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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))
Expand Down Expand Up @@ -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)
{
Expand All @@ -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 != (u32)-1 && 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)
Expand All @@ -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);
}
Expand All @@ -287,7 +329,10 @@ static int GDB_ParseCommonThreadInfo(char *out, GDBContext *ctx, int sig)
s64 dummy;
u32 core;
Result r = svcGetDebugThreadContext(&regs, 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;
Expand Down Expand Up @@ -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:
Expand Down
15 changes: 14 additions & 1 deletion sysmodules/rosalina/source/gdb/query.c
Expand Up @@ -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"
Expand Down Expand Up @@ -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
);
Expand Down
1 change: 1 addition & 0 deletions sysmodules/rosalina/source/gdb/server.c
Expand Up @@ -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));
Expand Down

0 comments on commit 01ebbf1

Please sign in to comment.