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
This commit is contained in:
TuxSH
2019-04-13 19:18:47 +02:00
parent 763a1de8d3
commit e11cc090b2
12 changed files with 290 additions and 30 deletions

View File

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

View File

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

View File

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

View File

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

View File

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