remove gdbstub

This commit is contained in:
CocoSimone
2023-02-19 12:12:11 +01:00
parent 9837919678
commit 6e3b81655e
6 changed files with 6 additions and 883 deletions

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 Stephen Lane-Walsh
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,553 +0,0 @@
//
// gdbstub version 1.1.1
//
// MIT License
//
// Copyright (c) 2020 Stephen Lane-Walsh
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#ifndef GDBSTUB_H
#define GDBSTUB_H
#include <cstdint>
#include <sys/types.h>
typedef void (*gdbstub_connected_t)(void * user_data);
typedef void (*gdbstub_disconnected_t)(void * user_data);
typedef void (*gdbstub_start_t)(void * user_data);
typedef void (*gdbstub_stop_t)(void * user_data);
typedef void (*gdbstub_step_t)(void * user_data);
typedef void (*gdbstub_set_breakpoint_t)(void * user_data, uint32_t address);
typedef void (*gdbstub_clear_breakpoint_t)(void * user_data, uint32_t address);
typedef size_t (*gdbstub_get_memory_t)(void * user_data, char * buffer, size_t buffer_length, uint32_t address, size_t length);
typedef size_t (*gdbstub_get_register_value_t)(void * user_data, char * buffer, size_t buffer_length, int reg);
typedef size_t (*gdbstub_get_general_registers_t)(void * user_data, char * buffer, size_t buffer_length);
typedef struct gdbstub_config gdbstub_config_t;
struct gdbstub_config
{
uint16_t port;
void * user_data;
gdbstub_connected_t connected;
gdbstub_disconnected_t disconnected;
gdbstub_start_t start;
gdbstub_stop_t stop;
gdbstub_step_t step;
gdbstub_set_breakpoint_t set_breakpoint;
gdbstub_clear_breakpoint_t clear_breakpoint;
gdbstub_get_memory_t get_memory;
gdbstub_get_register_value_t get_register_value;
gdbstub_get_general_registers_t get_general_registers;
const char * target_config;
size_t target_config_length;
const char * memory_map;
size_t memory_map_length;
};
typedef struct gdbstub gdbstub_t;
gdbstub_t * gdbstub_init(gdbstub_config_t config);
void gdbstub_term(gdbstub_t * gdb);
void gdbstub_tick(gdbstub_t * gdb);
void gdbstub_breakpoint_hit(gdbstub_t * gdb);
#endif // GDBSTUB_H
#ifdef GDBSTUB_IMPLEMENTATION
#include <cerrno>
#include <fcntl.h>
#include <netinet/in.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#if defined(WIN32)
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "Ws2_32.lib")
#define close closesocket
#else
#include <unistd.h>
#endif
#define GDBSTUB_BUFFER_LENGTH 4096
#define GDBSTUB_PACKET_LENGTH 2048
typedef enum
{
GDB_STATE_NO_PACKET,
GDB_STATE_IN_PACKET,
GDB_STATE_IN_CHECKSUM,
} gdbstate_t;
struct gdbstub
{
gdbstub_config_t config;
int server;
int client;
char buffer[GDBSTUB_BUFFER_LENGTH];
size_t buffer_length;
gdbstate_t state;
char packet[GDBSTUB_BUFFER_LENGTH];
size_t packet_length;
uint8_t packet_checksum;
char checksum[2];
size_t checksum_length;
};
void _gdbstub_recv(gdbstub_t * gdb);
void _gdbstub_send(gdbstub_t * gdb, const char * data, size_t data_length);
void _gdbstub_send_paged(gdbstub_t * gdb, int offset, int length, const char * data, size_t data_length);
void _gdbstub_process_packet(gdbstub_t * gdb);
gdbstub_t * gdbstub_init(gdbstub_config_t config)
{
gdbstub_t * gdb = (gdbstub_t *)malloc(sizeof(gdbstub_t));
if (!gdb) {
fprintf(stderr, "out of memory\n");
return NULL;
}
gdb->config = config;
gdb->server = -1;
gdb->client = -1;
gdb->state = GDB_STATE_NO_PACKET;
gdb->packet_length = 0;
gdb->packet_checksum = 0;
gdb->checksum_length = 0;
int result;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(gdb->config.port);
addr.sin_addr.s_addr = INADDR_ANY;
gdb->server = socket(AF_INET, SOCK_STREAM, 0);
if (gdb->server < 0) {
perror("socket failed");
free(gdb);
return NULL;
}
result = fcntl(gdb->server, F_SETFL, fcntl(gdb->server, F_GETFL, 0) | O_NONBLOCK);
if (result < 0) {
perror("fcntl O_NONBLOCK failed");
free(gdb);
return NULL;
}
int reuse = 1;
result = setsockopt(gdb->server, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse));
if (result < 0) {
perror("setsockopt SO_REUSEADDR failed");
free(gdb);
return NULL;
}
#ifdef SO_REUSEPORT
result = setsockopt(gdb->server, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse));
if (result < 0) {
perror("setsockopt SO_REUSEPORT failed");
free(gdb);
return NULL;
}
#endif
result = bind(gdb->server, (struct sockaddr *)&addr, sizeof(addr));
if (result < 0) {
perror("bind failed");
free(gdb);
return NULL;
}
result = listen(gdb->server, 1);
if (result < 0) {
perror("listen failed");
free(gdb);
return NULL;
}
printf("listening for gdb on port %hu\n", gdb->config.port);
return gdb;
}
void gdbstub_term(gdbstub_t * gdb)
{
if (gdb) {
if (gdb->client >= 0) {
close(gdb->client);
gdb->client = -1;
}
if (gdb->server >= 0) {
close(gdb->server);
gdb->server = -1;
}
free(gdb);
}
}
void gdbstub_tick(gdbstub_t * gdb)
{
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
if (gdb->client < 0) {
gdb->client = accept(gdb->server, (struct sockaddr *)&addr, &addrlen);
if (gdb->client >= 0) {
printf("accepted gdb connection\n");
fcntl(gdb->client, F_SETFL, fcntl(gdb->client, F_GETFL, 0) | O_NONBLOCK);
if (gdb->config.connected) {
gdb->config.connected(gdb->config.user_data);
}
}
}
else {
_gdbstub_recv(gdb);
}
}
void gdbstub_breakpoint_hit(gdbstub_t * gdb) {
_gdbstub_send(gdb, "T05", 3);
}
void _gdbstub_send(gdbstub_t * gdb, const char * data, size_t data_length)
{
uint8_t checksum = 0;
for (size_t i = 0; i < data_length; ++i) {
checksum += data[i];
}
gdb->packet_length = snprintf(gdb->packet, GDBSTUB_PACKET_LENGTH, "$%s#%02x", data, checksum);
int bytes = send(gdb->client, gdb->packet, gdb->packet_length, 0);
# if defined(GDBSTUB_DEBUG)
printf("gdbstub sent '%s'\n", gdb->packet);
# endif
if (bytes < 0) {
perror("lost gdb connection");
close(gdb->client);
gdb->client = -1;
gdb->state = GDB_STATE_NO_PACKET;
if (gdb->config.disconnected) {
gdb->config.disconnected(gdb->config.user_data);
}
}
}
void _gdbstub_send_paged(gdbstub_t * gdb, int offset, int length, const char * data, size_t data_length)
{
if (length > GDBSTUB_PACKET_LENGTH - 6) {
length = GDBSTUB_PACKET_LENGTH - 6;
}
if (length < (data_length - offset)) {
// Any page but the last
gdb->buffer_length = snprintf(gdb->buffer, GDBSTUB_BUFFER_LENGTH, "m%.*s", length, data + offset);
_gdbstub_send(gdb, gdb->buffer, gdb->buffer_length);
}
else if (offset != 0) {
// The last page
gdb->buffer_length = snprintf(gdb->buffer, GDBSTUB_BUFFER_LENGTH, "l%.*s", length, data + offset);
_gdbstub_send(gdb, gdb->buffer, gdb->buffer_length);
}
else {
// The only page
gdb->buffer_length = snprintf(gdb->buffer, GDBSTUB_BUFFER_LENGTH, "l%s", data);
_gdbstub_send(gdb, gdb->buffer, gdb->buffer_length);
}
}
void _gdbstub_recv(gdbstub_t * gdb)
{
gdb->buffer_length = recv(gdb->client, gdb->buffer, GDBSTUB_BUFFER_LENGTH, 0);
if (gdb->buffer_length < 0 && errno != EAGAIN) {
perror("lost gdb connection");
close(gdb->client);
gdb->client = -1;
gdb->state = GDB_STATE_NO_PACKET;
if (gdb->config.disconnected) {
gdb->config.disconnected(gdb->config.user_data);
}
return;
}
for (size_t i = 0; i < gdb->buffer_length; ++i) {
char c = gdb->buffer[i];
switch (gdb->state)
{
case GDB_STATE_NO_PACKET:
if (c == '$') {
gdb->state = GDB_STATE_IN_PACKET;
gdb->packet_length = 0;
gdb->packet_checksum = 0;
}
else if (c == 3) {
// GDB Interrupt '0x03' Packet
gdb->config.stop(gdb->config.user_data);
_gdbstub_send(gdb, "S00", 3);
}
break;
case GDB_STATE_IN_PACKET:
if (c == '#') {
gdb->state = GDB_STATE_IN_CHECKSUM;
gdb->checksum_length = 0;
}
else {
gdb->packet[gdb->packet_length++] = c;
gdb->packet_checksum += c;
}
break;
case GDB_STATE_IN_CHECKSUM:
gdb->checksum[gdb->checksum_length++] = c;
if (gdb->checksum_length == 2) {
int checksum;
sscanf(gdb->checksum, "%2x", &checksum);
if (gdb->packet_checksum != checksum) {
// freak out?
}
send(gdb->client, "+", 1, 0);
gdb->packet[gdb->packet_length] = '\0';
# if defined(GDBSTUB_DEBUG)
printf("gdbstub received '$%s#%c%c'\n", gdb->packet, gdb->checksum[0], gdb->checksum[1]);
# endif
_gdbstub_process_packet(gdb);
gdb->state = GDB_STATE_NO_PACKET;
}
}
}
}
void _gdbstub_process_packet(gdbstub_t * gdb)
{
switch (gdb->packet[0])
{
case 'c':
// Continue execution
if (gdb->config.start) {
gdb->config.start(gdb->config.user_data);
}
return;
case 'D':
// Disconnect
printf("gdb disconnected\n");
_gdbstub_send(gdb, "OK", 2);
close(gdb->client);
gdb->client = -1;
gdb->state = GDB_STATE_NO_PACKET;
if (gdb->config.disconnected) {
gdb->config.disconnected(gdb->config.user_data);
}
return;
case 'g':
// Get general registers
if (gdb->config.get_general_registers) {
gdb->buffer_length = gdb->config.get_general_registers(gdb->config.user_data, gdb->buffer, GDBSTUB_BUFFER_LENGTH);
_gdbstub_send(gdb, gdb->buffer, gdb->buffer_length);
}
else {
_gdbstub_send(gdb, "", 0);
}
return;
case 'H':
// Set active thread
_gdbstub_send(gdb, "OK", 2);
return;
case 'm':
// Read memory
if (gdb->config.get_memory) {
int address, length;
sscanf(gdb->packet + 1, "%x,%x", &address, &length);
gdb->buffer_length = gdb->config.get_memory(gdb->config.user_data, gdb->buffer, GDBSTUB_BUFFER_LENGTH, address, length);
_gdbstub_send(gdb, gdb->buffer, gdb->buffer_length);
}
else {
_gdbstub_send(gdb, "", 0);
}
return;
case 'p':
// Read the value of register n
if (gdb->config.get_register_value) {
int reg;
sscanf(gdb->packet + 1, "%x", &reg);
gdb->buffer_length = gdb->config.get_register_value(gdb->config.user_data, gdb->buffer, GDBSTUB_BUFFER_LENGTH, reg);
_gdbstub_send(gdb, gdb->buffer, gdb->buffer_length);
}
else {
_gdbstub_send(gdb, "", 0);
}
return;
case 'q':
// Check for available features
if (strncmp(gdb->packet, "qSupported", 10) == 0) {
strcpy(gdb->buffer, "PacketSize=1024");
gdb->buffer_length = 15;
if (gdb->config.target_config) {
strcpy(gdb->buffer + gdb->buffer_length, ";qXfer:features:read+");
gdb->buffer_length += 21;
}
if (gdb->config.memory_map) {
strcpy(gdb->buffer + gdb->buffer_length, ";qXfer:memory-map:read+");
gdb->buffer_length += 23;
}
_gdbstub_send(gdb, gdb->buffer, gdb->buffer_length);
return;
}
// We have no thread ID
else if (gdb->packet[1] == 'C') {
_gdbstub_send(gdb, "QC00", 4);
return;
}
// We are always "attached" to an existing process
else if (strncmp(gdb->packet, "qAttached", 9) == 0) {
_gdbstub_send(gdb, "1", 1);
return;
}
// There is no trace running
else if (strncmp(gdb->packet, "qTStatus", 8) == 0) {
_gdbstub_send(gdb, "T0", 2);
return;
}
// Target configuration XML
else if (strncmp(gdb->packet, "qXfer:features:read:target.xml:", 31) == 0) {
int offset, length;
sscanf(gdb->packet + 31, "%x,%x", &offset, &length);
_gdbstub_send_paged(gdb, offset, length, gdb->config.target_config, gdb->config.target_config_length);
return;
}
// Memory map XML
else if (strncmp(gdb->packet, "qXfer:memory-map:read::", 23) == 0) {
int offset, length;
sscanf(gdb->packet + 23, "%x,%x", &offset, &length);
_gdbstub_send_paged(gdb, offset, length, gdb->config.memory_map, gdb->config.memory_map_length);
return;
}
// Trace control operations
else if (strncmp(gdb->packet, "qTfP", 4) == 0) {
_gdbstub_send(gdb, "", 0);
return;
}
else if (strncmp(gdb->packet, "qTfV", 4) == 0) {
_gdbstub_send(gdb, "", 0);
return;
}
else if (strncmp(gdb->packet, "qTsP", 4) == 0) {
_gdbstub_send(gdb, "", 0);
return;
}
// Thread #0
else if (strncmp(gdb->packet, "qfThreadInfo", 12) == 0) {
_gdbstub_send(gdb, "lm0", 3);
return;
}
// GDB is ready to serve symbol requests
else if (strncmp(gdb->packet, "qSymbol", 7) == 0) {
_gdbstub_send(gdb, "OK", 2);
return;
}
break;
case 's':
// Single execution step
if (gdb->config.step) {
gdb->config.step(gdb->config.user_data);
}
_gdbstub_send(gdb, "T05", 3);
return;
case 'v':
// Various remote operations, not supported
_gdbstub_send(gdb, "", 0);
return;
case 'z':
// Remove breakpoint
if (gdb->config.clear_breakpoint) {
uint32_t address;
sscanf(gdb->packet, "z0,%x", &address);
gdb->config.clear_breakpoint(gdb->config.user_data, address);
}
_gdbstub_send(gdb, "OK", 2);
break;
case 'Z':
// Add breakpoint
if (gdb->config.set_breakpoint) {
uint32_t address;
sscanf(gdb->packet, "Z0,%x", &address);
gdb->config.set_breakpoint(gdb->config.user_data, address);
}
_gdbstub_send(gdb, "OK", 2);
break;
case '?':
// Break immediately
if (gdb->config.stop) {
gdb->config.stop(gdb->config.user_data);
}
// TODO: improve
_gdbstub_send(gdb, "S00", 3);
return;
}
fprintf(stderr, "unknown gdb command '%s'\n", gdb->packet);
}
#endif // GDBSTUB_IMPLEMENTATION

