thermosphere: gdb: target xml + various refactoring

This commit is contained in:
TuxSH 2020-01-24 20:24:05 +00:00
parent 58d52675cd
commit 78723164c1
7 changed files with 143 additions and 586 deletions

View file

@ -99,6 +99,8 @@ typedef struct GDBContext
int latestSentPacketSize; int latestSentPacketSize;
char buffer[GDB_BUF_LEN + 4]; char buffer[GDB_BUF_LEN + 4];
char *workBuffer; char *workBuffer;
size_t targetXmlLen;
} GDBContext; } GDBContext;
typedef int (*GDBCommandHandler)(GDBContext *ctx); typedef int (*GDBCommandHandler)(GDBContext *ctx);

View file

@ -5,28 +5,24 @@
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) * SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/ */
#include "gdb/query.h" #include "../utils.h"
#include "gdb/xfer.h"
#include "gdb/thread.h"
#include "gdb/mem.h"
#include "gdb/net.h"
#include "gdb/remote_command.h"
typedef enum GDBQueryDirection #include "query.h"
{ #include "xfer.h"
#include "thread.h"
#include "mem.h"
#include "net.h"
#include "remote_command.h"
typedef enum GDBQueryDirection {
GDB_QUERY_DIRECTION_READ, GDB_QUERY_DIRECTION_READ,
GDB_QUERY_DIRECTION_WRITE GDB_QUERY_DIRECTION_WRITE
} GDBQueryDirection; } GDBQueryDirection;
// https://gcc.gnu.org/onlinedocs/gcc-5.3.0/cpp/Stringification.html
#define xstr(s) str(s)
#define str(s) #s
#define GDB_QUERY_HANDLER_LIST_ITEM_3(name, name2, direction) { name, GDB_QUERY_HANDLER(name2), GDB_QUERY_DIRECTION_##direction } #define GDB_QUERY_HANDLER_LIST_ITEM_3(name, name2, direction) { name, GDB_QUERY_HANDLER(name2), GDB_QUERY_DIRECTION_##direction }
#define GDB_QUERY_HANDLER_LIST_ITEM(name, direction) GDB_QUERY_HANDLER_LIST_ITEM_3(xstr(name), name, direction) #define GDB_QUERY_HANDLER_LIST_ITEM(name, direction) GDB_QUERY_HANDLER_LIST_ITEM_3(STRINGIZE(name), name, direction)
static const struct static const struct {
{
const char *name; const char *name;
GDBCommandHandler handler; GDBCommandHandler handler;
GDBQueryDirection direction; GDBQueryDirection direction;
@ -43,7 +39,6 @@ static const struct
GDB_QUERY_HANDLER_LIST_ITEM(GetTLSAddr, READ), GDB_QUERY_HANDLER_LIST_ITEM(GetTLSAddr, READ),
GDB_QUERY_HANDLER_LIST_ITEM_3("C", CurrentThreadId, READ), GDB_QUERY_HANDLER_LIST_ITEM_3("C", CurrentThreadId, READ),
GDB_QUERY_HANDLER_LIST_ITEM_3("Search", SearchMemory, READ), GDB_QUERY_HANDLER_LIST_ITEM_3("Search", SearchMemory, READ),
GDB_QUERY_HANDLER_LIST_ITEM(CatchSyscalls, WRITE),
GDB_QUERY_HANDLER_LIST_ITEM(Rcmd, READ), GDB_QUERY_HANDLER_LIST_ITEM(Rcmd, READ),
}; };
@ -89,11 +84,12 @@ int GDB_HandleWriteQuery(GDBContext *ctx)
GDB_DECLARE_QUERY_HANDLER(Supported) GDB_DECLARE_QUERY_HANDLER(Supported)
{ {
// TODO!
return GDB_SendFormattedPacket(ctx, return GDB_SendFormattedPacket(ctx,
"PacketSize=%x;" "PacketSize=%x;"
"qXfer:features:read+;qXfer:osdata:read+;" "qXfer:features:read+;qXfer:osdata:read+;"
"QStartNoAckMode+;QThreadEvents+;QCatchSyscalls+;" "QStartNoAckMode+;QThreadEvents+"
"vContSupported+;swbreak+", "vContSupported+;swbreak+;hwbreak+",
GDB_BUF_LEN // should have been sizeof(ctx->buffer) but GDB memory functions are bugged GDB_BUF_LEN // should have been sizeof(ctx->buffer) but GDB memory functions are bugged
); );
@ -107,40 +103,5 @@ GDB_DECLARE_QUERY_HANDLER(StartNoAckMode)
GDB_DECLARE_QUERY_HANDLER(Attached) GDB_DECLARE_QUERY_HANDLER(Attached)
{ {
return GDB_SendPacket(ctx, (ctx->flags & GDB_FLAG_CREATED) ? "0" : "1", 1); return GDB_SendPacket(ctx, "1", 1);
}
GDB_DECLARE_QUERY_HANDLER(CatchSyscalls)
{
if(ctx->commandData[0] == '0')
{
memset(ctx->svcMask, 0, 32);
return R_SUCCEEDED(svcKernelSetState(0x10002, ctx->pid, false)) ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, EPERM);
}
else if(ctx->commandData[0] == '1')
{
if(ctx->commandData[1] == ';')
{
u32 id;
const char *pos = ctx->commandData + 1;
memset(ctx->svcMask, 0, 32);
do
{
pos = GDB_ParseHexIntegerList(&id, pos + 1, 1, ';');
if(pos == NULL)
return GDB_ReplyErrno(ctx, EILSEQ);
if(id < 0xFE)
ctx->svcMask[id / 32] |= 1 << (31 - (id % 32));
}
while(*pos != 0);
}
else
memset(ctx->svcMask, 0xFF, 32);
return R_SUCCEEDED(svcKernelSetState(0x10002, ctx->pid, true, ctx->svcMask)) ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, EPERM);
}
else
return GDB_ReplyErrno(ctx, EILSEQ);
} }

