This commit is contained in:
Zeck 2023-09-10 23:37:06 -05:00
parent fba8a57cf1
commit 83df1b49a5
15 changed files with 405 additions and 9 deletions

3
.gitignore vendored
View file

@ -1,2 +1,5 @@
/projects/unix/_obj*/
/projects/unix/libmupen64plus*.so*
/.vscode
/projects/unix/*.dll
/src/asm_defines/*.h

View file

@ -729,16 +729,17 @@ ifeq ($(DEBUGGER), 1)
$(SRCDIR)/debugger/dbg_debugger.c \
$(SRCDIR)/debugger/dbg_decoder.c \
$(SRCDIR)/debugger/dbg_memory.c \
$(SRCDIR)/debugger/dbg_breakpoints.c
LDLIBS += -lopcodes -lbfd
$(SRCDIR)/debugger/dbg_breakpoints.c \
$(SRCDIR)/debugger/gdbstub/gdbstub.cpp
LDLIBS += -lopcodes -lbfd -liberty -lintl -liconv -lsframe -lz -lzstd -lSDL2_net
# UGLY libopcodes/libbfd version check (we check for >= 2.28 and >= 2.39)
LIBOPCODES_VERSION := $(shell $(STRINGS) --version | head -n1 | rev | cut -d ' ' -f1 | rev)
LIBOPCODES_MAJOR := $(shell echo $(LIBOPCODES_VERSION) | cut -f1 -d.)
LIBOPCODES_MINOR := $(shell echo $(LIBOPCODES_VERSION) | cut -f2 -d.)
LIBOPCODES_POINT := $(shell echo $(LIBOPCODES_VERSION) | cut -f3 -d.)
LIBOPCODES_GE_2_29 := $(shell [ $(LIBOPCODES_MAJOR) -gt 2 -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -ge 29 \) -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -eq 28 -a $(LIBOPCODES_POINT) -ge 1 \) ] && echo true)
LIBBFD_GE_2_39 := $(shell [ $(LIBOPCODES_MAJOR) -gt 2 -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -ge 29 \) -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -eq 39 -a $(LIBOPCODES_POINT) -ge 1 \) ] && echo true)
LIBOPCODES_POINT := $(shell echo $(LIBOPCODES_VERSION) | cut -f3 -d. | sed 's/^$$/0/')
LIBOPCODES_GE_2_29 := $(shell [ $(LIBOPCODES_MAJOR) -gt 2 -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -ge 28 \) -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -eq 28 -a $(LIBOPCODES_POINT) -ge 1 \) ] && echo true)
LIBBFD_GE_2_39 := $(shell [ $(LIBOPCODES_MAJOR) -gt 2 -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -ge 39 \) -o \( $(LIBOPCODES_MAJOR) -eq 2 -a $(LIBOPCODES_MINOR) -eq 39 -a $(LIBOPCODES_POINT) -ge 1 \) ] && echo true)
ifeq ($(LIBOPCODES_GE_2_29),true)
CFLAGS += -DUSE_LIBOPCODES_GE_2_29
endif

View file

@ -29,6 +29,11 @@
#include "m64p_frontend.h"
#include "m64p_types.h"
#ifdef __cplusplus
extern "C"
{
#endif
#if defined(__GNUC__)
#define ATTR_FMT(fmtpos, attrpos) __attribute__ ((format (printf, fmtpos, attrpos)))
#else
@ -41,5 +46,9 @@ extern m64p_error SetStateCallback(ptr_StateCallback pFunc, void *Context);
extern void DebugMessage(int level, const char *message, ...) ATTR_FMT(2,3);
extern void StateChanged(m64p_core_param param_type, int new_value);
#ifdef __cplusplus
}
#endif
#endif /* API_CALLBACKS_H */

View file

@ -447,7 +447,7 @@ EXPORT uint32_t CALL DebugVirtualToPhysical(uint32_t address)
struct r4300_core* r4300 = &dev->r4300;
if ((address & UINT32_C(0xc0000000)) != UINT32_C(0x80000000)) {
address = virtual_to_physical_address(r4300, address, 0);
address = virtual_to_physical_address_no_tlb_refill_exception(r4300, address, 0);
if (address == 0) {
return 0;
}

View file

@ -48,6 +48,7 @@
#include "main/netplay.h"
#include "plugin/plugin.h"
#include "vidext.h"
#include "debugger/gdbstub/gdbstub.h"
/* some local state variables */
static int l_CoreInit = 0;
@ -108,6 +109,10 @@ EXPORT m64p_error CALL CoreStartup(int APIVersion, const char *ConfigPath, const
workqueue_init();
#if defined(DBG)
gdbstub_init();
#endif
l_CoreInit = 1;
return M64ERR_SUCCESS;
}

View file

@ -26,6 +26,11 @@
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif
struct memory;
extern int g_NumBreakpoints;
@ -43,5 +48,9 @@ int lookup_breakpoint(uint32_t address, uint32_t size, uint32_t flags);
int log_breakpoint(uint32_t PC, uint32_t Flag, uint32_t Access);
void replace_breakpoint_num(struct memory* mem, int, m64p_breakpoint*);
#ifdef __cplusplus
}
#endif
#endif /* __BREAKPOINTS_H__ */

View file

@ -27,6 +27,7 @@
#include "dbg_breakpoints.h"
#include "dbg_debugger.h"
#include "dbg_memory.h"
#include "gdbstub/gdbstub.h"
#ifdef DBG
@ -94,6 +95,7 @@ void update_debugger(uint32_t pc)
DebuggerCallback(DEBUG_UI_UPDATE, pc); /* call front-end to notify user interface to update */
}
if (g_dbg_runstate == M64P_DBG_RUNSTATE_PAUSED) {
gdb_try_send_signal_stop();
// The emulation thread is blocked until a step call via the API.
SDL_SemWait(sem_pending_steps);
}

View file

@ -25,6 +25,11 @@
#include "api/m64p_types.h"
#ifdef __cplusplus
extern "C"
{
#endif
extern int g_DebuggerActive; /* True if the debugger is running */
extern m64p_dbg_runstate g_dbg_runstate;
@ -39,5 +44,9 @@ void update_debugger(uint32_t pc);
void destroy_debugger(void);
void debugger_step(void);
#ifdef __cplusplus
}
#endif
#endif /* __DBG_DEBUGGER_H__ */

View file

@ -0,0 +1,296 @@
#include <SDL.h>
#include <SDL_net.h>
#include <numeric>
#include <string>
#include "gdbstub.h"
#include "api/callbacks.h"
#include "../dbg_debugger.h"
#include "../dbg_breakpoints.h"
#include "device/r4300/tlb.h"
#include "main/main.h"
#define BUFFER_SIZE 8192
char gdb_buffer[BUFFER_SIZE];
char gdb_send_buffer[BUFFER_SIZE];
TCPsocket gdb_socket;
auto sendLength = 0;
int gdb_loop(void *x);
void gdb_send_signal_stop();
void set_checksum_and_sendlength(int hash_index);
uint32_t DebugVirtualToPhysical(uint32_t address);
void gdbstub_init() {
#if SDL_VERSION_ATLEAST(2,0,0)
SDL_CreateThread(gdb_loop, "gdb_loop", NULL);
#else
SDL_CreateThread(gdb_loop, NULL);
#endif
}
int gdb_loop(void *x) {
if(SDL_Init(0) == -1) {
DebugMessage(M64MSG_ERROR, "Couldn't initialize SDL: %s", SDL_GetError());
return M64ERR_SYSTEM_FAIL;
}
if(SDLNet_Init() == -1) {
DebugMessage(M64MSG_ERROR, "Gdb stub: Could not initialize SDL Net library");
return M64ERR_SYSTEM_FAIL;
}
IPaddress serverIP;
SDLNet_ResolveHost(&serverIP, NULL, 5555);
auto serverSocket = SDLNet_TCP_Open(&serverIP);
if(serverSocket == nullptr) {
DebugMessage(M64MSG_ERROR, "Gdb stub: Server socket creation failed");
return M64ERR_SYSTEM_FAIL;
}
while(true)
{
gdb_socket = SDLNet_TCP_Accept(serverSocket);
while(gdb_socket != nullptr)
{
auto recvLength = SDLNet_TCP_Recv(gdb_socket, gdb_buffer, BUFFER_SIZE - 1);
if(recvLength <= 0) {
DebugMessage(M64MSG_ERROR, "Gdb stub: TCP receive error");
break;
}
auto& qSupported = "qSupported";
if(gdb_buffer[0] == '-') {
sendLength = 0;
} else if(gdb_buffer[0] == '+') {
gdb_send_buffer[0] = '+';
sendLength = 1;
} else if(gdb_buffer[0] == '\x03') {
g_dbg_runstate = M64P_DBG_RUNSTATE_PAUSED;
} else if(memcmp(gdb_buffer + 1, qSupported, sizeof(qSupported) - 1) == 0) {
auto& supported = "$PacketSize=1400;hwbreak+;";
strncpy(gdb_send_buffer, supported, sizeof(supported) - 1);
set_checksum_and_sendlength(sizeof(supported) - 1);
} else if(gdb_buffer[1] == 'g') {
auto regs = r4300_regs(&g_dev.r4300);
gdb_send_buffer[0] = '$';
for(auto i = 0; i < 32; i++) {
snprintf(gdb_send_buffer + 1 + i * 16, 17, "%016" SCNx64, regs[i]);
}
auto cp0reg = r4300_cp0_regs(&g_dev.r4300.cp0);
snprintf(gdb_send_buffer + 1 + 32 * 16, 17, "%016" SCNx64, (uint64_t) cp0reg[CP0_STATUS_REG]);
snprintf(gdb_send_buffer + 1 + 33 * 16, 17, "%016" SCNx64, (uint64_t) g_dev.r4300.lo);
snprintf(gdb_send_buffer + 1 + 34 * 16, 17, "%016" SCNx64, (uint64_t) g_dev.r4300.hi);
snprintf(gdb_send_buffer + 1 + 35 * 16, 17, "%016" SCNx64, (uint64_t) cp0reg[CP0_BADVADDR_REG]);
snprintf(gdb_send_buffer + 1 + 36 * 16, 17, "%016" SCNx64, (uint64_t) cp0reg[CP0_CAUSE_REG]);
snprintf(gdb_send_buffer + 1 + 37 * 16, 17, "%016" SCNx64, (uint64_t) *r4300_pc(&g_dev.r4300));
const auto reg_len = 8 * 2;
const auto non_gen_reg_count = 6;
const auto non_gen_reg_total_size = reg_len * non_gen_reg_count;
const auto gen_reg_count = 32;
const auto gen_reg_total_size = reg_len * gen_reg_count;
int message_end = 1 + gen_reg_total_size + non_gen_reg_total_size;
set_checksum_and_sendlength(message_end);
} else if(gdb_buffer[1] == 'G') {
auto regs = r4300_regs(&g_dev.r4300);
for(auto i = 0; i < 32; i++) {
uint64_t data;
char buf[17];
strncpy(buf, gdb_buffer + 2 + i * 16, sizeof(buf) - 1);
buf[16] = '\0';
sscanf(buf, "%" SCNx64, &data);
regs[i] = data;
}
auto cp0reg = r4300_cp0_regs(&g_dev.r4300.cp0);
uint64_t data;
const auto hex16str = sizeof("XXXXXXXXXXXXXXXX");
char buf[hex16str];
buf[hex16str - 1] = '\0';
strncpy(buf, gdb_buffer + 2 + 32 * 16, sizeof(buf) - 1);
sscanf(buf, "%" SCNx64, &data);
cp0reg[CP0_STATUS_REG] = data;
strncpy(buf, gdb_buffer + 2 + 33 * 16, sizeof(buf) - 1);
sscanf(buf, "%" SCNx64, &data);
g_dev.r4300.lo = data;
strncpy(buf, gdb_buffer + 2 + 34 * 16, sizeof(buf) - 1);
sscanf(buf, "%" SCNx64, &data);
g_dev.r4300.hi = data;
strncpy(buf, gdb_buffer + 2 + 35 * 16, sizeof(buf) - 1);
sscanf(buf, "%" SCNx64, &data);
cp0reg[CP0_BADVADDR_REG] = data;
strncpy(buf, gdb_buffer + 2 + 36 * 16, sizeof(buf) - 1);
sscanf(buf, "%" SCNx64, &data);
cp0reg[CP0_CAUSE_REG] = data;
strncpy(buf, gdb_buffer + 2 + 37 * 16, sizeof(buf) - 1);
sscanf(buf, "%" SCNx64, &data);
*r4300_pc(&g_dev.r4300) = data;
strncpy(gdb_send_buffer, "$OK", sizeof("$OK") - 1);
set_checksum_and_sendlength(sizeof("$OK") - 1);
} else if(gdb_buffer[1] == '?') {
//a ? is sent on connection by gdb
if(g_dbg_runstate == M64P_DBG_RUNSTATE_PAUSED) {
auto& reply = "$S05";
strncpy(gdb_send_buffer, reply, sizeof(reply) - 1);
set_checksum_and_sendlength(sizeof(reply) - 1);
}
else g_dbg_runstate = M64P_DBG_RUNSTATE_PAUSED;
} else if(gdb_buffer[1] == 's') {
debugger_step();
} else if(gdb_buffer[1] == 'c') {
g_dbg_runstate = M64P_DBG_RUNSTATE_RUNNING;
debugger_step();
} else if(gdb_buffer[1] == 'Z' || gdb_buffer[1] == 'z') {
unsigned int address;
unsigned int kind; //for read/write, this is size bytes to watch at addr
sscanf(gdb_buffer + 4, "%x,%x", &address, &kind);
auto execbk = gdb_buffer[2] == '0' || gdb_buffer[2] == '1';
auto writebk = gdb_buffer[2] == '2';
auto readbk = gdb_buffer[2] == '3';
unsigned int flags = M64P_BKP_FLAG_ENABLED | (
execbk ? M64P_BKP_FLAG_EXEC :
writebk ? M64P_BKP_FLAG_WRITE :
readbk ? M64P_BKP_FLAG_READ :
M64P_BKP_FLAG_WRITE | M64P_BKP_FLAG_READ);
if(!execbk) address = DebugVirtualToPhysical(address);
m64p_breakpoint bkpt = {
.address = address,
.endaddr = execbk ? address : address + kind,
.flags = flags
};
if(gdb_buffer[1] == 'Z') {
auto num = add_breakpoint_struct(&g_dev.mem, &bkpt);
if(num == -1) {
strncpy(gdb_send_buffer, "$E01", sizeof("$E01") - 1);
set_checksum_and_sendlength(sizeof("$E01") - 1);
} else {
enable_breakpoint(&g_dev.mem, num);
strncpy(gdb_send_buffer, "$OK", sizeof("$OK") - 1);
set_checksum_and_sendlength(sizeof("$OK") - 1);
}
} else {
remove_breakpoint_by_address(&g_dev.mem, address);
strncpy(gdb_send_buffer, "$OK", sizeof("$OK") - 1);
set_checksum_and_sendlength(sizeof("$OK") - 1);
}
} else if(gdb_buffer[1] == 'm') {
unsigned int address;
unsigned int length;
sscanf(gdb_buffer + 2, "%x,%x", &address, &length);
auto start = fast_mem_access_no_tlb_refill_exception(&g_dev.r4300, address);
if(start == nullptr) {
strncpy(gdb_send_buffer, "$E01", sizeof("$E01") - 1);
set_checksum_and_sendlength(sizeof("$E01") - 1);
} else {
gdb_send_buffer[0] = '$';
auto initialOffset = address & 0x3;
auto wordCount = ((initialOffset + length + 0x3) & ~0x3) >> 2;
for(unsigned int wordi = 0; wordi < wordCount; wordi++) {
auto word = start[wordi];
for(unsigned int bytei = wordi == 0 ? initialOffset : 0, writei = (unsigned int)0; wordi * 4 - initialOffset + bytei < length && bytei < 4; bytei++, writei++) {
auto byte = (word >> (3 - bytei) * 8) & 0xFF;
snprintf(gdb_send_buffer + 1 + wordi * 4 + writei * 2, 3, "%02x", byte);
}
}
set_checksum_and_sendlength(1 + length * 2);
}
} else if(gdb_buffer[1] == 'M') {
unsigned int address;
unsigned int length;
sscanf(gdb_buffer + 2, "%x,%x", &address, &length);
auto start = fast_mem_access_no_tlb_refill_exception(&g_dev.r4300, address);
if(start == nullptr) {
strncpy(gdb_send_buffer, "$E01", sizeof("$E01") - 1);
set_checksum_and_sendlength(sizeof("$E01") - 1);
} else {
auto initialOffset = address & 0x3;
auto wordCount = ((initialOffset + length + 0x3) & ~0x3) >> 2;
auto bytes = strchr(gdb_buffer, ':');
for(unsigned int wordi = 0; wordi < wordCount; wordi++) {
auto word = &start[wordi];
for(unsigned int bytei = wordi == 0 ? initialOffset : 0, writei = (unsigned int)0; wordi * 4 - initialOffset + bytei < length && bytei < 4; bytei++, writei++) {
unsigned int data;
char buf[3];
buf[2] = '\0';
strncpy(buf, bytes + 1 + wordi * 4 + writei * 2, sizeof(buf) - 1);
sscanf(buf, "%02x", &data);
auto mask = 0xFF << (3 - bytei) * 8;
*word &= ~mask;
*word |= data << (3 - bytei) * 8;
}
}
strncpy(gdb_send_buffer, "$OK", sizeof("$OK") - 1);
set_checksum_and_sendlength(sizeof("$OK") - 1);
}
} else {
auto& emptyReply = "$#00";
strncpy(gdb_send_buffer, emptyReply, sizeof(emptyReply) - 1);
sendLength = sizeof(emptyReply) - 1;
}
SDLNet_TCP_Send(gdb_socket, gdb_send_buffer, sendLength);
}
}
return 0;
}
void gdb_try_send_signal_stop() {
if(gdb_socket == nullptr) return;
gdb_send_signal_stop();
}
void set_checksum_and_sendlength(int hash_index) {
gdb_send_buffer[hash_index] = '#';
auto checkSend = std::accumulate(gdb_send_buffer + 1, gdb_send_buffer + hash_index, 0) % 256;
snprintf(gdb_send_buffer + hash_index + 1, 3, "%02x", checkSend);
sendLength = hash_index + sizeof("#XX") - 1;
}
void gdb_send_signal_stop()
{
char stopb[7];
auto& reply = "$S05#";
strncpy(stopb, reply, sizeof(reply) - 1);
auto checkSend = std::accumulate(reply + 1, reply + sizeof(reply) - 2, 0) % 256;
snprintf(stopb + sizeof(reply) - 1, 3, "%02x", checkSend);
auto sendLength = sizeof(reply) - 1 + 2;
SDLNet_TCP_Send(gdb_socket, stopb, sendLength);
}
uint32_t DebugVirtualToPhysical(uint32_t address)
{
struct device* dev = &g_dev;
struct r4300_core* r4300 = &dev->r4300;
if ((address & UINT32_C(0xc0000000)) != UINT32_C(0x80000000)) {
address = virtual_to_physical_address_no_tlb_refill_exception(r4300, address, 0);
if (address == 0) {
return 0;
}
}
address &= UINT32_C(0x1fffffff);
return address;
}

