Skip to content

Commit 4e9c7ef

Browse files
committed
Restore extended-remote support & map <GDB PID>-><1 + 3DS PID> (breaking 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)
1 parent 2b5da40 commit 4e9c7ef

File tree

9 files changed

+199
-39
lines changed

9 files changed

+199
-39
lines changed

sysmodules/rosalina/include/gdb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ typedef struct GDBContext
124124
Handle processAttachedEvent, continuedEvent;
125125
Handle eventToWaitFor;
126126

127+
bool multiprocessExtEnabled;
127128
bool catchThreadEvents;
128129
bool processEnded, processExited;
129130

sysmodules/rosalina/include/gdb/debug.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ GDB_DECLARE_HANDLER(Restart);
1414
GDB_DECLARE_VERBOSE_HANDLER(Attach);
1515
GDB_DECLARE_HANDLER(Detach);
1616
GDB_DECLARE_HANDLER(Kill);
17+
GDB_DECLARE_VERBOSE_HANDLER(Kill);
1718
GDB_DECLARE_HANDLER(Break);
1819
GDB_DECLARE_HANDLER(Continue);
1920
GDB_DECLARE_VERBOSE_HANDLER(Continue);

sysmodules/rosalina/include/gdb/thread.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@
99

1010
#include "gdb.h"
1111

12+
static inline u32 GDB_ConvertFromRealPid(u32 pid)
13+
{
14+
return pid + 1;
15+
}
16+
17+
static inline u32 GDB_ConvertToRealPid(u32 pid)
18+
{
19+
return pid - 1;
20+
}
21+
22+
const char *GDB_ParseThreadId(GDBContext *ctx, u32 *outPid, u32 *outTid, const char *str, char lastSep);
23+
u32 GDB_ParseDecodeSingleThreadId(GDBContext *ctx, const char *str, char lastSep);
24+
int GDB_EncodeThreadId(GDBContext *ctx, char *outbuf, u32 tid);
25+
1226
u32 GDB_GetCurrentThreadFromList(GDBContext *ctx, u32 *threadIds, u32 nbThreads);
1327
u32 GDB_GetCurrentThread(GDBContext *ctx);
1428

sysmodules/rosalina/source/gdb/debug.c

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ GDB_DECLARE_VERBOSE_HANDLER(Attach)
138138
return GDB_ReplyErrno(ctx, EILSEQ);
139139

140140
RecursiveLock_Lock(&ctx->lock);
141-
ctx->pid = pid;
141+
ctx->pid = GDB_ConvertToRealPid(pid);
142142
Result r = GDB_AttachToProcess(ctx);
143143
if(R_FAILED(r))
144144
GDB_DetachImmediatelyExtended(ctx);
@@ -154,6 +154,16 @@ GDB_DECLARE_VERBOSE_HANDLER(Attach)
154154

155155
GDB_DECLARE_HANDLER(Detach)
156156
{
157+
if (ctx->multiprocessExtEnabled)
158+
{
159+
//;pid
160+
u32 pid;
161+
if(ctx->commandData[0] != ';' || GDB_ParseHexIntegerList(&pid, ctx->commandData + 1, 1, 0) == NULL)
162+
return GDB_ReplyErrno(ctx, EILSEQ);
163+
pid = GDB_ConvertToRealPid(pid);
164+
if (pid != ctx->pid)
165+
return GDB_ReplyErrno(ctx, EPERM);
166+
}
157167
ctx->state = GDB_STATE_DETACHING;
158168
if (ctx->flags & GDB_FLAG_EXTENDED_REMOTE)
159169
GDB_DetachImmediatelyExtended(ctx);
@@ -170,6 +180,29 @@ GDB_DECLARE_HANDLER(Kill)
170180
return 0;
171181
}
172182

183+
GDB_DECLARE_VERBOSE_HANDLER(Kill)
184+
{
185+
if (ctx->multiprocessExtEnabled)
186+
{
187+
//;pid . ';' is already consumed by our caller
188+
u32 pid;
189+
if(GDB_ParseHexIntegerList(&pid, ctx->commandData, 1, 0) == NULL)
190+
return GDB_ReplyErrno(ctx, EILSEQ);
191+
pid = GDB_ConvertToRealPid(pid);
192+
if (pid != ctx->pid)
193+
return GDB_ReplyErrno(ctx, EPERM);
194+
}
195+
196+
int ret = GDB_ReplyOk(ctx);
197+
198+
ctx->state = GDB_STATE_DETACHING;
199+
ctx->flags |= GDB_FLAG_TERMINATE_PROCESS;
200+
if (ctx->flags & GDB_FLAG_EXTENDED_REMOTE)
201+
GDB_DetachImmediatelyExtended(ctx);
202+
203+
return ret;
204+
}
205+
173206
GDB_DECLARE_HANDLER(Break)
174207
{
175208
if(!(ctx->flags & GDB_FLAG_PROCESS_CONTINUING))
@@ -232,7 +265,7 @@ GDB_DECLARE_HANDLER(Continue)
232265

233266
GDB_DECLARE_VERBOSE_HANDLER(Continue)
234267
{
235-
char *pos = ctx->commandData;
268+
const char *pos = ctx->commandData;
236269
bool currentThreadFound = false;
237270
while(pos != NULL && *pos != 0 && !currentThreadFound)
238271
{
@@ -247,18 +280,21 @@ GDB_DECLARE_VERBOSE_HANDLER(Continue)
247280
break;
248281
}
249282

250-
char *nextpos = (char *)strchr(pos, ';');
251-
if(strncmp(pos, "-1", 2) == 0)
283+
u32 pid, tid;
284+
285+
const char *nextpos = GDB_ParseThreadId(ctx, &pid, &tid, pos, ';');
286+
if (nextpos == NULL)
287+
return GDB_ReplyErrno(ctx, EILSEQ);
288+
289+
if (tid == 0)
252290
currentThreadFound = true;
291+
if (pid != ctx->pid)
292+
return GDB_ReplyErrno(ctx, EPERM);
253293
else
254-
{
255-
u32 threadId;
256-
if(GDB_ParseHexIntegerList(&threadId, pos, 1, ';') == NULL)
257-
return GDB_ReplyErrno(ctx, EILSEQ);
258-
currentThreadFound = currentThreadFound || threadId == ctx->currentThreadId;
259-
}
294+
currentThreadFound = currentThreadFound || tid == ctx->currentThreadId;
260295

261-
pos = nextpos;
296+
if (nextpos != NULL && *nextpos != '\0')
297+
pos = nextpos + 1;
262298
}
263299

264300
if(ctx->currentThreadId == 0 || currentThreadFound)
@@ -269,12 +305,18 @@ GDB_DECLARE_VERBOSE_HANDLER(Continue)
269305

270306
GDB_DECLARE_HANDLER(GetStopReason)
271307
{
308+
char pidbuf[32];
309+
if (ctx->multiprocessExtEnabled && ctx->state == GDB_STATE_ATTACHED)
310+
sprintf(pidbuf, ";process:%lx", GDB_ConvertFromRealPid(ctx->pid));
311+
else
312+
pidbuf[0] = '\0';
313+
272314
if (ctx->processEnded && ctx->processExited) {
273-
return GDB_SendPacket(ctx, "W00", 3);
315+
return GDB_SendFormattedPacket(ctx, "W00%s", pidbuf);
274316
} else if (ctx->processEnded && !ctx->processExited) {
275-
return GDB_SendPacket(ctx, "X0f", 3);
317+
return GDB_SendFormattedPacket(ctx, "X0f%s", pidbuf);
276318
} else if (ctx->debug == 0) {
277-
return GDB_SendPacket(ctx, "W00", 3);
319+
return GDB_SendFormattedPacket(ctx, "W00%s", pidbuf);
278320
} else {
279321
return GDB_SendStopReply(ctx, &ctx->latestDebugEvent);
280322
}
@@ -287,7 +329,10 @@ static int GDB_ParseCommonThreadInfo(char *out, GDBContext *ctx, int sig)
287329
s64 dummy;
288330
u32 core;
289331
Result r = svcGetDebugThreadContext(&regs, ctx->debug, threadId, THREADCONTEXT_CONTROL_ALL);
290-
int n = sprintf(out, "T%02xthread:%lx;", sig, threadId);
332+
333+
char tidbuf[32];
334+
GDB_EncodeThreadId(ctx, tidbuf, ctx->currentThreadId);
335+
int n = sprintf(out, "T%02xthread:%s;", sig, tidbuf);
291336

292337
if(R_FAILED(r))
293338
return n;
@@ -450,7 +495,12 @@ int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info)
450495
{
451496
// exited (no error / unhandled exception), SIGTERM (process terminated) * 2
452497
static const char *processExitReplies[] = { "W00", "X0f", "X0f" };
453-
return GDB_SendPacket(ctx, processExitReplies[(u32)info->exit_process.reason], 3);
498+
char pidbuf[32];
499+
if (ctx->multiprocessExtEnabled && ctx->state == GDB_STATE_ATTACHED)
500+
sprintf(pidbuf, ";process:%lx", GDB_ConvertFromRealPid(ctx->pid));
501+
else
502+
pidbuf[0] = '\0';
503+
return GDB_SendFormattedPacket(ctx, "%s%s", processExitReplies[(u32)info->exit_process.reason], pidbuf);
454504
}
455505

456506
case DBGEVENT_EXCEPTION:

sysmodules/rosalina/source/gdb/query.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* SPDX-License-Identifier: (MIT OR GPL-2.0-or-later)
66
*/
77

8+
#define _GNU_SOURCE
89
#include "gdb/query.h"
910
#include "gdb/xfer.h"
1011
#include "gdb/thread.h"
@@ -89,11 +90,23 @@ int GDB_HandleWriteQuery(GDBContext *ctx)
8990

9091
GDB_DECLARE_QUERY_HANDLER(Supported)
9192
{
93+
const char *pos = ctx->commandData, *nextpos = pos;
94+
do
95+
{
96+
pos = nextpos;
97+
nextpos = strchrnul(pos, ';');
98+
if (*nextpos != ';' && *nextpos != '\0')
99+
return GDB_ReplyErrno(ctx, EILSEQ);
100+
101+
if (strncmp(pos, "multiprocess+", nextpos - pos) == 0)
102+
ctx->multiprocessExtEnabled = true;
103+
} while (*nextpos++ != '\0');
104+
92105
return GDB_SendFormattedPacket(ctx,
93106
"PacketSize=%x;"
94107
"qXfer:features:read+;qXfer:osdata:read+;"
95108
"QStartNoAckMode+;QThreadEvents+;QCatchSyscalls+;"
96-
"vContSupported+;swbreak+",
109+
"vContSupported+;swbreak+;multiprocess+",
97110

98111
GDB_BUF_LEN // should have been sizeof(ctx->buffer) but GDB memory functions are bugged
99112
);

sysmodules/rosalina/source/gdb/server.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ int GDB_CloseClient(GDBContext *ctx)
199199
ctx->state = GDB_STATE_DISCONNECTED;
200200

201201
ctx->catchThreadEvents = false;
202+
ctx->multiprocessExtEnabled = false;
202203

203204
memset(&ctx->latestDebugEvent, 0, sizeof(DebugEventInfo));
204205
memset(ctx->memoryOsInfoXmlData, 0, sizeof(ctx->memoryOsInfoXmlData));

sysmodules/rosalina/source/gdb/thread.c

Lines changed: 99 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,79 @@
1010
#include "fmt.h"
1111
#include <stdlib.h>
1212

13+
const char *GDB_ParseThreadId(GDBContext *ctx, u32 *outPid, u32 *outTid, const char *str, char lastSep)
14+
{
15+
const char *pos = str;
16+
u32 pid = ctx->pid;
17+
u32 tid = 0;
18+
19+
// pPID.TID
20+
if (ctx->multiprocessExtEnabled)
21+
{
22+
// Check for 'p'
23+
if (*pos++ != 'p')
24+
return NULL;
25+
// -1 pid?
26+
if (strncmp(pos, "-1.", 3) == 0)
27+
{
28+
pid = (u32)-1; // Should encode as 0 but oh well
29+
pos += 3;
30+
}
31+
else
32+
{
33+
pos = GDB_ParseHexIntegerList(&pid, pos, 1, '.');
34+
if (pos == NULL)
35+
return NULL;
36+
pid = GDB_ConvertToRealPid(pid);
37+
++pos;
38+
}
39+
}
40+
41+
// Fallthrough
42+
// TID
43+
if (strncmp(pos, "-1", 2) == 0)
44+
{
45+
tid = 0; // TID 0 is always invalid
46+
pos += 2;
47+
}
48+
else
49+
{
50+
if (pid == (u32)-1)
51+
return NULL; // this is never allowed
52+
53+
pos = GDB_ParseHexIntegerList(&tid, pos, 1, lastSep);
54+
if (pos == NULL)
55+
return NULL;
56+
}
57+
58+
if (pos != NULL)
59+
{
60+
*outPid = pid;
61+
*outTid = tid;
62+
}
63+
return pos;
64+
}
65+
66+
u32 GDB_ParseDecodeSingleThreadId(GDBContext *ctx, const char *str, char lastSep)
67+
{
68+
u32 pid, tid;
69+
if (GDB_ParseThreadId(ctx, &pid, &tid, str, lastSep) == NULL)
70+
return 0;
71+
72+
if (pid != ctx->pid)
73+
return 0;
74+
75+
return tid;
76+
}
77+
78+
int GDB_EncodeThreadId(GDBContext *ctx, char *outbuf, u32 tid)
79+
{
80+
if (ctx->multiprocessExtEnabled)
81+
return sprintf(outbuf, "p%lx.%lx", GDB_ConvertFromRealPid(ctx->pid), tid);
82+
else
83+
return sprintf(outbuf, "%lx", tid);
84+
}
85+
1386
static s32 GDB_GetDynamicThreadPriority(GDBContext *ctx, u32 threadId)
1487
{
1588
Handle process, thread;
@@ -95,27 +168,25 @@ GDB_DECLARE_HANDLER(SetThreadId)
95168
{
96169
if(ctx->commandData[0] == 'g')
97170
{
98-
if(strncmp(ctx->commandData + 1, "-1", 2) == 0)
99-
return GDB_ReplyErrno(ctx, EILSEQ); // a thread must be specified
100-
101-
u32 id;
102-
if(GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL)
171+
u32 tid = GDB_ParseDecodeSingleThreadId(ctx, ctx->commandData + 1, 0);
172+
if (tid == 0)
103173
return GDB_ReplyErrno(ctx, EILSEQ);
104-
ctx->selectedThreadId = id;
174+
175+
ctx->selectedThreadId = tid;
105176
return GDB_ReplyOk(ctx);
106177
}
107178
else if(ctx->commandData[0] == 'c')
108179
{
109180
// We can't stop/continue particular threads (uncompliant behavior)
110-
if(strncmp(ctx->commandData + 1, "-1", 2) == 0)
111-
ctx->selectedThreadIdForContinuing = 0;
112-
else
113-
{
114-
u32 id;
115-
if(GDB_ParseHexIntegerList(&id, ctx->commandData + 1, 1, 0) == NULL)
116-
return GDB_ReplyErrno(ctx, EILSEQ);
117-
ctx->selectedThreadIdForContinuing = id;
118-
}
181+
182+
u32 pid, tid;
183+
if (GDB_ParseThreadId(ctx, &pid, &tid, ctx->commandData + 1, 0) == NULL)
184+
return GDB_ReplyErrno(ctx, EILSEQ);
185+
186+
if (pid != ctx->pid)
187+
return GDB_ReplyErrno(ctx, EPERM);
188+
189+
ctx->selectedThreadIdForContinuing = tid;
119190

120191
return GDB_ReplyOk(ctx);
121192
}
@@ -125,14 +196,14 @@ GDB_DECLARE_HANDLER(SetThreadId)
125196

126197
GDB_DECLARE_HANDLER(IsThreadAlive)
127198
{
128-
u32 threadId;
129199
s64 dummy;
130200
u32 mask;
131201

132-
if(GDB_ParseHexIntegerList(&threadId, ctx->commandData, 1, 0) == NULL)
202+
u32 tid = GDB_ParseDecodeSingleThreadId(ctx, ctx->commandData, 0);
203+
if (tid == 0)
133204
return GDB_ReplyErrno(ctx, EILSEQ);
134205

135-
Result r = svcGetDebugThreadParam(&dummy, &mask, ctx->debug, threadId, DBGTHREAD_PARAMETER_SCHEDULING_MASK_LOW);
206+
Result r = svcGetDebugThreadParam(&dummy, &mask, ctx->debug, tid, DBGTHREAD_PARAMETER_SCHEDULING_MASK_LOW);
136207
if(R_SUCCEEDED(r) && mask != 2)
137208
return GDB_ReplyOk(ctx);
138209
else
@@ -144,7 +215,9 @@ GDB_DECLARE_QUERY_HANDLER(CurrentThreadId)
144215
if(ctx->currentThreadId == 0)
145216
ctx->currentThreadId = GDB_GetCurrentThread(ctx);
146217

147-
return ctx->currentThreadId != 0 ? GDB_SendFormattedPacket(ctx, "QC%lx", ctx->currentThreadId) : GDB_ReplyErrno(ctx, EPERM);
218+
char buf[32];
219+
GDB_EncodeThreadId(ctx, buf, ctx->currentThreadId);
220+
return ctx->currentThreadId != 0 ? GDB_SendFormattedPacket(ctx, "QC%s", buf) : GDB_ReplyErrno(ctx, EPERM);
148221
}
149222

150223
static void GDB_GenerateThreadListData(GDBContext *ctx)
@@ -168,7 +241,11 @@ static void GDB_GenerateThreadListData(GDBContext *ctx)
168241
char *bufptr = ctx->threadListData;
169242

170243
for(u32 i = 0; i < nbAliveThreads; i++)
171-
bufptr += sprintf(bufptr, i == (nbAliveThreads - 1) ? "%lx" : "%lx,", aliveThreadIds[i]);
244+
{
245+
bufptr += GDB_EncodeThreadId(ctx, bufptr, aliveThreadIds[i]);
246+
if (i < nbAliveThreads - 1)
247+
*bufptr++ = ',';
248+
}
172249
}
173250

174251
static int GDB_SendThreadData(GDBContext *ctx)
@@ -242,7 +319,8 @@ GDB_DECLARE_QUERY_HANDLER(ThreadExtraInfo)
242319

243320
u32 tls = 0;
244321

245-
if(GDB_ParseHexIntegerList(&id, ctx->commandData, 1, 0) == NULL)
322+
id = GDB_ParseDecodeSingleThreadId(ctx, ctx->commandData, 0);
323+
if (id == 0)
246324
return GDB_ReplyErrno(ctx, EILSEQ);
247325

248326
for(u32 i = 0; i < MAX_DEBUG_THREAD; i++)

0 commit comments

Comments
 (0)