From b7fd1225ba18d0ba1c6fc734575958e61a4c4d86 Mon Sep 17 00:00:00 2001 From: Matthew Parlane Date: Sun, 6 Jan 2013 23:28:27 +1300 Subject: [PATCH] Started gdb stub support. --- CMakeLists.txt | 6 + Source/Core/Common/Src/Log.h | 1 + Source/Core/Common/Src/LogManager.cpp | 1 + Source/Core/Core/CMakeLists.txt | 4 + Source/Core/Core/Src/ConfigManager.cpp | 4 + Source/Core/Core/Src/Core.cpp | 28 + Source/Core/Core/Src/CoreParameter.cpp | 3 + Source/Core/Core/Src/CoreParameter.h | 3 + Source/Core/Core/Src/PowerPC/GDBStub.cpp | 942 ++++++++++++++++++ Source/Core/Core/Src/PowerPC/GDBStub.h | 61 ++ .../Src/PowerPC/Interpreter/Interpreter.cpp | 20 +- 11 files changed, 1071 insertions(+), 2 deletions(-) create mode 100644 Source/Core/Core/Src/PowerPC/GDBStub.cpp create mode 100644 Source/Core/Core/Src/PowerPC/GDBStub.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b025de11ef..0349500ba8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,6 +222,12 @@ if(FASTLOG) add_definitions(-DDEBUGFAST) endif() + +option(GDBSTUB "Enable gdb stub for remote debugging." OFF) +if(GDBSTUB) + add_definitions(-DUSE_GDBSTUB) +endif(GDBSTUB) + # For now GLES and EGL are tied to each other. # Enabling GLES also disables the OpenGL plugin. option(USE_GLES "Enables GLES, disables OGL" OFF) diff --git a/Source/Core/Common/Src/Log.h b/Source/Core/Common/Src/Log.h index e24fd8583c..f88a386c2c 100644 --- a/Source/Core/Common/Src/Log.h +++ b/Source/Core/Common/Src/Log.h @@ -44,6 +44,7 @@ enum LOG_TYPE { DVDINTERFACE, DYNA_REC, EXPANSIONINTERFACE, + GDB_STUB, POWERPC, GPFIFO, OSHLE, diff --git a/Source/Core/Common/Src/LogManager.cpp b/Source/Core/Common/Src/LogManager.cpp index a79c413cf8..dc9f461480 100644 --- a/Source/Core/Common/Src/LogManager.cpp +++ b/Source/Core/Common/Src/LogManager.cpp @@ -57,6 +57,7 @@ LogManager::LogManager() m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVDInterface"); m_Log[LogTypes::GPFIFO] = new LogContainer("GP", "GPFifo"); m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "ExpansionInt"); + m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub"); m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "AudioInt"); m_Log[LogTypes::POWERPC] = new LogContainer("PowerPC", "IBM CPU"); m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE"); diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 6ffeeaec3a..1cf7f0a493 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -222,5 +222,9 @@ if(OPROFILE_FOUND) set(LIBS ${LIBS} opagent bfd) endif(OPROFILE_FOUND) +if(GDBSTUB) + set(SRCS ${SRCS} Src/PowerPC/GDBStub.cpp) +endif(GDBSTUB) + add_library(core STATIC ${SRCS}) target_link_libraries(core ${LIBS}) diff --git a/Source/Core/Core/Src/ConfigManager.cpp b/Source/Core/Core/Src/ConfigManager.cpp index 180e6c981d..183b35c968 100644 --- a/Source/Core/Core/Src/ConfigManager.cpp +++ b/Source/Core/Core/Src/ConfigManager.cpp @@ -157,6 +157,7 @@ void SConfig::SaveSettings() ini.Set("General", "RecursiveGCMPaths", m_RecursiveISOFolder); ini.Set("General", "NANDRoot", m_NANDPath); ini.Set("General", "WirelessMac", m_WirelessMac); + ini.Set("General", "GDBPort", m_LocalCoreStartupParameter.iGDBPort); // Interface ini.Set("Interface", "ConfirmStop", m_LocalCoreStartupParameter.bConfirmStop); @@ -270,6 +271,9 @@ void SConfig::LoadSettings() { ini.Get("General", "LastFilename", &m_LastFilename); ini.Get("General", "ShowLag", &m_ShowLag, false); + #ifdef USE_GDBSTUB + ini.Get("General", "GDBPort", &(m_LocalCoreStartupParameter.iGDBPort), -1); + #endif m_ISOFolder.clear(); int numGCMPaths; diff --git a/Source/Core/Core/Src/Core.cpp b/Source/Core/Core/Src/Core.cpp index e92b9088bc..0fa1d44134 100644 --- a/Source/Core/Core/Src/Core.cpp +++ b/Source/Core/Core/Src/Core.cpp @@ -55,6 +55,9 @@ #include "PowerPC/PowerPC.h" #include "PowerPC/JitCommon/JitBase.h" +#ifdef USE_GDBSTUB +#include "PowerPC/GDBStub.h" +#endif #include "DSPEmulator.h" #include "ConfigManager.h" @@ -101,6 +104,9 @@ std::string g_stateFileName; std::thread g_EmuThread; static std::thread g_cpu_thread; +#ifdef USE_GDBSTUB +static std::thread g_gdb_thread; +#endif static bool g_requestRefreshInfo = false; static int g_pauseAndLockDepth = 0; @@ -448,6 +454,20 @@ void EmuThread() else cpuThreadFunc = CpuThread; + #ifdef USE_GDBSTUB + if(_CoreParameter.iGDBPort > 0) + { + INFO_LOG(GDB_STUB, "Trying to start the GDB Stub listening on port %d.", _CoreParameter.iGDBPort); + Core::SetState(Core::CORE_PAUSE); + gdb_init(_CoreParameter.iGDBPort); + + g_gdb_thread = std::thread(gdb_thread); + + //gdb_signal(SIGTRAP); + gdb_add_bp(GDB_BP_TYPE_X, 0x80004050, 4); + } + #endif + // ENTER THE VIDEO THREAD LOOP if (_CoreParameter.bCPUThread) { @@ -491,6 +511,14 @@ void EmuThread() g_cpu_thread.join(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "CPU thread stopped.").c_str()); + + #ifdef USE_GDBSTUB + // Wait for g_gdb_thread to exit + INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping GDB thread ...").c_str()); + gdb_deinit(); + g_gdb_thread.join(); + INFO_LOG(CONSOLE, "%s", StopMessage(true, "GDB thread stopped.").c_str()); + #endif VolumeHandler::EjectVolume(); FileMon::Close(); diff --git a/Source/Core/Core/Src/CoreParameter.cpp b/Source/Core/Core/Src/CoreParameter.cpp index dee2452cb3..2c75a17eb3 100644 --- a/Source/Core/Core/Src/CoreParameter.cpp +++ b/Source/Core/Core/Src/CoreParameter.cpp @@ -68,6 +68,9 @@ SCoreStartupParameter::SCoreStartupParameter() void SCoreStartupParameter::LoadDefaults() { bEnableDebugging = false; + #ifdef USE_GDBSTUB + iGDBPort = -1; + #endif iCPUCore = 1; bCPUThread = false; bSkipIdle = false; diff --git a/Source/Core/Core/Src/CoreParameter.h b/Source/Core/Core/Src/CoreParameter.h index e67935906b..959997de70 100644 --- a/Source/Core/Core/Src/CoreParameter.h +++ b/Source/Core/Core/Src/CoreParameter.h @@ -71,6 +71,9 @@ struct SCoreStartupParameter // Settings bool bEnableDebugging; + #ifdef USE_GDBSTUB + int iGDBPort; + #endif bool bAutomaticStart; bool bBootToPause; diff --git a/Source/Core/Core/Src/PowerPC/GDBStub.cpp b/Source/Core/Core/Src/PowerPC/GDBStub.cpp new file mode 100644 index 0000000000..1ba5433638 --- /dev/null +++ b/Source/Core/Core/Src/PowerPC/GDBStub.cpp @@ -0,0 +1,942 @@ +// Copyright (C) 2010 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Originally written by Sven Peter for anergistic. + +#include "GDBStub.h" + +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#include +#else +#include +#include +#include +#endif +#include + + +#define GDB_BFR_MAX 10000 +#define GDB_MAX_BP 10 + +#define GDB_STUB_START '$' +#define GDB_STUB_END '#' +#define GDB_STUB_ACK '+' +#define GDB_STUB_NAK '-' + + +static int tmpsock = -1; +static int sock = -1; +static struct sockaddr_in saddr_server, saddr_client; + +static u8 cmd_bfr[GDB_BFR_MAX]; +static u32 cmd_len; + +static u32 sig = 0; +static u32 send_signal = 0; + +typedef struct { + u32 active; + u32 addr; + u32 len; +} gdb_bp_t; + +static gdb_bp_t bp_x[GDB_MAX_BP]; +static gdb_bp_t bp_r[GDB_MAX_BP]; +static gdb_bp_t bp_w[GDB_MAX_BP]; +static gdb_bp_t bp_a[GDB_MAX_BP]; + +// private helpers +static u8 hex2char(u8 hex) +{ + if (hex >= '0' && hex <= '9') + return hex - '0'; + else if (hex >= 'a' && hex <= 'f') + return hex - 'a' + 0xa; + else if (hex >= 'A' && hex <= 'F') + return hex - 'A' + 0xa; + + ERROR_LOG(GDB_STUB, "Invalid nibble: %c (%02x)\n", hex, hex); + return 0; +} + +static u8 nibble2hex(u8 n) +{ + n &= 0xf; + if (n < 0xa) + return '0' + n; + else + return 'A' + n - 0xa; +} + +static void mem2hex(u8 *dst, u8 *src, u32 len) +{ + u8 tmp; + + while (len-- > 0) { + tmp = *src++; + *dst++ = nibble2hex(tmp>>4); + *dst++ = nibble2hex(tmp); + } +} + +static void hex2mem(u8 *dst, u8 *src, u32 len) +{ + while (len-- > 0) { + *dst = hex2char(*src++) << 4; + *dst++ |= hex2char(*src++); + } +} + +static u8 gdb_read_byte() +{ + ssize_t res; + u8 c = '+'; + + res = recv(sock, &c, 1, MSG_WAITALL); + if (res != 1) + { + ERROR_LOG(GDB_STUB, "recv failed : %ld", res); + gdb_deinit(); + } + + return c; +} + +static u8 gdb_calc_chksum() +{ + u32 len = cmd_len; + u8 *ptr = cmd_bfr; + u8 c = 0; + + while(len-- > 0) + c += *ptr++; + + return c; +} + +static gdb_bp_t *gdb_bp_ptr(u32 type) +{ + switch (type) { + case GDB_BP_TYPE_X: + return bp_x; + case GDB_BP_TYPE_R: + return bp_x; + case GDB_BP_TYPE_W: + return bp_x; + case GDB_BP_TYPE_A: + return bp_x; + default: + return NULL; + } +} + +static gdb_bp_t *gdb_bp_empty_slot(u32 type) +{ + gdb_bp_t *p; + u32 i; + + p = gdb_bp_ptr(type); + if (p == NULL) + return NULL; + + for (i = 0; i < GDB_MAX_BP; i++) { + if (p[i].active == 0) + return &p[i]; + } + + return NULL; +} + +static gdb_bp_t *gdb_bp_find(u32 type, u32 addr, u32 len) +{ + gdb_bp_t *p; + u32 i; + + p = gdb_bp_ptr(type); + if (p == NULL) + return NULL; + + for (i = 0; i < GDB_MAX_BP; i++) { + if (p[i].active == 1 && + p[i].addr == addr && + p[i].len == len) + return &p[i]; + } + + return NULL; +} + +static void gdb_bp_remove(u32 type, u32 addr, u32 len) +{ + gdb_bp_t *p; + + do { + p = gdb_bp_find(type, addr, len); + if (p != NULL) { + DEBUG_LOG(GDB_STUB, "gdb: removed a breakpoint: %08x bytes at %08x\n", len, addr); + p->active = 0; + memset(p, 0, sizeof p); + } + } while (p != NULL); +} + +static int gdb_bp_check(u32 addr, u32 type) +{ + gdb_bp_t *p; + u32 i; + + p = gdb_bp_ptr(type); + if (p == NULL) + return 0; + + for (i = 0; i < GDB_MAX_BP; i++) { + if (p[i].active == 1 && + (addr >= p[i].addr && addr < p[i].addr + p[i].len)) + return 1; + } + + return 0; +} + +static void gdb_nak() +{ + const char nak = GDB_STUB_NAK; + ssize_t res; + + res = send(sock, &nak, 1, 0); + if (res != 1) + ERROR_LOG(GDB_STUB, "send failed"); +} + +static void gdb_ack() +{ + const char ack = GDB_STUB_ACK; + ssize_t res; + + res = send(sock, &ack, 1, 0); + if (res != 1) + ERROR_LOG(GDB_STUB, "send failed"); +} + +static void gdb_read_command() +{ + u8 c; + u8 chk_read, chk_calc; + + cmd_len = 0; + memset(cmd_bfr, 0, sizeof cmd_bfr); + + c = gdb_read_byte(); + if (c == '+') + { + //ignore ack + return; + } + else if (c == 0x03) + { + CCPU::Break(); + gdb_signal(SIGTRAP); + return; + } + else if (c != GDB_STUB_START) { + DEBUG_LOG(GDB_STUB, "gdb: read invalid byte %02x\n", c); + return; + } + + while ((c = gdb_read_byte()) != GDB_STUB_END) { + cmd_bfr[cmd_len++] = c; + if (cmd_len == sizeof cmd_bfr) + ERROR_LOG(GDB_STUB, "gdb: cmd_bfr overflow\n"); + } + + chk_read = hex2char(gdb_read_byte()) << 4; + chk_read |= hex2char(gdb_read_byte()); + + chk_calc = gdb_calc_chksum(); + + if (chk_calc != chk_read) { + ERROR_LOG(GDB_STUB, "gdb: invalid checksum: calculated %02x and read %02x for $%s# (length: %d)\n", chk_calc, chk_read, cmd_bfr, cmd_len); + cmd_len = 0; + + gdb_nak(); + } + + DEBUG_LOG(GDB_STUB, "gdb: read command %c with a length of %d: %s\n", cmd_bfr[0], cmd_len, cmd_bfr); +} + +static int gdb_data_available() { + struct timeval t; + fd_set _fds, *fds = &_fds; + + FD_ZERO(fds); + FD_SET(sock, fds); + + t.tv_sec = 0; + t.tv_usec = 20; + + if (select(sock + 1, fds, NULL, NULL, &t) < 0) + { + ERROR_LOG(GDB_STUB, "select failed"); + return 0; + } + + if (FD_ISSET(sock, fds)) + return 1; + return 0; +} + +static void gdb_reply(const char *reply) +{ + u8 chk; + u32 left; + u8 *ptr; + int n; + + memset(cmd_bfr, 0, sizeof cmd_bfr); + + cmd_len = strlen(reply); + if (cmd_len + 4 > sizeof cmd_bfr) + ERROR_LOG(GDB_STUB, "cmd_bfr overflow in gdb_reply"); + + memcpy(cmd_bfr + 1, reply, cmd_len); + + cmd_len++; + chk = gdb_calc_chksum(); + cmd_len--; + cmd_bfr[0] = GDB_STUB_START; + cmd_bfr[cmd_len + 1] = GDB_STUB_END; + cmd_bfr[cmd_len + 2] = nibble2hex(chk >> 4); + cmd_bfr[cmd_len + 3] = nibble2hex(chk); + + DEBUG_LOG(GDB_STUB, "gdb: reply (len: %d): %s\n", cmd_len, cmd_bfr); + + ptr = cmd_bfr; + left = cmd_len + 4; + while (left > 0) { + n = send(sock, ptr, left, 0); + if (n < 0) + ERROR_LOG(GDB_STUB, "gdb: send failed"); + left -= n; + ptr += n; + } +} + +static void gdb_handle_query() +{ + gdb_ack(); + DEBUG_LOG(GDB_STUB, "gdb: query '%s'\n", cmd_bfr+1); + + if (!strcmp((const char *)(cmd_bfr+1), "TStatus")) + { + return gdb_reply("T0"); + } + + gdb_reply(""); +} + +static void gdb_handle_set_thread() +{ + gdb_ack(); + if (memcmp(cmd_bfr, "Hg0", 3) == 0 || + memcmp(cmd_bfr, "Hc-1", 4) == 0 || + memcmp(cmd_bfr, "Hc0", 4) == 0) + return gdb_reply("OK"); + gdb_reply("E01"); +} + +static void gdb_handle_signal() +{ + char bfr[128]; + + gdb_ack(); + memset(bfr, 0, sizeof bfr); + sprintf(bfr, "T%02x%02x:%08x;%02x:%08x;", sig, 64, PC, 1, GPR(1)); + gdb_reply(bfr); +} + +static void wbe32hex(u8 *p, u32 v) +{ + u32 i; + for (i = 0; i < 8; i++) + p[i] = nibble2hex(v >> (28 - 4*i)); +} + +static void wbe64hex(u8 *p, u64 v) +{ + u32 i; + for (i = 0; i < 16; i++) + p[i] = nibble2hex(v >> (60 - 4*i)); +} + +static void gdb_read_register() +{ + static u8 reply[64]; + u32 id; + + memset(reply, 0, sizeof reply); + id = hex2char(cmd_bfr[1]) << 4; + id |= hex2char(cmd_bfr[2]); + + gdb_ack(); + 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); + break; + case 66: + wbe32hex(reply, 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_read_registers() +{ + static u8 bfr[GDB_BFR_MAX - 4]; + u8 * bufptr = bfr; + u32 i; + + gdb_ack(); + memset(bfr, 0, sizeof bfr); + + for (i = 0; i < 32; i++) { + wbe32hex(bufptr + i*4, GPR(i)); + } + bufptr += 32 * 4; + + for (i = 0; i < 32; i++) { + wbe64hex(bufptr + i*4, riPS0(i)); + } + bufptr += 64 * 4; + + wbe32hex(bufptr, PC); bufptr += 4; + wbe32hex(bufptr, MSR); bufptr += 4; + wbe32hex(bufptr, GetCR()); bufptr += 4; + wbe32hex(bufptr, LR); bufptr += 4; + + + wbe32hex(bufptr, CTR); bufptr += 4; + wbe32hex(bufptr, PowerPC::ppcState.spr[SPR_XER]); bufptr += 4; + // MQ register not used. + wbe32hex(bufptr, 0x0BADC0DE); bufptr += 4; + + + + gdb_reply((char *)bfr); +} + +static u32 re32hex(u8 *p) +{ + u32 i; + u32 res = 0; + + for (i = 0; i < 8; i++) + res = (res << 4) | hex2char(p[i]); + + return res; +} + +static u64 re64hex(u8 *p) +{ + u32 i; + u64 res = 0; + + for (i = 0; i < 16; i++) + res = (res << 4) | hex2char(p[i]); + + return res; +} + +static void gdb_write_registers() +{ + gdb_ack(); + + u32 i; + u8 * bufptr = cmd_bfr; + + for (i = 0; i < 32; i++) { + GPR(i) = re32hex(bufptr + i*4); + } + bufptr += 32 * 4; + + for (i = 0; i < 32; i++) { + riPS0(i) = re64hex(bufptr + i*4); + } + bufptr += 64 * 4; + + PC = re32hex(bufptr); bufptr += 4; + MSR = re32hex(bufptr); bufptr += 4; + SetCR(re32hex(bufptr)); bufptr += 4; + LR = re32hex(bufptr); bufptr += 4; + + + CTR = re32hex(bufptr); bufptr += 4; + PowerPC::ppcState.spr[SPR_XER] = re32hex(bufptr); bufptr += 4; + // MQ register not used. + (void)re32hex(bufptr); bufptr += 4; + + + gdb_reply("OK"); +} + +static void gdb_write_register() +{ + u32 id; + + gdb_ack(); + + id = hex2char(cmd_bfr[1]) << 4; + id |= hex2char(cmd_bfr[2]); + + + u8 * bufptr = cmd_bfr + 4; + + + 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 = re32hex(bufptr); + break; + case 66: + 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_mem() +{ + static u8 reply[GDB_BFR_MAX - 4]; + u32 addr, len; + u32 i; + + gdb_ack(); + + 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\n", len, addr); + + if (len*2 > sizeof reply) + gdb_reply("E01"); + + mem2hex(reply, Memory::GetPointer(addr), len); + reply[len*2] = '\0'; + gdb_reply((char *)reply); +} + +static void gdb_write_mem() +{ + u32 addr, len; + u32 i; + + gdb_ack(); + + 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\n", len, addr); + + hex2mem(Memory::GetPointer(addr), cmd_bfr + i, len); + gdb_reply("OK"); +} + +static void gdb_continue() +{ + gdb_ack(); + CCPU::EnableStepping(false); + m_GdbWaitEvent.Set(); + send_signal = 1; +} + + +bool gdb_add_bp(u32 type, u32 addr, u32 len) +{ + gdb_bp_t *bp; + bp = gdb_bp_empty_slot(type); + if (bp == NULL) + return false; + + bp->active = 1; + bp->addr = addr; + bp->len = len; + + DEBUG_LOG(GDB_STUB, "gdb: added %d breakpoint: %08x bytes at %08x\n", type, bp->len, bp->addr); + return true; +} + +static void _gdb_add_bp() +{ + u32 type; + u32 i, addr = 0, len = 0; + + gdb_ack(); + + 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_remove_bp() +{ + u32 type, addr, len, i; + + gdb_ack(); + + 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_parse_command() +{ + if (cmd_len == 0) + return; + + 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_ack(); + gdb_deinit(); + INFO_LOG(GDB_STUB, "killed by gdb"); + break; + 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(); + break; + case 's': + case 'C': + case 'c': + gdb_continue(); + break; + case 'v': + gdb_ack(); + gdb_reply(""); + break; + case 'z': + gdb_remove_bp(); + break; + case 'Z': + _gdb_add_bp(); + break; + default: + gdb_ack(); + gdb_reply(""); + break; + } +} + +#ifdef _WIN32 +WSADATA InitData; +#endif + +// exported functions + +void gdb_init(u32 port) +{ + socklen_t len; + int on; + #ifdef _WIN32 + WSAStartup(MAKEWORD(2,2), &InitData); + #endif + + m_GdbWaitEvent.Reset(); + + memset(bp_x, 0, sizeof bp_x); + memset(bp_r, 0, sizeof bp_r); + memset(bp_w, 0, sizeof bp_w); + memset(bp_a, 0, sizeof bp_a); + + tmpsock = socket(AF_INET, SOCK_STREAM, 0); + if (tmpsock == -1) + ERROR_LOG(GDB_STUB, "Failed to create gdb socket"); + + on = 1; + if (setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) < 0) + ERROR_LOG(GDB_STUB, "Failed to setsockopt"); + + memset(&saddr_server, 0, sizeof saddr_server); + saddr_server.sin_family = AF_INET; + saddr_server.sin_port = htons(port); + saddr_server.sin_addr.s_addr = INADDR_ANY; + + if (bind(tmpsock, (struct sockaddr *)&saddr_server, sizeof saddr_server) < 0) + ERROR_LOG(GDB_STUB, "Failed to bind gdb socket"); + + if (listen(tmpsock, 1) < 0) + ERROR_LOG(GDB_STUB, "Failed to listen to gdb socket"); + + INFO_LOG(GDB_STUB, "Waiting for gdb to connect...\n"); + sock = accept(tmpsock, (struct sockaddr *)&saddr_client, &len); + + if (sock < 0) + ERROR_LOG(GDB_STUB, "Failed to accept gdb client"); + INFO_LOG(GDB_STUB, "Client connected.\n"); + + saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr); + /*if (((saddr_client.sin_addr.s_addr >> 24) & 0xff) != 127 || + * ((saddr_client.sin_addr.s_addr >> 16) & 0xff) != 0 || + * ((saddr_client.sin_addr.s_addr >> 8) & 0xff) != 0 || + * ((saddr_client.sin_addr.s_addr >> 0) & 0xff) != 1) + * ERROR_LOG(GDB_STUB, "gdb: incoming connection not from localhost"); + */ + close(tmpsock); + tmpsock = -1; +} + + +void gdb_deinit() +{ + if (tmpsock != -1) + { + close(tmpsock); + tmpsock = -1; + } + if (sock != -1) + { + close(sock); + sock = -1; + } + + m_GdbWaitEvent.Set(); + m_GdbWaitEvent.Reset(); + + #ifdef _WIN32 + WSACleanup(); + #endif +} + +bool gdb_active() +{ + return tmpsock != -1 || sock != -1; +} + +void gdb_thread() +{ + while(sock != -1) + { + gdb_handle_events(); + } +} + +void gdb_handle_events() +{ + if (sock == -1) + return; + + while (sock != -1 && gdb_data_available()) { + gdb_read_command(); + gdb_parse_command(); + } +} + +int gdb_signal(u32 s) +{ + if (sock == -1) + return 1; + + sig = s; + + if (send_signal) { + gdb_handle_signal(); + send_signal = 0; + } + + return 0; +} + +int gdb_bp_x(u32 addr) +{ + if (sock == -1) + return 0; + + return gdb_bp_check(addr, GDB_BP_TYPE_X); +} + +int gdb_bp_r(u32 addr) +{ + if (sock == -1) + return 0; + + return gdb_bp_check(addr, GDB_BP_TYPE_R); +} + +int gdb_bp_w(u32 addr) +{ + if (sock == -1) + return 0; + + return gdb_bp_check(addr, GDB_BP_TYPE_W); +} + +int gdb_bp_a(u32 addr) +{ + if (sock == -1) + return 0; + + return gdb_bp_check(addr, GDB_BP_TYPE_A); +} diff --git a/Source/Core/Core/Src/PowerPC/GDBStub.h b/Source/Core/Core/Src/PowerPC/GDBStub.h new file mode 100644 index 0000000000..4fcaa76298 --- /dev/null +++ b/Source/Core/Core/Src/PowerPC/GDBStub.h @@ -0,0 +1,61 @@ +// Copyright (C) 2010 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Originally written by Sven Peter for anergistic. + +#ifndef GDB_H__ +#define GDB_H__ + +#include +#include "Common.h" +#include "Thread.h" +#include "PowerPC.h" +#include "../HW/CPU.h" +#include "../HW/Memmap.h" + +#ifdef _WIN32 +#define SIGTRAP 5 +#define SIGTERM 15 +#define MSG_WAITALL 8 +#endif + +typedef enum { + GDB_BP_TYPE_NONE = 0, + GDB_BP_TYPE_X, + GDB_BP_TYPE_R, + GDB_BP_TYPE_W, + GDB_BP_TYPE_A +} gdb_bp_type; + +void gdb_init(u32 port); +void gdb_deinit(); +bool gdb_active(); + +void gdb_thread(); +void gdb_handle_events(); +int gdb_signal(u32 signal); + +int gdb_bp_x(u32 addr); +int gdb_bp_r(u32 addr); +int gdb_bp_w(u32 addr); +int gdb_bp_a(u32 addr); + +bool gdb_add_bp(u32 type, u32 addr, u32 len); + +static Common::Event m_GdbWaitEvent; + +#endif diff --git a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter.cpp b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter.cpp index a8277d868a..f7604a4f12 100644 --- a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter.cpp +++ b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter.cpp @@ -27,6 +27,10 @@ #include "../../IPC_HLE/WII_IPC_HLE.h" #include "Atomic.h" +#ifdef USE_GDBSTUB +#include "../GDBStub.h" +#endif + namespace { u32 last_pc; @@ -99,7 +103,18 @@ void Trace( UGeckoInstruction &instCode ) int Interpreter::SingleStepInner(void) { static UGeckoInstruction instCode; - + + #ifdef USE_GDBSTUB + if (gdb_active() && gdb_bp_x(PC)) { + + Host_UpdateDisasmDialog(); + + gdb_signal(SIGTRAP); + gdb_handle_events(); + m_GdbWaitEvent.Wait(); + } + #endif + NPC = PC + sizeof(UGeckoInstruction); instCode.hex = Memory::Read_Opcode(PC); @@ -218,7 +233,8 @@ void Interpreter::Run() if (PCVec.size() > ShowSteps) PCVec.erase(PCVec.begin()); #endif - + + //2: check for breakpoint if (PowerPC::breakpoints.IsAddressBreakPoint(PC)) {