View File

@@ -35,7 +35,6 @@ include_directories(
../external/imgui/imgui
../external/imgui/imgui/backends
../external/discord-rpc/include
../external/gdbstub
${SDL2_INCLUDE_DIRS}
)

View File

@@ -2,7 +2,3 @@ file(GLOB SOURCES *.cpp)
file(GLOB HEADERS *.hpp)
add_library(backend ${SOURCES} ${HEADERS})
if(WIN32)
target_compile_definitions(backend PUBLIC DISABLE_GDB_STUB)
endif()

View File

@@ -1,293 +1 @@
#include <Debugger.hpp>
#ifndef DISABLE_GDB_STUB
#define GDBSTUB_IMPLEMENTATION
#include <gdbstub.h>
#include <log.hpp>
#include <Core.hpp>
const char* target_xml =
"<?xml version=\"1.0\"?>"
"<!DOCTYPE feature SYSTEM \"gdb-target.dtd\">"
"<target version=\"1.0\">"
"<architecture>mips:4000</architecture>"
"<osabi>none</osabi>"
"<feature name=\"org.gnu.gdb.mips.cpu\">"
" <reg name=\"r0\" bitsize=\"64\" regnum=\"0\"/>"
" <reg name=\"r1\" bitsize=\"64\"/>"
" <reg name=\"r2\" bitsize=\"64\"/>"
" <reg name=\"r3\" bitsize=\"64\"/>"
" <reg name=\"r4\" bitsize=\"64\"/>"
" <reg name=\"r5\" bitsize=\"64\"/>"
" <reg name=\"r6\" bitsize=\"64\"/>"
" <reg name=\"r7\" bitsize=\"64\"/>"
" <reg name=\"r8\" bitsize=\"64\"/>"
" <reg name=\"r9\" bitsize=\"64\"/>"
" <reg name=\"r10\" bitsize=\"64\"/>"
" <reg name=\"r11\" bitsize=\"64\"/>"
" <reg name=\"r12\" bitsize=\"64\"/>"
" <reg name=\"r13\" bitsize=\"64\"/>"
" <reg name=\"r14\" bitsize=\"64\"/>"
" <reg name=\"r15\" bitsize=\"64\"/>"
" <reg name=\"r16\" bitsize=\"64\"/>"
" <reg name=\"r17\" bitsize=\"64\"/>"
" <reg name=\"r18\" bitsize=\"64\"/>"
" <reg name=\"r19\" bitsize=\"64\"/>"
" <reg name=\"r20\" bitsize=\"64\"/>"
" <reg name=\"r21\" bitsize=\"64\"/>"
" <reg name=\"r22\" bitsize=\"64\"/>"
" <reg name=\"r23\" bitsize=\"64\"/>"
" <reg name=\"r24\" bitsize=\"64\"/>"
" <reg name=\"r25\" bitsize=\"64\"/>"
" <reg name=\"r26\" bitsize=\"64\"/>"
" <reg name=\"r27\" bitsize=\"64\"/>"
" <reg name=\"r28\" bitsize=\"64\"/>"
" <reg name=\"r29\" bitsize=\"64\"/>"
" <reg name=\"r30\" bitsize=\"64\"/>"
" <reg name=\"r31\" bitsize=\"64\"/>"
" <reg name=\"lo\" bitsize=\"64\" regnum=\"33\"/>"
" <reg name=\"hi\" bitsize=\"64\" regnum=\"34\"/>"
" <reg name=\"pc\" bitsize=\"64\" regnum=\"37\"/>"
" </feature>"
"<feature name=\"org.gnu.gdb.mips.cp0\">"
" <reg name=\"status\" bitsize=\"32\" regnum=\"32\"/>"
" <reg name=\"badvaddr\" bitsize=\"32\" regnum=\"35\"/>"
" <reg name=\"cause\" bitsize=\"32\" regnum=\"36\"/>"
" </feature>"
"<!-- TODO fix the sizes here. How do we deal with configurable sizes? -->"
"<feature name=\"org.gnu.gdb.mips.fpu\">"
" <reg name=\"f0\" bitsize=\"32\" type=\"ieee_single\" regnum=\"38\"/>"
" <reg name=\"f1\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f2\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f3\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f4\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f5\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f6\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f7\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f8\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f9\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f10\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f11\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f12\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f13\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f14\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f15\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f16\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f17\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f18\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f19\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f20\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f21\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f22\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f23\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f24\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f25\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f26\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f27\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f28\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f29\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f30\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"f31\" bitsize=\"32\" type=\"ieee_single\"/>"
" <reg name=\"fcsr\" bitsize=\"32\" group=\"float\"/>"
" <reg name=\"fir\" bitsize=\"32\" group=\"float\"/>"
" </feature>"
"</target>";
const char* memory_map =
"<?xml version=\"1.0\"?>"
"<memory-map>"
"<!-- KUSEG - TLB mapped, treat it as a giant block of RAM. Not ideal, but not sure how else to deal with it -->"
"<memory type=\"ram\" start=\"0x0000000000000000\" length=\"0x80000000\"/>"
"<!-- KSEG0 hardware mapped, full copy of the memory map goes here -->" // TODO finish
"<memory type=\"ram\" start=\"0xffffffff80000000\" length=\"0x800000\"/>" // RDRAM
"<memory type=\"ram\" start=\"0xffffffff84000000\" length=\"0x1000\"/>" // RSP DMEM
"<memory type=\"ram\" start=\"0xffffffff84001000\" length=\"0x1000\"/>" // RSP IMEM
"<memory type=\"rom\" start=\"0xffffffff9fc00000\" length=\"0x7c0\"/>" // PIF ROM
"<!-- KSEG1 hardware mapped, full copy of the memory map goes here -->" // TODO finish
"<memory type=\"ram\" start=\"0xffffffffa0000000\" length=\"0x800000\"/>" // RDRAM
"<memory type=\"ram\" start=\"0xffffffffa4000000\" length=\"0x1000\"/>" // RSP DMEM
"<memory type=\"ram\" start=\"0xffffffffa4001000\" length=\"0x1000\"/>" // RSP IMEM
"<memory type=\"rom\" start=\"0xffffffffbfc00000\" length=\"0x7c0\"/>" // PIF ROM
"</memory-map>";
void debugStart(void* user_data) {
auto* debugger = (Debugger*)user_data;
debugger->broken = false;
}
void debugStop(void* user_data) {
auto* debugger = (Debugger*)user_data;
debugger->broken = true;
}
void debugStep(void* user_data) {
auto* debugger = (Debugger*)user_data;
bool old_broken = debugger->broken;
debugger->broken = false;
n64::Core::CpuStep(debugger->core);
debugger->broken = old_broken;
debugger->steps += 2;
}
void debugSetBreakpoint(void* user_data, u32 address) {
auto* debugger = (Debugger*)user_data;
auto* breakpoint = (Breakpoint*)malloc(sizeof(Breakpoint));
breakpoint->address = address;
breakpoint->next = nullptr;
// Special case for this being the first breakpoint
if (debugger->breakpoints == nullptr) {
debugger->breakpoints = breakpoint;
} else {
// Find end of the list
auto* tail = debugger->breakpoints;
while (tail->next != nullptr) {
tail = tail->next;
}
tail->next = breakpoint;
}
}
void debugClearBreakpoint(void* user_data, u32 address) {
auto* debugger = (Debugger*)user_data;
if (debugger->breakpoints == nullptr) {
return; // No breakpoints set at all
} else if (debugger->breakpoints->address == address) {
// Special case for the first breakpoint being the one we want to clear
auto* next = debugger->breakpoints->next;
free(debugger->breakpoints);
debugger->breakpoints = next;
} else {
// Find the breakpoint somewhere in the list and free it
auto* iter = debugger->breakpoints;
while (iter->next != nullptr) {
if (iter->next->address == address) {
auto* next = iter->next->next;
free(iter->next);
iter->next = next;
}
}
}
}
size_t debugGetMemory(void* user_data, char* buffer, size_t length, u32 address, size_t bytes) {
auto* debugger = (Debugger*)user_data;
printf("Checking memory at address 0x%08X\n", address);
int printed = 0;
u32 paddr;
if(!n64::MapVAddr(debugger->core.CpuGetRegs(), n64::LOAD, address, paddr)) {
return 0;
}
for (int i = 0; i < bytes; i++) {
u8 value = debugger->core.mem.Read8(debugger->core.CpuGetRegs(), paddr + i);
printed += snprintf(buffer + (i*2), length, "%02X", value);
}
printf("Get memory: %ld bytes from 0x%08X: %d\n", bytes, paddr, printed);
return printed + 1;
}
size_t debugGetRegisterValue(void* user_data, char * buffer, size_t buffer_length, int reg) {
auto* debugger = (Debugger*)user_data;
switch (reg) {
case 0 ... 31:
return snprintf(buffer, buffer_length, "%016lx", debugger->core.CpuGetRegs().gpr[reg]);
case 32:
return snprintf(buffer, buffer_length, "%08x", debugger->core.CpuGetRegs().cop0.status.raw);
case 33:
return snprintf(buffer, buffer_length, "%016lx", debugger->core.CpuGetRegs().lo);
case 34:
return snprintf(buffer, buffer_length, "%016lx", debugger->core.CpuGetRegs().hi);
case 35:
return snprintf(buffer, buffer_length, "%016lx", debugger->core.CpuGetRegs().cop0.badVaddr);
case 36:
return snprintf(buffer, buffer_length, "%08x", debugger->core.CpuGetRegs().cop0.cause.raw);
case 37:
//printf("Sending PC: 0x%016lX\n", debugger->core.CpuGetRegs().pc);
return snprintf(buffer, buffer_length, "%016lx", debugger->core.CpuGetRegs().pc);
case 38 ... 71: // TODO FPU stuff
return snprintf(buffer, buffer_length, "%08x", 0);
default:
Util::panic("Debug get register %d value\n", reg);
}
}
size_t debugGetGeneralRegisters(void* user_data, char * buffer, size_t buffer_length) {
auto* debugger = (Debugger*)user_data;
printf("The buffer length is %ld!\n", buffer_length);
size_t printed = 0;
for (int i = 0; i < 32; i++) {
int ofs = i * 16; // 64 bit regs take up 16 ascii chars to print in hex
if (ofs + 16 > buffer_length) {
Util::panic("Too big!\n");
}
u64 reg = debugger->core.CpuGetRegs().gpr[i];
printed += snprintf(buffer + ofs, buffer_length - ofs, "%016lx", reg);
}
return printed;
}
Debugger::Debugger(n64::Core& core) : core(core) {
gdbstub_config_t config;
memset(&config, 0, sizeof(gdbstub_config_t));
config.port = 1337;
config.user_data = this;
config.start = (gdbstub_start_t) debugStart;
config.stop = (gdbstub_stop_t) debugStop;
config.step = (gdbstub_step_t) debugStep;
config.set_breakpoint = (gdbstub_set_breakpoint_t) debugSetBreakpoint;
config.clear_breakpoint = (gdbstub_clear_breakpoint_t) debugClearBreakpoint;
config.get_memory = (gdbstub_get_memory_t) debugGetMemory;
config.get_register_value = (gdbstub_get_register_value_t) debugGetRegisterValue;
config.get_general_registers = (gdbstub_get_general_registers_t) debugGetGeneralRegisters;
config.target_config = target_xml;
config.target_config_length = strlen(target_xml);
printf("Sizeof target: %ld\n", config.target_config_length);
config.memory_map = memory_map;
config.memory_map_length = strlen(memory_map);
printf("Sizeof memory map: %ld\n", config.memory_map_length);
gdb = gdbstub_init(config);
if (!gdb) {
Util::panic("Failed to initialize GDB stub!\n");
}
}
Debugger::~Debugger() {
if(enabled) {
gdbstub_term(gdb);
}
}
void Debugger::tick() const {
gdbstub_tick(gdb);
}
void Debugger::breakpointHit() {
broken = true;
gdbstub_breakpoint_hit(gdb);
}
#else
Debugger::Debugger(n64::Core& core) {
}
Debugger::~Debugger() {
}
void Debugger::tick() const {
}
void Debugger::breakpointHit() {
}
#endif

View File

@@ -1,6 +1,5 @@
#pragma once
#include <common.hpp>
#include <gdbstub.h>
struct Breakpoint {
u32 address = 0;
@@ -10,13 +9,10 @@ struct Breakpoint {
namespace n64 { struct Core; }
struct Debugger {
Debugger(n64::Core& core);
~Debugger();
Debugger(n64::Core& core) :core(core) {}
~Debugger() = default;
bool broken = false, enabled = true;
#ifndef DISABLE_GDB_STUB
int steps = 0;
gdbstub_t* gdb;
Breakpoint* breakpoints = nullptr;
n64::Core& core;
@@ -30,9 +26,7 @@ struct Debugger {
}
return false;
}
#else
inline bool checkBreakpoint(u32 address) const { return false; }
#endif
void tick() const;
void breakpointHit();
void tick() const {}
void breakpointHit() {}
};