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:
@@ -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)
|
||||
|
||||
@@ -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) },
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user