Started gdb stub support.
This commit is contained in:
parent
4d6056f146
commit
b7fd1225ba
|
@ -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)
|
||||
|
|
|
@ -44,6 +44,7 @@ enum LOG_TYPE {
|
|||
DVDINTERFACE,
|
||||
DYNA_REC,
|
||||
EXPANSIONINTERFACE,
|
||||
GDB_STUB,
|
||||
POWERPC,
|
||||
GPFIFO,
|
||||
OSHLE,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -68,6 +68,9 @@ SCoreStartupParameter::SCoreStartupParameter()
|
|||
void SCoreStartupParameter::LoadDefaults()
|
||||
{
|
||||
bEnableDebugging = false;
|
||||
#ifdef USE_GDBSTUB
|
||||
iGDBPort = -1;
|
||||
#endif
|
||||
iCPUCore = 1;
|
||||
bCPUThread = false;
|
||||
bSkipIdle = false;
|
||||
|
|
|
@ -71,6 +71,9 @@ struct SCoreStartupParameter
|
|||
|
||||
// Settings
|
||||
bool bEnableDebugging;
|
||||
#ifdef USE_GDBSTUB
|
||||
int iGDBPort;
|
||||
#endif
|
||||
bool bAutomaticStart;
|
||||
bool bBootToPause;
|
||||
|
||||
|
|
|
@ -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 <sven@fail0verflow.com> for anergistic.
|
||||
|
||||
#include "GDBStub.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <iphlpapi.h>
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
#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);
|
||||
}
|
|
@ -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 <sven@fail0verflow.com> for anergistic.
|
||||
|
||||
#ifndef GDB_H__
|
||||
#define GDB_H__
|
||||
|
||||
#include <signal.h>
|
||||
#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
|
|
@ -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))
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue