static void gdb_read_mem() { static u8 reply[GDB_BFR_MAX - 4]; u32 addr, len; u32 i; i = 1; addr = 0; while (cmd_bfr[i] != ',') addr = (addr << 4) | hex2char(cmd_bfr[i++]); i++; len = 0; while (i < cmd_len) len = (len << 4) | hex2char(cmd_bfr[i++]); DEBUG_LOG(GDB_STUB, "gdb: read memory: %08x bytes from %08x", len, addr); if (len * 2 > sizeof reply) gdb_reply("E01"); u8* data = Memory::GetPointer(addr); if (!data) return gdb_reply("E0"); mem2hex(reply, data, len); reply[len * 2] = '\0'; gdb_reply((char*)reply); }
static void gdb_handle_set_thread() { if (memcmp(cmd_bfr, "Hg0", 3) == 0 || memcmp(cmd_bfr, "Hc-1", 4) == 0 || memcmp(cmd_bfr, "Hc0", 4) == 0 || memcmp(cmd_bfr, "Hc1", 4) == 0) return gdb_reply("OK"); gdb_reply("E01"); }
static void gdb_handle_query() { DEBUG_LOG(GDB_STUB, "gdb: query '%s'", cmd_bfr + 1); if (!strcmp((const char*)(cmd_bfr + 1), "TStatus")) { return gdb_reply("T0"); } gdb_reply(""); }
static void gdb_write_register() { u32 id; u8* bufptr = cmd_bfr + 3; id = hex2char(cmd_bfr[1]); if (cmd_bfr[2] != '=') { ++bufptr; id <<= 4; id |= hex2char(cmd_bfr[2]); } switch (id) { case 0 ... 31: GPR(id) = re32hex(bufptr); break; case 32 ... 63: riPS0(id - 32) = re64hex(bufptr); break; case 64: PC = re32hex(bufptr); break; case 65: MSR.Hex = re32hex(bufptr); break; case 66: PowerPC::SetCR(re32hex(bufptr)); break; case 67: LR = re32hex(bufptr); break; case 68: CTR = re32hex(bufptr); break; case 69: PowerPC::ppcState.spr[SPR_XER] = re32hex(bufptr); break; case 70: // do nothing, we dont have MQ break; case 71: FPSCR.Hex = re32hex(bufptr); break; default: return gdb_reply("E01"); break; } gdb_reply("OK"); }
static void gdb_read_register() { static u8 reply[64]; u32 id; memset(reply, 0, sizeof reply); id = hex2char(cmd_bfr[1]); if (cmd_bfr[2] != '\0') { id <<= 4; id |= hex2char(cmd_bfr[2]); } switch (id) { case 0 ... 31: wbe32hex(reply, GPR(id)); break; case 32 ... 63: wbe64hex(reply, riPS0(id - 32)); break; case 64: wbe32hex(reply, PC); break; case 65: wbe32hex(reply, MSR.Hex); break; case 66: wbe32hex(reply, PowerPC::GetCR()); break; case 67: wbe32hex(reply, LR); break; case 68: wbe32hex(reply, CTR); break; case 69: wbe32hex(reply, PowerPC::ppcState.spr[SPR_XER]); break; case 70: wbe32hex(reply, 0x0BADC0DE); break; case 71: wbe32hex(reply, FPSCR.Hex); break; default: return gdb_reply("E01"); break; } gdb_reply((char*)reply); }
static void gdb_handle_signal() { char bfr[128]; memset(bfr, 0, sizeof bfr); sprintf(bfr, "T%02x%02x:%08x;%02x:%08x;", sig, 64, PC, 1, GPR(1)); gdb_reply(bfr); }
static void gdb_write_mem() { u32 addr, len; u32 i; i = 1; addr = 0; while (cmd_bfr[i] != ',') addr = (addr << 4) | hex2char(cmd_bfr[i++]); i++; len = 0; while (cmd_bfr[i] != ':') len = (len << 4) | hex2char(cmd_bfr[i++]); DEBUG_LOG(GDB_STUB, "gdb: write memory: %08x bytes to %08x", len, addr); u8* dst = Memory::GetPointer(addr); if (!dst) return gdb_reply("E00"); hex2mem(dst, cmd_bfr + i + 1, len); gdb_reply("OK"); }
static void gdb_write_registers() { u32 i; u8* bufptr = cmd_bfr; for (i = 0; i < 32; i++) { GPR(i) = re32hex(bufptr + i * 8); } bufptr += 32 * 8; gdb_reply("OK"); }
static void gdb_remove_bp() { u32 type, addr, len, i; type = hex2char(cmd_bfr[1]); switch (type) { case 0: case 1: type = GDB_BP_TYPE_X; break; case 2: type = GDB_BP_TYPE_W; break; case 3: type = GDB_BP_TYPE_R; break; case 4: type = GDB_BP_TYPE_A; break; default: return gdb_reply("E01"); } addr = 0; len = 0; i = 3; while (cmd_bfr[i] != ',') addr = (addr << 4) | hex2char(cmd_bfr[i++]); i++; while (i < cmd_len) len = (len << 4) | hex2char(cmd_bfr[i++]); gdb_bp_remove(type, addr, len); gdb_reply("OK"); }
static void _gdb_add_bp() { u32 type; u32 i, addr = 0, len = 0; type = hex2char(cmd_bfr[1]); switch (type) { case 0: case 1: type = GDB_BP_TYPE_X; break; case 2: type = GDB_BP_TYPE_W; break; case 3: type = GDB_BP_TYPE_R; break; case 4: type = GDB_BP_TYPE_A; break; default: return gdb_reply("E01"); } i = 3; while (cmd_bfr[i] != ',') addr = addr << 4 | hex2char(cmd_bfr[i++]); i++; while (i < cmd_len) len = len << 4 | hex2char(cmd_bfr[i++]); if (!gdb_add_bp(type, addr, len)) return gdb_reply("E02"); gdb_reply("OK"); }
static void gdb_read_registers() { static u8 bfr[GDB_BFR_MAX - 4]; u8* bufptr = bfr; u32 i; memset(bfr, 0, sizeof bfr); for (i = 0; i < 32; i++) { wbe32hex(bufptr + i * 8, GPR(i)); } bufptr += 32 * 8; gdb_reply((char*)bfr); }
void gdb_handle_exception() { while (gdb_active()) { if (!gdb_data_available()) continue; gdb_read_command(); if (cmd_len == 0) continue; switch (cmd_bfr[0]) { case 'q': gdb_handle_query(); break; case 'H': gdb_handle_set_thread(); break; case '?': gdb_handle_signal(); break; case 'k': gdb_deinit(); INFO_LOG(GDB_STUB, "killed by gdb"); return; case 'g': gdb_read_registers(); break; case 'G': gdb_write_registers(); break; case 'p': gdb_read_register(); break; case 'P': gdb_write_register(); break; case 'm': gdb_read_mem(); break; case 'M': gdb_write_mem(); PowerPC::ppcState.iCache.Reset(); Host_UpdateDisasmDialog(); break; case 's': gdb_step(); return; case 'C': case 'c': gdb_continue(); return; case 'z': gdb_remove_bp(); break; case 'Z': _gdb_add_bp(); break; default: gdb_reply(""); break; } } }
static int gdbserver_main(struct GDBState *s) { char buf[MAX_PACKET_LEN]; char outbuf[MAX_PACKET_LEN]; char *ptr; uint32_t addr, len; int32_t thread, type, ret; for (;;) { memset(buf, 0, sizeof(buf)); memset(outbuf, 0, sizeof(outbuf)); get_packet(s, &buf[0]); gdb_ack(s); printf("command (%lu): %s\n", strlen(buf), buf); ptr = &buf[3]; switch (buf[2]) { case '?': gdb_reply(s, "T05thread:00;"); break; case 'g': cpu_read_registers(s, outbuf); gdb_reply(s, outbuf); break; case 'H': type = *ptr++; thread = strtoull(ptr, (char **)&ptr, 16); if (thread <= 0) { gdb_reply(s, "OK"); break; } else if (thread >= 1) { gdb_reply(s, "E22"); break; } switch (type) { case 'c': case 'g': gdb_reply(s, "OK"); break; default: gdb_reply(s, "E22"); break; } break; case 'k': goto end_command; case 'm': /* +$m9200,4#98 */ addr = strtoull(ptr, (char **)&ptr, 16); if (*ptr == ',') ++ptr; len = strtoull(ptr, NULL, 16); cpu_read_memory(s, addr, len, outbuf); gdb_reply(s, outbuf); break; case 'p': outbuf[0] = '\0'; gdb_reply(s, outbuf); break; case 'q': if (!strncmp(ptr, "Supported", 9)) { snprintf(outbuf, sizeof(outbuf), "PacketSize=%x", MAX_PACKET_LEN); gdb_reply(s, outbuf); } else if (!strncmp(ptr, "Offset", 6)) { gdb_reply(s, "Text=00000000;Data=00000000;Bss=00000000"); } else if (*ptr == 'C') gdb_reply(s, "QC1"); else gdb_reply(s, ""); break; case 'v': if (!strncmp(ptr, "Cont", 4)) { ptr += 4; if (*ptr == '?') { gdb_reply(s, "vCont;c;C;s;S"); break; } //set_reg(s->env, REG_PC, get_reg(s->env, REG_PC)+4); set_reg(s->env, REG_PC, 0x8224); gdb_reply(s, "S05"); } else return 0; break; case 'z': case 'Z': type = strtoul(ptr, (char **)&ptr, 16); if (*ptr == ',') ptr++; addr = strtoull(ptr, (char **)&ptr, 16); if (*ptr == ',') ptr++; len = strtoull(ptr, (char **)&ptr, 16); if (buf[2] == 'Z') ret = gdb_breakpoint_insert(addr, len, type); else ret = gdb_breakpoint_remove(addr, len, type); if (ret >= 0) gdb_reply(s, "OK"); break; default: return 0; } } end_command: return 0; }
/* * command parsing an dispatching */ static int gdb_parse_command(unsigned int *regfile) { if(!gdb_verify_checksum()) { gdb_nak(); return INIT; } else { gdb_ack(); } switch(cmd[0]) { case 'H': { /* * Command H (actually Hct) is used to select * the current thread (-1 meaning all threads) * We just fake we recognize the the command * and send an 'OK' response. */ gdb_reply("OK"); } break; case 'q': { extern unsigned __data_start; extern unsigned __bss_start; /* * There are several q commands: * * qXXXX Request info about XXXX. * QXXXX=yyyy Set value of XXXX to yyyy. * qOffsets Get segment offsets * * Currently we only support the 'qOffsets' * form. * * *Note* that we actually have to lie, * At first thought looks like we should * return '_start', '__data_start' & * '__bss_start', however gdb gets * confused because the kernel link script * pre-links at 0x80000000. To keep gdb * gdb happy we just substract that amount. */ if(strcmp(cmd+1, "Offsets")== 0) { gdb_reply( "Text=%x;Data=%x;Bss=%x", 0, ((unsigned)(&__data_start))-0x80000000, ((unsigned)(&__bss_start))-0x80000000 ); } else { gdb_reply("ENS"); } } break; case '?': { /* * command '?' is used for retrieving the signal * that stopped the program. Fully implemeting * this command requires help from the debugger, * by now we just fake a SIGKILL */ gdb_reply("S09"); /* SIGKILL = 9 */ } break; case 'g': { /* * command 'g' is used for reading the register * file. Faked by now. * * For x86 the register order is: * * eax, ebx, ecx, edx, * esp, ebp, esi, edi, * eip, eflags, * cs, ss, ds, es * * Note that even thought the segment descriptors * are actually 16 bits wide, gdb requires them * as 32 bit integers. Note also that for some * reason (unknown to me) gdb wants the register * dump in *big endian* format. */ gdb_regreply(regfile, GDB_REGISTER_FILE_COUNT); } break; case 'm': { char *ptr; unsigned address; unsigned len; /* * The 'm' command has the form mAAA,LLL * where AAA is the address and LLL is the * number of bytes. */ ptr= cmd+1; address= 0; len= 0; while(ptr && *ptr && (*ptr!= ',')) { address<<= 4; address+= parse_nibble(*ptr); ptr+= 1; } if(*ptr== ',') { ptr+= 1; } while(ptr && *ptr) { len<<= 4; len+= parse_nibble(*ptr); ptr+= 1; } if(len> 128) { len= 128; } /* * We cannot directly access the requested memory * for gdb may be trying to access an stray pointer * We copy the memory to a safe buffer using * the bulletproof user_memcpy(). */ if(user_memcpy(safe_mem, (char*)address, len)< 0) { gdb_reply("E02"); } else { gdb_memreply(safe_mem, len); } } break; case 'k': { /* * Command 'k' actual semantics is 'kill the damn thing'. * However gdb sends that command when you disconnect * from a debug session. I guess that 'kill' for the * kernel would map to reboot... however that's a * a very mean thing to do, instead we just quit * the gdb state machine and fallback to the regular * kernel debugger command prompt. */ return QUIT; } break; default: { gdb_reply("E01"); } break; } return WAITACK; }