Skip to content
Permalink
Browse files

gdb: add support to run new processes

Only titleId [mediaType [launchFlags]] is supported, and the launched title shouldn't rely on APT and all 3 parameters should be hex-encoded.

usage example, with titleId+mediaType:
(gdb) set remote file-exec 0004013000003702
(gdb) r 0
  • Loading branch information...
TuxSH committed Apr 13, 2019
1 parent 763a1de commit e11cc090b27266e922db76cb7a7850d67bda8f18
@@ -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);
@@ -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);
@@ -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, ...);
@@ -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);
@@ -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)
@@ -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 <stdlib.h>
#include <signal.h>
#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;
}

@@ -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;

@@ -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)

0 comments on commit e11cc09

Please sign in to comment.
You can’t perform that action at this time.