View file

@ -0,0 +1,11 @@
#ifdef __cplusplus
extern "C"
{
#endif
void gdbstub_init();
void gdb_try_send_signal_stop();
#ifdef __cplusplus
}
#endif

View file

@ -297,6 +297,20 @@ uint32_t *fast_mem_access(struct r4300_core* r4300, uint32_t address)
return mem_base_u32(r4300->mem->base, address);
}
uint32_t *fast_mem_access_no_tlb_refill_exception(struct r4300_core* r4300, uint32_t address)
{
if ((address & UINT32_C(0xc0000000)) != UINT32_C(0x80000000)) {
address = virtual_to_physical_address_no_tlb_refill_exception(r4300, address, 2);
if (address == 0) // TLB exception
return NULL;
}
address &= UINT32_C(0x1ffffffc);
return mem_base_u32(r4300->mem->base, address);
}
/* Read aligned word from memory.
* address may not be word-aligned for byte or hword accesses.
* Alignment is taken care of when calling mem handler.

View file

@ -19,6 +19,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifdef __cplusplus
extern "C"
{
#endif
#ifndef M64P_DEVICE_R4300_R4300_CORE_H
#define M64P_DEVICE_R4300_R4300_CORE_H
@ -233,6 +238,7 @@ unsigned int get_r4300_emumode(struct r4300_core* r4300);
* Can access RDRAM, SP_DMEM, SP_IMEM and ROM, using TLB if necessary
* Useful for getting fast access to a zone with executable code. */
uint32_t *fast_mem_access(struct r4300_core* r4300, uint32_t address);
uint32_t *fast_mem_access_no_tlb_refill_exception(struct r4300_core* r4300, uint32_t address);
int r4300_read_aligned_word(struct r4300_core* r4300, uint32_t address, uint32_t* value);
int r4300_read_aligned_dword(struct r4300_core* r4300, uint32_t address, uint64_t* value);
@ -254,3 +260,7 @@ void generic_jump_to(struct r4300_core* r4300, unsigned int address);
void savestates_load_set_pc(struct r4300_core* r4300, uint32_t pc);
#endif
#ifdef __cplusplus
}
#endif