View file

@ -5,330 +5,43 @@
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) * SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/ */
#include "gdb/remote_command.h" #include <string.h>
#include "gdb/net.h"
#include "csvc.h"
#include "fmt.h"
#include "gdb/breakpoints.h"
#include "remote_command.h"
#include "net.h"
struct struct
{ {
const char *name; const char *name;
GDBCommandHandler handler; GDBCommandHandler handler;
} remoteCommandHandlers[] = } remoteCommandHandlers[] = {
{
{ "syncrequestinfo" , GDB_REMOTE_COMMAND_HANDLER(SyncRequestInfo) },
{ "translatehandle" , GDB_REMOTE_COMMAND_HANDLER(TranslateHandle) },
{ "getmmuconfig" , GDB_REMOTE_COMMAND_HANDLER(GetMmuConfig) },
{ "getmemregions" , GDB_REMOTE_COMMAND_HANDLER(GetMemRegions) },
{ "flushcaches" , GDB_REMOTE_COMMAND_HANDLER(FlushCaches) },
{ "toggleextmemaccess", GDB_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess) },
}; };
static const char *GDB_SkipSpaces(const char *pos) static const char *GDB_SkipSpaces(const char *pos)
{ {
const char *nextpos; const char *nextpos;
for(nextpos = pos; *nextpos != 0 && ((*nextpos >= 9 && *nextpos <= 13) || *nextpos == ' '); nextpos++); for (nextpos = pos; *nextpos != 0 && ((*nextpos >= 9 && *nextpos <= 13) || *nextpos == ' '); nextpos++);
return nextpos; return nextpos;
} }
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo)
{
char outbuf[GDB_BUF_LEN / 2 + 1];
Result r;
int n;
if(ctx->commandData[0] != 0)
return GDB_ReplyErrno(ctx, EILSEQ);
if(ctx->selectedThreadId == 0)
ctx->selectedThreadId = ctx->currentThreadId;
if(ctx->selectedThreadId == 0)
{
n = sprintf(outbuf, "Cannot run this command without a selected thread.\n");
goto end;
}
u32 id;
u32 cmdId;
ThreadContext regs;
u32 instr;
Handle process;
r = svcOpenProcess(&process, ctx->pid);
if(R_FAILED(r))
{
n = sprintf(outbuf, "Invalid process (wtf?)\n");
goto end;
}
for(id = 0; id < MAX_DEBUG_THREAD && ctx->threadInfos[id].id != ctx->selectedThreadId; id++);
r = svcGetDebugThreadContext(&regs, ctx->debug, ctx->selectedThreadId, THREADCONTEXT_CONTROL_CPU_REGS);
if(R_FAILED(r) || id == MAX_DEBUG_THREAD)
{
n = sprintf(outbuf, "Invalid or running thread.\n");
goto end;
}
r = svcReadProcessMemory(&cmdId, ctx->debug, ctx->threadInfos[id].tls + 0x80, 4);
if(R_FAILED(r))
{
n = sprintf(outbuf, "Invalid or running thread.\n");
goto end;
}
r = svcReadProcessMemory(&instr, ctx->debug, regs.cpu_registers.pc, (regs.cpu_registers.cpsr & 0x20) ? 2 : 4);
if(R_SUCCEEDED(r) && (((regs.cpu_registers.cpsr & 0x20) && instr == BREAKPOINT_INSTRUCTION_THUMB) || instr == BREAKPOINT_INSTRUCTION_ARM))
{
u32 savedInstruction;
if(GDB_GetBreakpointInstruction(&savedInstruction, ctx, regs.cpu_registers.pc) == 0)
instr = savedInstruction;
}
if(R_FAILED(r) || ((regs.cpu_registers.cpsr & 0x20) && !(instr == 0xDF32 || (instr == 0xDFFE && regs.cpu_registers.r[12] == 0x32)))
|| (!(regs.cpu_registers.cpsr & 0x20) && !(instr == 0xEF000032 || (instr == 0xEF0000FE && regs.cpu_registers.r[12] == 0x32))))
{
n = sprintf(outbuf, "The selected thread is not currently performing a sync request (svc 0x32).\n");
goto end;
}
char name[12];
Handle handle;
r = svcCopyHandle(&handle, CUR_PROCESS_HANDLE, (Handle)regs.cpu_registers.r[0], process);
if(R_FAILED(r))
{
n = sprintf(outbuf, "Invalid handle.\n");
goto end;
}
r = svcControlService(SERVICEOP_GET_NAME, name, handle);
if(R_FAILED(r))
name[0] = 0;
n = sprintf(outbuf, "%s 0x%lx, 0x%08lx\n", name, cmdId, ctx->threadInfos[id].tls + 0x80);
end:
svcCloseHandle(handle);
svcCloseHandle(process);
return GDB_SendHexPacket(ctx, outbuf, n);
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle)
{
bool ok;
u32 val;
char *end;
int n;
Result r;
u32 kernelAddr;
Handle handle, process;
s64 refcountRaw;
u32 refcount;
char classBuf[32], serviceBuf[12] = { 0 };
char outbuf[GDB_BUF_LEN / 2 + 1];
if(ctx->commandData[0] == 0)
return GDB_ReplyErrno(ctx, EILSEQ);
val = xstrtoul(ctx->commandData, &end, 0, true, &ok);
if(!ok)
return GDB_ReplyErrno(ctx, EILSEQ);
end = (char *)GDB_SkipSpaces(end);
if(*end != 0)
return GDB_ReplyErrno(ctx, EILSEQ);
r = svcOpenProcess(&process, ctx->pid);
if(R_FAILED(r))
{
n = sprintf(outbuf, "Invalid process (wtf?)\n");
goto end;
}
r = svcCopyHandle(&handle, CUR_PROCESS_HANDLE, (Handle)val, process);
if(R_FAILED(r))
{
n = sprintf(outbuf, "Invalid handle.\n");
goto end;
}
svcTranslateHandle(&kernelAddr, classBuf, handle);
svcGetHandleInfo(&refcountRaw, handle, 1);
svcControlService(SERVICEOP_GET_NAME, serviceBuf, handle);
refcount = (u32)(refcountRaw - 1);
if(serviceBuf[0] != 0)
n = sprintf(outbuf, "(%s *)0x%08lx /* %s handle, %lu %s */\n", classBuf, kernelAddr, serviceBuf, refcount, refcount == 1 ? "reference" : "references");
else
n = sprintf(outbuf, "(%s *)0x%08lx /* %lu %s */\n", classBuf, kernelAddr, refcount, refcount == 1 ? "reference" : "references");
end:
svcCloseHandle(handle);
svcCloseHandle(process);
return GDB_SendHexPacket(ctx, outbuf, n);
}
extern bool isN3DS;
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig)
{
int n;
char outbuf[GDB_BUF_LEN / 2 + 1];
Result r;
Handle process;
if(ctx->commandData[0] != 0)
return GDB_ReplyErrno(ctx, EILSEQ);
r = svcOpenProcess(&process, ctx->pid);
if(R_FAILED(r))
n = sprintf(outbuf, "Invalid process (wtf?)\n");
else
{
s64 TTBCR, TTBR0;
svcGetSystemInfo(&TTBCR, 0x10002, 0);
svcGetProcessInfo(&TTBR0, process, 0x10008);
n = sprintf(outbuf, "TTBCR = %lu\nTTBR0 = 0x%08lx\nTTBR1 =", (u32)TTBCR, (u32)TTBR0);
for(u32 i = 0; i < (isN3DS ? 4 : 2); i++)
{
s64 TTBR1;
svcGetSystemInfo(&TTBR1, 0x10002, 1 + i);
if(i == (isN3DS ? 3 : 1))
n += sprintf(outbuf + n, " 0x%08lx\n", (u32)TTBR1);
else
n += sprintf(outbuf + n, " 0x%08lx /", (u32)TTBR1);
}
svcCloseHandle(process);
}
return GDB_SendHexPacket(ctx, outbuf, n);
}
static const char *FormatMemPerm(u32 perm)
{
if (perm == MEMPERM_DONTCARE)
return "???";
static char buf[4] = {0};
buf[0] = perm & MEMPERM_READ ? 'r' : '-';
buf[1] = perm & MEMPERM_WRITE ? 'w' : '-';
buf[2] = perm & MEMPERM_EXECUTE ? 'x' : '-';
return buf;
}
static const char *FormatMemState(u32 state)
{
if (state > 11)
return "Unknown";
static const char *states[12] =
{
"Free",
"Reserved",
"IO",
"Static",
"Code",
"Private",
"Shared",
"Continuous",
"Aliased",
"Alias",
"AliasCode",
"Locked"
};
return states[state];
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions)
{
u32 address = 0;
u32 posInBuffer = 0;
u32 maxPosInBuffer = GDB_BUF_LEN / 2 - 35; ///< 35 is the maximum length of a formatted region
Handle handle;
MemInfo memi;
PageInfo pagei;
char outbuf[GDB_BUF_LEN / 2 + 1];
if(R_FAILED(svcOpenProcess(&handle, ctx->pid)))
{
posInBuffer = sprintf(outbuf, "Invalid process (wtf?)\n");
goto end;
}
while (address < 0x40000000 ///< Limit to check for regions
&& posInBuffer < maxPosInBuffer
&& R_SUCCEEDED(svcQueryProcessMemory(&memi, &pagei, handle, address)))
{
// Update the address for next region
address = memi.base_addr + memi.size;
// If region isn't FREE then add it to the list
if (memi.state != MEMSTATE_FREE)
{
const char *perm = FormatMemPerm(memi.perm);
const char *state = FormatMemState(memi.state);
posInBuffer += sprintf(outbuf + posInBuffer, "%08lx - %08lx %s %s\n",
memi.base_addr, address, perm, state);
}
}
svcCloseHandle(handle);
end:
return GDB_SendHexPacket(ctx, outbuf, posInBuffer);
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(FlushCaches)
{
if(ctx->commandData[0] != 0)
return GDB_ReplyErrno(ctx, EILSEQ);
svcFlushEntireDataCache();
svcInvalidateEntireInstructionCache();
return GDB_ReplyOk(ctx);
}
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess)
{
int n;
char outbuf[GDB_BUF_LEN / 2 + 1];
ctx->enableExternalMemoryAccess = !ctx->enableExternalMemoryAccess;
n = sprintf(outbuf, "External memory access %s successfully.\n", ctx->enableExternalMemoryAccess ? "enabled" : "disabled");
return GDB_SendHexPacket(ctx, outbuf, n);
}
GDB_DECLARE_QUERY_HANDLER(Rcmd) GDB_DECLARE_QUERY_HANDLER(Rcmd)
{ {
char commandData[GDB_BUF_LEN / 2 + 1]; char commandData[GDB_BUF_LEN / 2 + 1];
char *endpos; char *endpos;
const char *errstr = "Unrecognized command.\n"; const char *errstr = "Unrecognized command.\n";
u32 len = strlen(ctx->commandData); size_t len = strlen(ctx->commandData);
if(len == 0 || (len % 2) == 1 || GDB_DecodeHex(commandData, ctx->commandData, len / 2) != len / 2) if(len == 0 || (len % 2) == 1 || GDB_DecodeHex(commandData, ctx->commandData, len / 2) != len / 2) {
return GDB_ReplyErrno(ctx, EILSEQ); return GDB_ReplyErrno(ctx, EILSEQ);
}
commandData[len / 2] = 0; commandData[len / 2] = 0;
for(endpos = commandData; !(*endpos >= 9 && *endpos <= 13) && *endpos != ' ' && *endpos != 0; endpos++); for (endpos = commandData; !(*endpos >= 9 && *endpos <= 13) && *endpos != ' ' && *endpos != 0; endpos++);
char *nextpos = (char *)GDB_SkipSpaces(endpos); char *nextpos = (char *)GDB_SkipSpaces(endpos);
*endpos = 0; *endpos = 0;
for(u32 i = 0; i < sizeof(remoteCommandHandlers) / sizeof(remoteCommandHandlers[0]); i++) for (size_t i = 0; i < sizeof(remoteCommandHandlers) / sizeof(remoteCommandHandlers[0]); i++) {
{ if (strcmp(commandData, remoteCommandHandlers[i].name) == 0) {
if(strcmp(commandData, remoteCommandHandlers[i].name) == 0)
{
ctx->commandData = nextpos; ctx->commandData = nextpos;
return remoteCommandHandlers[i].handler(ctx); return remoteCommandHandlers[i].handler(ctx);
} }

View file

@ -12,11 +12,4 @@
#define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name) #define GDB_REMOTE_COMMAND_HANDLER(name) GDB_HANDLER(RemoteCommand##name)
#define GDB_DECLARE_REMOTE_COMMAND_HANDLER(name) GDB_DECLARE_HANDLER(RemoteCommand##name) #define GDB_DECLARE_REMOTE_COMMAND_HANDLER(name) GDB_DECLARE_HANDLER(RemoteCommand##name)
GDB_DECLARE_REMOTE_COMMAND_HANDLER(SyncRequestInfo);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(TranslateHandle);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMmuConfig);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(GetMemRegions);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(FlushCaches);
GDB_DECLARE_REMOTE_COMMAND_HANDLER(ToggleExternalMemoryAccess);
GDB_DECLARE_QUERY_HANDLER(Rcmd); GDB_DECLARE_QUERY_HANDLER(Rcmd);

View file

@ -5,45 +5,39 @@
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) * SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/ */
#include "gdb/verbose.h" #include <string.h>
#include "gdb/net.h"
#include "gdb/debug.h"
#include "gdb/tio.h"
static const struct #include "verbose.h"
{ #include "net.h"
#include "debug.h"
static const struct {
const char *name; const char *name;
GDBCommandHandler handler; GDBCommandHandler handler;
} gdbVerboseCommandHandlers[] = } gdbVerboseCommandHandlers[] = {
{
{ "Attach", GDB_VERBOSE_HANDLER(Attach) },
{ "Cont?", GDB_VERBOSE_HANDLER(ContinueSupported) }, { "Cont?", GDB_VERBOSE_HANDLER(ContinueSupported) },
{ "Cont", GDB_VERBOSE_HANDLER(Continue) }, { "Cont", GDB_VERBOSE_HANDLER(Continue) },
{ "File", GDB_VERBOSE_HANDLER(File) },
{ "MustReplyEmpty", GDB_HANDLER(Unsupported) }, { "MustReplyEmpty", GDB_HANDLER(Unsupported) },
{ "Run", GDB_VERBOSE_HANDLER(Run) },
}; };
GDB_DECLARE_HANDLER(VerboseCommand) GDB_DECLARE_HANDLER(VerboseCommand)
{ {
char *nameBegin = ctx->commandData; // w/o leading 'v' char *nameBegin = ctx->commandData; // w/o leading 'v'
if(*nameBegin == 0) if (*nameBegin == 0) {
return GDB_ReplyErrno(ctx, EILSEQ); return GDB_ReplyErrno(ctx, EILSEQ);
}
char *nameEnd; char *nameEnd;
char *vData = NULL; char *vData = NULL;
for(nameEnd = nameBegin; *nameEnd != 0 && *nameEnd != ';' && *nameEnd != ':'; nameEnd++); for (nameEnd = nameBegin; *nameEnd != 0 && *nameEnd != ';' && *nameEnd != ':'; nameEnd++);
if(*nameEnd != 0) if (*nameEnd != 0) {
{
*nameEnd = 0; *nameEnd = 0;
vData = nameEnd + 1; vData = nameEnd + 1;
} }
for(u32 i = 0; i < sizeof(gdbVerboseCommandHandlers) / sizeof(gdbVerboseCommandHandlers[0]); i++) for (size_t i = 0; i < sizeof(gdbVerboseCommandHandlers) / sizeof(gdbVerboseCommandHandlers[0]); i++) {
{ if (strcmp(gdbVerboseCommandHandlers[i].name, nameBegin) == 0) {
if(strcmp(gdbVerboseCommandHandlers[i].name, nameBegin) == 0)
{
ctx->commandData = vData; ctx->commandData = vData;
return gdbVerboseCommandHandlers[i].handler(ctx); return gdbVerboseCommandHandlers[i].handler(ctx);
} }
@ -54,5 +48,6 @@ GDB_DECLARE_HANDLER(VerboseCommand)
GDB_DECLARE_VERBOSE_HANDLER(ContinueSupported) GDB_DECLARE_VERBOSE_HANDLER(ContinueSupported)
{ {
return GDB_SendPacket(ctx, "vCont;c;C", 9); const char *supported = "vCont;c;C;s;S;t;r";
return GDB_SendPacket(ctx, supported, strlen(supported));
} }

View file

@ -5,249 +5,151 @@
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later) * SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
*/ */
#include <3ds/os.h> #include <string.h>
#include <stdio.h>
#include "gdb/xfer.h" #include "../utils.h"
#include "gdb/net.h"
#include "fmt.h"
#include "osdata_cfw_version_template_xml.h" #include "xfer.h"
#include "osdata_memory_template_xml.h" #include "net.h"
#include "osdata_xml.h"
#include "target_xml.h"
struct
{ struct {
const char *name; const char *name;
int (*handler)(GDBContext *ctx, bool write, const char *annex, u32 offset, u32 length); int (*handler)(GDBContext *ctx, bool write, const char *annex, size_t offset, size_t length);
} xferCommandHandlers[] = } xferCommandHandlers[] = {
{
{ "features", GDB_XFER_HANDLER(Features) }, { "features", GDB_XFER_HANDLER(Features) },
{ "osdata", GDB_XFER_HANDLER(OsData) },
}; };
static void GDB_GenerateTargetXml(char *buf)
{
int pos;
const char *hdr = "<?xml version=\"1.0\"?><!DOCTYPE feature SYSTEM \"gdb-target.dtd\">";
const char *cpuDescBegin = "<feature name=\"org.gnu.gdb.aarch64.core\">";
const char *cpuDescEnd =
"<reg name=\"sp\" bitsize=\"64\" type=\"data_ptr\"/><reg name=\"pc\""
"bitsize=\"64\" type=\"code_ptr\"/><reg name=\"cpsr\" bitsize=\"32\"/></feature>";
const char *fpuDescBegin =
"<feature name=\"org.gnu.gdb.aarch64.fpu\"><vector id=\"v2d\" type=\"ieee_double\" count=\"2\"/>"
"<vector id=\"v2u\" type=\"uint64\" count=\"2\"/><vector id=\"v2i\" type=\"int64\" count=\"2\"/>"
"<vector id=\"v4f\" type=\"ieee_single\" count=\"4\"/><vector id=\"v4u\" type=\"uint32\" count=\"4\"/>"
"<vector id=\"v4i\" type=\"int32\" count=\"4\"/><vector id=\"v8u\" type=\"uint16\" count=\"8\"/>"
"<vector id=\"v8i\" type=\"int16\" count=\"8\"/><vector id=\"v16u\" type=\"uint8\" count=\"16\"/>"
"<vector id=\"v16i\" type=\"int8\" count=\"16\"/><vector id=\"v1u\" type=\"uint128\" count=\"1\"/>"
"<vector id=\"v1i\" type=\"int128\" count=\"1\"/><union id=\"vnd\"><field name=\"f\" type=\"v2d\"/>"
"<field name=\"u\" type=\"v2u\"/><field name=\"s\" type=\"v2i\"/></union><union id=\"vns\">"
"<field name=\"f\" type=\"v4f\"/><field name=\"u\" type=\"v4u\"/><field name=\"s\" type=\"v4i\"/></union>"
"<union id=\"vnh\"><field name=\"u\" type=\"v8u\"/><field name=\"s\" type=\"v8i\"/></union><union id=\"vnb\">"
"<field name=\"u\" type=\"v16u\"/><field name=\"s\" type=\"v16i\"/></union><union id=\"vnq\">"
"<field name=\"u\" type=\"v1u\"/><field name=\"s\" type=\"v1i\"/></union><union id=\"aarch64v\">"
"<field name=\"d\" type=\"vnd\"/><field name=\"s\" type=\"vns\"/><field name=\"h\" type=\"vnh\"/>"
"<field name=\"b\" type=\"vnb\"/><field name=\"q\" type=\"vnq\"/></union>";
const char *fpuDescEnd = "<reg name=\"fpsr\" bitsize=\"32\"/>\r\n <reg name=\"fpcr\" bitsize=\"32\"/>\r\n</feature>";
const char *footer = "</target>";
strcpy(buf, hdr);
// CPU registers
strcat(buf, cpuDescBegin);
pos = (int)strlen(buf);
for (u32 i = 0; i < 31; i++) {
pos += sprintf(buf + pos, "<reg name=\"x%u\" bitsize=\"64\"/>", i);
}
strcat(buf, cpuDescEnd);
strcat(buf, fpuDescBegin);
pos = (int)strlen(buf);
for (u32 i = i; i < 32; i++) {
pos += sprintf(buf + pos, "<reg name=\"v%u\" bitsize=\"128\" type=\"aarch64v\"/>", i);
}
strcat(buf, fpuDescEnd);
strcat(buf, footer);
}
GDB_DECLARE_XFER_HANDLER(Features) GDB_DECLARE_XFER_HANDLER(Features)
{ {
if(strcmp(annex, "target.xml") != 0 || write) if(strcmp(annex, "target.xml") != 0 || write) {
return GDB_ReplyEmpty(ctx); return GDB_ReplyEmpty(ctx);
else
return GDB_SendStreamData(ctx, (const char *)target_xml, offset, length, target_xml_size, false);
}
struct
{
const char *name;
int (*handler)(GDBContext *ctx, bool write, u32 offset, u32 length);
} xferOsDataCommandHandlers[] =
{
{ "cfwversion", GDB_XFER_OSDATA_HANDLER(CfwVersion) },
{ "memory", GDB_XFER_OSDATA_HANDLER(Memory) },
{ "processes", GDB_XFER_OSDATA_HANDLER(Processes) },
};
GDB_DECLARE_XFER_OSDATA_HANDLER(CfwVersion)
{
if(write)
return GDB_HandleUnsupported(ctx);
else
{
char buf[512]; // Make sure this doesn't overflow
char versionString[16];
s64 out;
u32 version, commitHash;
bool isRelease;
u32 sz;
svcGetSystemInfo(&out, 0x10000, 0);
version = (u32)out;
svcGetSystemInfo(&out, 0x10000, 1);
commitHash = (u32)out;
svcGetSystemInfo(&out, 0x10000, 0x200);
isRelease = (bool)out;
if(GET_VERSION_REVISION(version) == 0)
sprintf(versionString, "v%lu.%lu", GET_VERSION_MAJOR(version), GET_VERSION_MINOR(version));
else
sprintf(versionString, "v%lu.%lu.%lu", GET_VERSION_MAJOR(version), GET_VERSION_MINOR(version), GET_VERSION_REVISION(version));
sz = (u32)sprintf(buf, (const char *)osdata_cfw_version_template_xml, versionString, commitHash, isRelease ? "Yes" : "No");
return GDB_SendStreamData(ctx, buf, offset, length, sz, false);
}
}
GDB_DECLARE_XFER_OSDATA_HANDLER(Memory)
{
if(write)
return GDB_HandleUnsupported(ctx);
else
{
if(ctx->memoryOsInfoXmlData[0] == 0)
{
s64 out;
u32 applicationUsed, systemUsed, baseUsed;
u32 applicationTotal = *(vu32 *)0x1FF80040, systemTotal = *(vu32 *)0x1FF80044, baseTotal = *(vu32 *)0x1FF80048;
svcGetSystemInfo(&out, 0, 1);
applicationUsed = (u32)out;
svcGetSystemInfo(&out, 0, 2);
systemUsed = (u32)out;
svcGetSystemInfo(&out, 0, 3);
baseUsed = (u32)out;
sprintf(ctx->memoryOsInfoXmlData, (const char *)osdata_memory_template_xml,
applicationUsed, applicationTotal - applicationUsed, applicationTotal, (u32)((5ULL + ((1000ULL * applicationUsed) / applicationTotal)) / 10ULL),
systemUsed, systemTotal - systemUsed, systemTotal, (u32)((5ULL + ((1000ULL * systemUsed) / systemTotal)) / 10ULL),
baseUsed, baseTotal - baseUsed, baseTotal, (u32)((5ULL + ((1000ULL * baseUsed) / baseTotal)) / 10ULL)
);
} }
u32 size = strlen(ctx->memoryOsInfoXmlData); // Generate the target xml on-demand
int n = GDB_SendStreamData(ctx, ctx->memoryOsInfoXmlData, offset, length, size, false); // This is a bit whack, we rightfully assume that GDB won't sent any other command during the stream transfer
if (ctx->targetXmlLen == 0) {
GDB_GenerateTargetXml(ctx->workBuffer);
ctx->targetXmlLen = strlen(ctx->workBuffer);
}
if(offset + length >= size) int n = GDB_SendStreamData(ctx, ctx->workBuffer, offset, length, ctx->targetXmlLen, false);
ctx->memoryOsInfoXmlData[0] = 0; // we're done, invalidate
// Transfer ended
if(offset + length >= ctx->targetXmlLen) {
ctx->targetXmlLen = 0;
}
return n; return n;
}
}
GDB_DECLARE_XFER_OSDATA_HANDLER(Processes)
{
if(write)
return GDB_HandleUnsupported(ctx);
else
{
if(ctx->processesOsInfoXmlData[0] == 0)
{
static const char header[] =
/*"<?xml version=\"1.0\"?>"
"<!DOCTYPE target SYSTEM \"osdata.dtd\">" IDA rejects the xml header*/
"<osdata type=\"processes\">";
static const char item[] =
"<item>"
"<column name=\"pid\">%lu</column>"
"<column name=\"command\">%s</column>"
"</item>";
static const char footer[] = "</osdata>";
int n;
u32 pos = 0;
u32 pidList[0x40];
s32 processAmount;
strcpy(ctx->processesOsInfoXmlData, header);
pos = sizeof(header) - 1;
svcGetProcessList(&processAmount, pidList, 0x40);
for(s32 i = 0; i < processAmount; i++)
{
u32 pid = pidList[i];
char name[9] = { 0 };
s64 out;
Handle processHandle;
Result res = svcOpenProcess(&processHandle, pidList[i]);
if(R_FAILED(res))
continue;
svcGetProcessInfo(&out, processHandle, 0x10000);
memcpy(name, &out, 8);
svcCloseHandle(processHandle);
n = sprintf(ctx->processesOsInfoXmlData + pos, item, pid, name);
pos += (u32)n;
}
strcpy(ctx->processesOsInfoXmlData + pos, footer);
pos = sizeof(footer) - 1;
}
u32 size = strlen(ctx->processesOsInfoXmlData);
int n = GDB_SendStreamData(ctx, ctx->processesOsInfoXmlData, offset, length, size, false);
if(offset + length >= size)
ctx->processesOsInfoXmlData[0] = 0; // we're done, invalidate
return n;
}
}
GDB_DECLARE_XFER_HANDLER(OsData)
{
if(strcmp(annex, "") == 0 && !write)
return GDB_SendStreamData(ctx, (const char *)osdata_xml, offset, length, osdata_xml_size, false);
else
{
for(u32 i = 0; i < sizeof(xferOsDataCommandHandlers) / sizeof(xferOsDataCommandHandlers[0]); i++)
{
if(strcmp(annex, xferOsDataCommandHandlers[i].name) == 0)
return xferOsDataCommandHandlers[i].handler(ctx, write, offset, length);
}
}
return GDB_HandleUnsupported(ctx);
} }
GDB_DECLARE_QUERY_HANDLER(Xfer) GDB_DECLARE_QUERY_HANDLER(Xfer)
{ {
const char *objectStart = ctx->commandData; const char *objectStart = ctx->commandData;
char *objectEnd = (char*)strchr(objectStart, ':'); char *objectEnd = (char*)strchr(objectStart, ':');
if(objectEnd == NULL) return -1; if (objectEnd == NULL) {
return -1;
}
*objectEnd = 0; *objectEnd = 0;
char *opStart = objectEnd + 1; char *opStart = objectEnd + 1;
char *opEnd = (char*)strchr(opStart, ':'); char *opEnd = (char*)strchr(opStart, ':');
if(opEnd == NULL) return -1; if(opEnd == NULL) {
return -1;
}
*opEnd = 0; *opEnd = 0;
char *annexStart = opEnd + 1; char *annexStart = opEnd + 1;
char *annexEnd = (char*)strchr(annexStart, ':'); char *annexEnd = (char*)strchr(annexStart, ':');
if(annexEnd == NULL) return -1; if(annexEnd == NULL) {
return -1;
}
*annexEnd = 0; *annexEnd = 0;
const char *offStart = annexEnd + 1; const char *offStart = annexEnd + 1;
u32 offset, length; size_t offset, length;
bool write; bool write;
const char *pos; const char *pos;
if(strcmp(opStart, "read") == 0) if (strcmp(opStart, "read") == 0) {
{ unsigned int lst[2];
u32 lst[2]; if(GDB_ParseHexIntegerList(lst, offStart, 2, 0) == NULL) {
if(GDB_ParseHexIntegerList(lst, offStart, 2, 0) == NULL)
return GDB_ReplyErrno(ctx, EILSEQ); return GDB_ReplyErrno(ctx, EILSEQ);
}
offset = lst[0]; offset = lst[0];
length = lst[1]; length = lst[1];
write = false; write = false;
} } else if (strcmp(opStart, "write") == 0) {
else if(strcmp(opStart, "write") == 0)
{
pos = GDB_ParseHexIntegerList(&offset, offStart, 1, ':'); pos = GDB_ParseHexIntegerList(&offset, offStart, 1, ':');
if(pos == NULL || *pos++ != ':') if (pos == NULL || *pos++ != ':') {
return GDB_ReplyErrno(ctx, EILSEQ); return GDB_ReplyErrno(ctx, EILSEQ);
}
u32 len = strlen(pos); size_t len = strlen(pos);
if(len == 0 || (len % 2) != 0) if (len == 0 || (len % 2) != 0) {
return GDB_ReplyErrno(ctx, EILSEQ); return GDB_ReplyErrno(ctx, EILSEQ);
}
length = len / 2; length = len / 2;
write = true; write = true;
} } else {
else
return GDB_ReplyErrno(ctx, EILSEQ); return GDB_ReplyErrno(ctx, EILSEQ);
}
for(u32 i = 0; i < sizeof(xferCommandHandlers) / sizeof(xferCommandHandlers[0]); i++) for (size_t i = 0; i < sizeof(xferCommandHandlers) / sizeof(xferCommandHandlers[0]); i++) {
{ if (strcmp(objectStart, xferCommandHandlers[i].name) == 0) {
if(strcmp(objectStart, xferCommandHandlers[i].name) == 0) if(write) {
{
if(write)
ctx->commandData = (char *)pos; ctx->commandData = (char *)pos;
}
return xferCommandHandlers[i].handler(ctx, write, annexStart, offset, length); return xferCommandHandlers[i].handler(ctx, write, annexStart, offset, length);
} }

View file

@ -10,17 +10,8 @@
#include "gdb.h" #include "gdb.h"
#define GDB_XFER_HANDLER(name) GDB_HANDLER(Xfer##name) #define GDB_XFER_HANDLER(name) GDB_HANDLER(Xfer##name)
#define GDB_DECLARE_XFER_HANDLER(name) int GDB_XFER_HANDLER(name)(GDBContext *ctx, bool write, const char *annex, u32 offset, u32 length) #define GDB_DECLARE_XFER_HANDLER(name) int GDB_XFER_HANDLER(name)(GDBContext *ctx, bool write, const char *annex, size_t offset, size_t length)
#define GDB_XFER_OSDATA_HANDLER(name) GDB_XFER_HANDLER(OsData##name)
#define GDB_DECLARE_XFER_OSDATA_HANDLER(name) int GDB_XFER_OSDATA_HANDLER(name)(GDBContext *ctx, bool write, u32 offset, u32 length)
GDB_DECLARE_XFER_HANDLER(Features); GDB_DECLARE_XFER_HANDLER(Features);
GDB_DECLARE_XFER_OSDATA_HANDLER(CfwVersion);
GDB_DECLARE_XFER_OSDATA_HANDLER(Memory);
GDB_DECLARE_XFER_OSDATA_HANDLER(Processes);
GDB_DECLARE_XFER_HANDLER(OsData);
GDB_DECLARE_QUERY_HANDLER(Xfer); GDB_DECLARE_QUERY_HANDLER(Xfer);