mirror of
https://github.com/RKX1209/nsemu.git
synced 2024-06-23 14:43:16 -04:00
Add initial support of gdbstub
This commit is contained in:
parent
1ac5401062
commit
fdd127bcce
|
@ -24,13 +24,17 @@ void Interpreter::Run() {
|
|||
//uint64_t estimate = 3413000, mx = 10000;
|
||||
uint64_t estimate = 3414000, mx = 10000;
|
||||
while (Cpu::GetState () == Cpu::State::Running) {
|
||||
if (counter >= estimate){
|
||||
Cpu::DumpMachine ();
|
||||
}
|
||||
if (counter >= estimate + mx)
|
||||
break;
|
||||
SingleStep ();
|
||||
counter++;
|
||||
if (GdbStub::enabled) {
|
||||
GdbStub::HandlePacket();
|
||||
} else {
|
||||
if (counter >= estimate){
|
||||
Cpu::DumpMachine ();
|
||||
}
|
||||
if (counter >= estimate + mx)
|
||||
break;
|
||||
SingleStep ();
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
357
GdbStub.cpp
Normal file
357
GdbStub.cpp
Normal file
|
@ -0,0 +1,357 @@
|
|||
/* nsemu - LGPL - Copyright 2018 rkx1209<rkx1209dev@gmail.com> */
|
||||
/* TODO: Move host dependent functions to host_util.hpp */
|
||||
#include "Nsemu.hpp"
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
namespace GdbStub {
|
||||
|
||||
static int server_fd, client_fd;
|
||||
|
||||
uint8_t cmd_buf[GDB_BUFFER_SIZE], last_packet[GDB_BUFFER_SIZE + 4];
|
||||
|
||||
static int buf_index, line_sum, checksum, last_packet_len;
|
||||
|
||||
static RSState state = RS_IDLE;
|
||||
|
||||
volatile bool enabled = false;
|
||||
|
||||
static inline bool IsXdigit(char ch) {
|
||||
return ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F') || ('0' <= ch && ch <= '9');
|
||||
}
|
||||
|
||||
static inline int FromHex(int v)
|
||||
{
|
||||
if (v >= '0' && v <= '9')
|
||||
return v - '0';
|
||||
else if (v >= 'A' && v <= 'F')
|
||||
return v - 'A' + 10;
|
||||
else if (v >= 'a' && v <= 'f')
|
||||
return v - 'a' + 10;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
static inline int ToHex(int v)
|
||||
{
|
||||
if (v < 10)
|
||||
return v + '0';
|
||||
else
|
||||
return v - 10 + 'a';
|
||||
}
|
||||
|
||||
static int IsQueryPacket (const char *p, const char *query, char separator)
|
||||
{
|
||||
unsigned int query_len = strlen(query);
|
||||
|
||||
return strncmp(p, query, query_len) == 0 && (p[query_len] == '\0' || p[query_len] == separator);
|
||||
}
|
||||
|
||||
void Init() {
|
||||
struct sockaddr_in addr;
|
||||
|
||||
server_fd = socket (AF_INET, SOCK_STREAM, 0);
|
||||
if (server_fd < 0) {
|
||||
ns_abort ("socket failed\n");
|
||||
}
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(1234);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.sin_len = sizeof(addr);
|
||||
|
||||
if (::bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
|
||||
ns_abort ("bind failed");
|
||||
}
|
||||
|
||||
if (listen(server_fd, 5) != 0) {
|
||||
ns_abort ("listen failed");
|
||||
}
|
||||
client_fd = accept(server_fd, nullptr, nullptr);
|
||||
if (client_fd < 0) {
|
||||
ns_abort ("Failed to accept gdb client\n");
|
||||
}
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
uint8_t ReadByte() {
|
||||
uint8_t byte;
|
||||
auto size = recv (client_fd, &byte, 1, MSG_WAITALL);
|
||||
if (size != 1) {
|
||||
ns_abort ("Failed recv :%ld", size);
|
||||
}
|
||||
return byte;
|
||||
}
|
||||
|
||||
void WriteBytes(uint8_t *buf, size_t len) {
|
||||
if (send (client_fd, buf, len, 0) == -1) {
|
||||
ns_abort ("Failed send\n");
|
||||
}
|
||||
}
|
||||
|
||||
void SendPacket(uint8_t *buf, size_t len) {
|
||||
int csum;
|
||||
uint8_t *p;
|
||||
for (;;) {
|
||||
p = last_packet;
|
||||
*(p++) = '$';
|
||||
memcpy(p, buf, len);
|
||||
p += len;
|
||||
csum = 0;
|
||||
for(int i = 0; i < len; i++) {
|
||||
csum += buf[i];
|
||||
}
|
||||
*(p++) = '#';
|
||||
*(p++) = ToHex((csum >> 4) & 0xf);
|
||||
*(p++) = ToHex((csum) & 0xf);
|
||||
|
||||
last_packet_len = p - last_packet;
|
||||
WriteBytes (last_packet, last_packet_len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WritePacket(char *buf) {
|
||||
SendPacket ((uint8_t *)buf, strlen(buf));
|
||||
}
|
||||
static RSState HandleCommand(char *line_buf) {
|
||||
const char *p;
|
||||
uint32_t thread;
|
||||
int ch, reg_size, type, res;
|
||||
char buf[GDB_BUFFER_SIZE];
|
||||
uint8_t mem_buf[GDB_BUFFER_SIZE];
|
||||
unsigned long addr, len;
|
||||
|
||||
ns_print("command='%s'\n", line_buf);
|
||||
|
||||
p = line_buf;
|
||||
ch = *p++;
|
||||
switch(ch) {
|
||||
case '?':
|
||||
/* XXX: Is it correct to fix thread id to '1'? */
|
||||
snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP, 1);
|
||||
WritePacket(buf);
|
||||
break;
|
||||
case 'c':
|
||||
if (*p != '\0') {
|
||||
addr = strtol(p, (char **)&p, 16);
|
||||
}
|
||||
//Continue ();
|
||||
break;
|
||||
case 'g':
|
||||
len = 0;
|
||||
// for (addr = 0; addr < GENERAL_REG_MAX; addr++) {
|
||||
// //reg_size = gdb_read_register(mem_buf + len, addr);
|
||||
// len += reg_size;
|
||||
// }
|
||||
// memtohex(buf, mem_buf, len);
|
||||
// WritePacket(buf);
|
||||
break;
|
||||
case 'm':
|
||||
addr = strtol(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
len = strtol(p, NULL, 16);
|
||||
|
||||
/* memtohex() doubles the required space */
|
||||
if (len > GDB_BUFFER_SIZE / 2) {
|
||||
WritePacket ("E22");
|
||||
break;
|
||||
}
|
||||
|
||||
// if (target_memory_rw (addr, mem_buf, len, false) != 0) {
|
||||
// WritePacket ("E14");
|
||||
// } else {
|
||||
// //memtohex(buf, mem_buf, len);
|
||||
// //WritePacket(buf);
|
||||
// }
|
||||
break;
|
||||
case 'p':
|
||||
addr = strtol(p, (char **)&p, 16);
|
||||
//reg_size = gdb_read_register(mem_buf, addr);
|
||||
if (reg_size) {
|
||||
//memtohex(buf, mem_buf, reg_size);
|
||||
WritePacket(buf);
|
||||
} else {
|
||||
WritePacket("E14");
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
case 'Q':
|
||||
if (IsQueryPacket(p, "Supported", ':')) {
|
||||
snprintf(buf, sizeof(buf), "PacketSize=%x", GDB_BUFFER_SIZE);
|
||||
WritePacket(buf);
|
||||
break;
|
||||
} else if (strcmp(p, "C") == 0) {
|
||||
WritePacket("QC1");
|
||||
break;
|
||||
}
|
||||
goto unknown_command;
|
||||
case 'z':
|
||||
case 'Z':
|
||||
type = strtol(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
addr = strtol(p, (char **)&p, 16);
|
||||
if (*p == ',')
|
||||
p++;
|
||||
len = strtol(p, (char **)&p, 16);
|
||||
if (ch == 'Z') {
|
||||
//res = gdb_breakpoint_insert(addr, len, type);
|
||||
} else {
|
||||
//res = gdb_breakpoint_remove(addr, len, type);
|
||||
}
|
||||
if (res >= 0)
|
||||
WritePacket("OK");
|
||||
else
|
||||
WritePacket("E22");
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
type = *p++;
|
||||
thread = strtol(p, (char **)&p, 16);
|
||||
if (thread == -1 || thread == 0) {
|
||||
WritePacket("OK");
|
||||
break;
|
||||
}
|
||||
switch (type) {
|
||||
case 'c':
|
||||
WritePacket("OK");
|
||||
break;
|
||||
case 'g':
|
||||
WritePacket("OK");
|
||||
break;
|
||||
default:
|
||||
WritePacket("E22");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'T':
|
||||
thread = strtol(p, (char **)&p, 16);
|
||||
WritePacket("OK");
|
||||
break;
|
||||
default:
|
||||
unknown_command:
|
||||
/* put empty packet */
|
||||
buf[0] = '\0';
|
||||
WritePacket(buf);
|
||||
break;
|
||||
}
|
||||
return RS_IDLE;
|
||||
}
|
||||
|
||||
void HandlePacket() {
|
||||
uint8_t ch = ReadByte();
|
||||
uint8_t reply;
|
||||
switch (state) {
|
||||
case RS_IDLE:
|
||||
if (ch == '$') {
|
||||
/* start of command packet */
|
||||
buf_index = 0;
|
||||
line_sum = 0;
|
||||
state = RS_GETLINE;
|
||||
} else {
|
||||
//printf("%s: received garbage between packets: 0x%x\n", __func__, ch);
|
||||
}
|
||||
break;
|
||||
case RS_GETLINE:
|
||||
if (ch == '}') {
|
||||
state = RS_GETLINE_ESC;
|
||||
line_sum += ch;
|
||||
} else if (ch == '*') {
|
||||
/* start run length encoding sequence */
|
||||
state = RS_GETLINE_RLE;
|
||||
line_sum += ch;
|
||||
} else if (ch == '#') {
|
||||
/* end of command, start of checksum*/
|
||||
state = RS_CHKSUM1;
|
||||
} else if (buf_index >= sizeof(cmd_buf) - 1) {
|
||||
state = RS_IDLE;
|
||||
ns_print ("Gdb: command buffer overrun, dropping command\n");
|
||||
} else {
|
||||
/* unescaped command character */
|
||||
cmd_buf[buf_index++] = ch;
|
||||
line_sum += ch;
|
||||
}
|
||||
break;
|
||||
case RS_GETLINE_ESC:
|
||||
if (ch == '#') {
|
||||
/* unexpected end of command in escape sequence */
|
||||
state = RS_CHKSUM1;
|
||||
} else if (buf_index >= sizeof(cmd_buf) - 1) {
|
||||
/* command buffer overrun */
|
||||
state = RS_IDLE;
|
||||
ns_print ("Gdb: command buffer overrun, dropping command\n");
|
||||
} else {
|
||||
/* parse escaped character and leave escape state */
|
||||
cmd_buf[buf_index++] = ch ^ 0x20;
|
||||
line_sum += ch;
|
||||
state = RS_GETLINE;
|
||||
}
|
||||
break;
|
||||
case RS_GETLINE_RLE:
|
||||
if (ch < ' ') {
|
||||
/* invalid RLE count encoding */
|
||||
state = RS_GETLINE;
|
||||
ns_print ("Gdb: got invalid RLE count: 0x%x\n", ch);
|
||||
} else {
|
||||
/* decode repeat length */
|
||||
int repeat = (unsigned char)ch - ' ' + 3;
|
||||
if (buf_index + repeat >= sizeof(cmd_buf) - 1) {
|
||||
/* that many repeats would overrun the command buffer */
|
||||
ns_print ("Gdb: command buffer overrun, dropping command\n");
|
||||
state = RS_IDLE;
|
||||
} else if (buf_index < 1) {
|
||||
/* got a repeat but we have nothing to repeat */
|
||||
ns_print ("Gdb: got invalid RLE sequence\n");
|
||||
state = RS_GETLINE;
|
||||
} else {
|
||||
/* repeat the last character */
|
||||
memset(cmd_buf + buf_index,
|
||||
cmd_buf[buf_index - 1], repeat);
|
||||
buf_index += repeat;
|
||||
line_sum += ch;
|
||||
state = RS_GETLINE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RS_CHKSUM1:
|
||||
/* get high hex digit of checksum */
|
||||
if (!IsXdigit(ch)) {
|
||||
ns_print("Gdb: got invalid command checksum digit\n");
|
||||
state = RS_GETLINE;
|
||||
break;
|
||||
}
|
||||
cmd_buf[buf_index] = '\0';
|
||||
checksum = FromHex(ch) << 4;
|
||||
state = RS_CHKSUM2;
|
||||
break;
|
||||
case RS_CHKSUM2:
|
||||
/* get low hex digit of checksum */
|
||||
if (!IsXdigit(ch)) {
|
||||
ns_print("Gdb: got invalid command checksum digit\n");
|
||||
state = RS_GETLINE;
|
||||
break;
|
||||
}
|
||||
checksum |= FromHex(ch);
|
||||
|
||||
if (checksum != (line_sum & 0xff)) {
|
||||
ns_print("Gdb: got command packet with incorrect checksum\n");
|
||||
/* send NAK reply */
|
||||
reply = '-';
|
||||
WriteBytes (&reply, 1);
|
||||
state = RS_IDLE;
|
||||
} else {
|
||||
ns_print("Gdb: valid packet!\n");
|
||||
/* send ACK reply */
|
||||
reply = '+';
|
||||
WriteBytes (&reply, 1);
|
||||
state = HandleCommand ((char *)cmd_buf);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ns_print("Gdb: got unknown status\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
12
Main.cpp
12
Main.cpp
|
@ -76,14 +76,15 @@ void Banner() {
|
|||
}
|
||||
|
||||
enum optionIndex {
|
||||
UNKNOWN, HELP, ENABLE_TRACE
|
||||
UNKNOWN, HELP, ENABLE_TRACE, ENABLE_GDB,
|
||||
};
|
||||
const option::Descriptor usage[] =
|
||||
{
|
||||
{ UNKNOWN, 0, "", "", Arg::None, "USAGE: nsemu [options] <nso-binary>\n\n"
|
||||
"Options:" },
|
||||
{ HELP, 0, "", "help", Arg::None, " --help \tPrint help message" },
|
||||
{ ENABLE_TRACE, 0, "t","enable-trace", Arg::None, " --enable-trace, -t \tEnable Trace" },
|
||||
{ ENABLE_TRACE, 0, "t","enable-trace", Arg::None, " --enable-trace, -t \tEnable Trace" },
|
||||
{ ENABLE_GDB, 0, "s","enable-gdb", Arg::None, " --enable-gdb -s \tEnable GDBServer" },
|
||||
{ 0, 0, nullptr, nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
@ -106,8 +107,11 @@ printUsage:
|
|||
option::printUsage (cout, usage);
|
||||
return 0;
|
||||
}
|
||||
if (options[ENABLE_TRACE].count () > 0) {
|
||||
InitTrace ("nsemu_trace.json");
|
||||
if (options[ENABLE_TRACE].count () > 0) {
|
||||
InitTrace ("nsemu_trace.json");
|
||||
}
|
||||
if (options[ENABLE_GDB].count () > 0) {
|
||||
GdbStub::Init();
|
||||
}
|
||||
#if 0
|
||||
if (options[NSO].count () > 0) {
|
||||
|
|
42
include/GdbStub.hpp
Normal file
42
include/GdbStub.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef _GDBSTUB_HPP
|
||||
#define _GDBSTUB_HPP
|
||||
|
||||
#define GDB_BREAKPOINT_SW 0
|
||||
#define GDB_BREAKPOINT_HW 1
|
||||
#define GDB_WATCHPOINT_WRITE 2
|
||||
#define GDB_WATCHPOINT_READ 3
|
||||
#define GDB_WATCHPOINT_ACCESS 4
|
||||
|
||||
#define GDB_BUFFER_SIZE 4096
|
||||
|
||||
namespace GdbStub {
|
||||
|
||||
extern volatile bool enabled;
|
||||
|
||||
enum RSState {
|
||||
RS_INACTIVE,
|
||||
RS_IDLE,
|
||||
RS_GETLINE,
|
||||
RS_GETLINE_ESC,
|
||||
RS_GETLINE_RLE,
|
||||
RS_CHKSUM1,
|
||||
RS_CHKSUM2,
|
||||
};
|
||||
|
||||
enum {
|
||||
GDB_SIGNAL_0 = 0,
|
||||
GDB_SIGNAL_INT = 2,
|
||||
GDB_SIGNAL_QUIT = 3,
|
||||
GDB_SIGNAL_TRAP = 5,
|
||||
GDB_SIGNAL_ABRT = 6,
|
||||
GDB_SIGNAL_ALRM = 14,
|
||||
GDB_SIGNAL_IO = 23,
|
||||
GDB_SIGNAL_XCPU = 24,
|
||||
GDB_SIGNAL_UNKNOWN = 143
|
||||
};
|
||||
|
||||
void Init();
|
||||
void HandlePacket();
|
||||
|
||||
};
|
||||
#endif
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
#include "GdbStub.hpp"
|
||||
#include "Memory.hpp"
|
||||
#include "Util.hpp"
|
||||
#include "NintendoObject.hpp"
|
||||
|
|
Loading…
Reference in a new issue