View file

@ -100,7 +100,7 @@ void tlb_map(struct tlb* tlb, size_t entry)
}
}
uint32_t virtual_to_physical_address(struct r4300_core* r4300, uint32_t address, int w)
uint32_t virtual_to_physical_address_no_tlb_refill_exception(struct r4300_core* r4300, uint32_t address, int w)
{
const struct tlb* tlb = &r4300->cp0.tlb;
unsigned int addr = address >> 12;
@ -140,8 +140,16 @@ uint32_t virtual_to_physical_address(struct r4300_core* r4300, uint32_t address,
//printf("tlb exception !!! @ %x, %x, add:%x\n", address, w, r4300->pc->addr);
//getchar();
TLB_refill_exception(r4300, address, w);
//return 0x80000000;
return 0x00000000;
}
uint32_t virtual_to_physical_address(struct r4300_core* r4300, uint32_t address, int w)
{
uint32_t physicalAddress = virtual_to_physical_address_no_tlb_refill_exception(r4300, address, w);
TLB_refill_exception(r4300, address, w);
return physicalAddress;
}

View file

@ -25,6 +25,11 @@
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif
struct r4300_core;
struct tlb_entry
@ -65,5 +70,10 @@ void tlb_unmap(struct tlb* tlb, size_t entry);
void tlb_map(struct tlb* tlb, size_t entry);
uint32_t virtual_to_physical_address(struct r4300_core* r4300, uint32_t address, int w);
uint32_t virtual_to_physical_address_no_tlb_refill_exception(struct r4300_core* r4300, uint32_t address, int w);
#ifdef __cplusplus
}
#endif
#endif /* M64P_DEVICE_R4300_TLB_H */

View file

@ -30,6 +30,11 @@
#include "device/device.h"
#include "osal/preproc.h"
#ifdef __cplusplus
extern "C"
{
#endif
#if defined(__GNUC__)
#define ATTR_FMT(fmtpos, attrpos) __attribute__ ((format (printf, fmtpos, attrpos)))
#else
@ -103,5 +108,9 @@ m64p_error main_reset(int do_hard_reset);
m64p_error open_pif(const unsigned char* pifimage, unsigned int size);
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H__ */