static int GDB_ParseCommonThreadInfo(char *out, GDBContext *ctx, int sig) { u32 threadId = ctx->currentThreadId; ThreadContext regs; s64 dummy; u32 core; Result r = svcGetDebugThreadContext(®s, ctx->debug, threadId, THREADCONTEXT_CONTROL_ALL); int n = sprintf(out, "T%02xthread:%x;", sig, threadId); if(R_FAILED(r)) return n; r = svcGetDebugThreadParam(&dummy, &core, ctx->debug, ctx->currentThreadId, DBGTHREAD_PARAMETER_CPU_CREATOR); // Creator = "first ran, and running the thread" if(R_SUCCEEDED(r)) n += sprintf(out + n, "core:%x;", core); for(u32 i = 0; i <= 12; i++) n += sprintf(out + n, "%x:%08x;", i, __builtin_bswap32(regs.cpu_registers.r[i])); n += sprintf(out + n, "d:%08x;e:%08x;f:%08x;19:%08x;", __builtin_bswap32(regs.cpu_registers.sp), __builtin_bswap32(regs.cpu_registers.lr), __builtin_bswap32(regs.cpu_registers.pc), __builtin_bswap32(regs.cpu_registers.cpsr)); for(u32 i = 0; i < 16; i++) { u64 val; memcpy(&val, ®s.fpu_registers.d[i], 8); n += sprintf(out + n, "%x:%016llx;", 26 + i, __builtin_bswap64(val)); } n += sprintf(out + n, "2a:%08x;2b:%08x;", __builtin_bswap32(regs.fpu_registers.fpscr), __builtin_bswap32(regs.fpu_registers.fpexc)); return n; }
int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info) { char buffer[GDB_BUF_LEN + 1]; switch(info->type) { case DBGEVENT_ATTACH_PROCESS: break; // Dismissed case DBGEVENT_ATTACH_THREAD: { if(info->attach_thread.creator_thread_id == 0 || !ctx->catchThreadEvents) break; // Dismissed else { ctx->currentThreadId = info->thread_id; return GDB_SendPacket(ctx, "T05create:;", 10); } } case DBGEVENT_EXIT_THREAD: { if(ctx->catchThreadEvents && info->exit_thread.reason < EXITTHREAD_EVENT_EXIT_PROCESS) { // no signal, SIGTERM, SIGQUIT (process exited), SIGTERM (process terminated) static int threadExitRepliesSigs[] = { 0, SIGTERM, SIGQUIT, SIGTERM }; return GDB_SendFormattedPacket(ctx, "w%02x;%x", threadExitRepliesSigs[(u32)info->exit_thread.reason], info->thread_id); } break; } case DBGEVENT_EXIT_PROCESS: { // exited (no error / unhandled exception), SIGTERM (process terminated) * 2 static const char *processExitReplies[] = { "W00", "X0f", "X0f" }; return GDB_SendPacket(ctx, processExitReplies[(u32)info->exit_process.reason], 3); } case DBGEVENT_EXCEPTION: { ExceptionEvent exc = info->exception; switch(exc.type) { case EXCEVENT_UNDEFINED_INSTRUCTION: case EXCEVENT_PREFETCH_ABORT: // doesn't include hardware breakpoints case EXCEVENT_DATA_ABORT: // doesn't include hardware watchpoints case EXCEVENT_UNALIGNED_DATA_ACCESS: case EXCEVENT_UNDEFINED_SYSCALL: { u32 signum = exc.type == EXCEVENT_UNDEFINED_INSTRUCTION ? SIGILL : (exc.type == EXCEVENT_UNDEFINED_SYSCALL ? SIGSYS : SIGSEGV); ctx->currentThreadId = info->thread_id; GDB_ParseCommonThreadInfo(buffer, ctx, signum); return GDB_SendFormattedPacket(ctx, "%s", buffer); } case EXCEVENT_ATTACH_BREAK: return GDB_SendPacket(ctx, "S00", 3); case EXCEVENT_STOP_POINT: { ctx->currentThreadId = info->thread_id; switch(exc.stop_point.type) { case STOPPOINT_SVC_FF: { GDB_ParseCommonThreadInfo(buffer, ctx, SIGTRAP); return GDB_SendFormattedPacket(ctx, "%sswbreak:;", buffer); break; } case STOPPOINT_BREAKPOINT: { // /!\ Not actually implemented (and will never be) GDB_ParseCommonThreadInfo(buffer, ctx, SIGTRAP); return GDB_SendFormattedPacket(ctx, "%shwbreak:;", buffer); break; } case STOPPOINT_WATCHPOINT: { const char *kinds[] = { "", "r", "", "a" }; WatchpointKind kind = GDB_GetWatchpointKind(ctx, exc.stop_point.fault_information); if(kind == WATCHPOINT_DISABLED) GDB_SendDebugString(ctx, "Warning: unknown watchpoint encountered!\n"); GDB_ParseCommonThreadInfo(buffer, ctx, SIGTRAP); return GDB_SendFormattedPacket(ctx, "%s%swatch:%08x;", buffer, kinds[(u32)kind], exc.stop_point.fault_information); break; } default: break; } } case EXCEVENT_USER_BREAK: { ctx->currentThreadId = info->thread_id; GDB_ParseCommonThreadInfo(buffer, ctx, SIGINT); return GDB_SendFormattedPacket(ctx, "%s", buffer); //TODO } case EXCEVENT_DEBUGGER_BREAK: { u32 threadIds[4]; u32 nbThreads = 0; for(u32 i = 0; i < 4; i++) { if(exc.debugger_break.thread_ids[i] > 0) threadIds[nbThreads++] = (u32)exc.debugger_break.thread_ids[i]; } u32 currentThreadId = nbThreads > 0 ? GDB_GetCurrentThreadFromList(ctx, threadIds, nbThreads) : GDB_GetCurrentThread(ctx); s64 dummy; u32 mask = 0; svcGetDebugThreadParam(&dummy, &mask, ctx->debug, currentThreadId, DBGTHREAD_PARAMETER_SCHEDULING_MASK_LOW); if(mask == 1) { ctx->currentThreadId = currentThreadId; GDB_ParseCommonThreadInfo(buffer, ctx, SIGINT); return GDB_SendFormattedPacket(ctx, "%s", buffer); } else return GDB_SendPacket(ctx, "S02", 3); } default: break; } break; } case DBGEVENT_SYSCALL_IN: { ctx->currentThreadId = info->thread_id; GDB_ParseCommonThreadInfo(buffer, ctx, SIGTRAP); return GDB_SendFormattedPacket(ctx, "%ssyscall_entry:%02x;", buffer, info->syscall.syscall); } case DBGEVENT_SYSCALL_OUT: { ctx->currentThreadId = info->thread_id; GDB_ParseCommonThreadInfo(buffer, ctx, SIGTRAP); return GDB_SendFormattedPacket(ctx, "%ssyscall_return:%02x;", buffer, info->syscall.syscall); } case DBGEVENT_OUTPUT_STRING: { u32 addr = info->output_string.string_addr; u32 remaining = info->output_string.string_size; u32 sent = 0; int total = 0; while(remaining > 0) { u32 pending = (GDB_BUF_LEN - 1) / 2; pending = pending < remaining ? pending : remaining; int res = GDB_SendMemory(ctx, "O", 1, addr + sent, pending); if(res < 0 || (u32) res != 5 + 2 * pending) break; sent += pending; remaining -= pending; total += res; } return total; } default: break; } return 0; }