From 43629abbd4022506ef5dad119f6e9e96e40cc1f6 Mon Sep 17 00:00:00 2001 From: vhaudiquet Date: Sun, 8 Oct 2023 13:02:50 +0200 Subject: [PATCH] GDBstub base implementation (gdb is happy) --- src/gdbstub/gdbstub.c | 189 +++++++++++++++++++++++++++++++++--------- src/option.c | 2 +- 2 files changed, 150 insertions(+), 41 deletions(-) diff --git a/src/gdbstub/gdbstub.c b/src/gdbstub/gdbstub.c index 5e9cfbe..d0f7e40 100644 --- a/src/gdbstub/gdbstub.c +++ b/src/gdbstub/gdbstub.c @@ -1,5 +1,7 @@ #include "gdbstub.h" +#include "cpu/rv32cpu.h" + #include #include #include @@ -16,56 +18,163 @@ typedef struct sockaddr_in sockaddr_in_t; typedef struct sockaddr sockaddr_t; #define GDBSTUB_PORT 1234 +#define PACKET_BUFFER_SIZE 2048 socket_t gdbstub_server_socket; socket_t gdb_socket; +void gdbstub_thread_gdb(); + +/* + * Receive a packet from client gdb + * Ignores the ACK on the way, and only returns the packet (not initial '$' symbol) + */ +static void gdbstub_recv_packet(char* packet, ssize_t* size) +{ + char data; + recv(gdb_socket, &data, 1, 0); + + // Start of packet symbol is '$' + // Skip data that is not packet (should only be ACK '+') + if(data != '$') + return gdbstub_recv_packet(packet, size); + + // Receive packet until end of packet '#' symbol + size_t i = 0; + do + { + recv(gdb_socket, packet + i, 1, 0); + i++; + } while(packet[i - 1] != '#'); + + // Receive checksum after end of packet symbol + recv(gdb_socket, packet + i, 1, 0); + recv(gdb_socket, packet + i + 1, 1, 0); + + if(size) *size = i + 2; +} + +/* + * Send a packet to client gdb + * Pass only the packet data as parameter to this function + */ +static void gdbstub_send_packet(char* packet, ssize_t size) +{ + // Compute packet checksum + char end[4] = {0}; + uint8_t sum = 0; + for(size_t i = 0; i < size; i++) + { + sum += packet[i]; + } + snprintf(end, 4, "#%02x", sum); + + // Send ACK to last packet, and begin of packet symbol + char begin[2] = {'+', '$'}; + send(gdb_socket, begin, 2, 0); + + // Send the actual data of packet + send(gdb_socket, packet, size, 0); + + // Send 'end of packet' and checksum + send(gdb_socket, end, 3, 0); +} + /* -* Starts a server that will listen for GDB connections -*/ + * Send a packet to client gdb saying we don't support asked feature + */ +static void gdbstub_send_unsupported() +{ + char unsupported[] = "+$#00"; + send(gdb_socket, unsupported, 5, 0); +} + +/* + * Starts a server that will listen for GDB connections + */ void gdbstub_start() { - socket_t sock = socket(AF_INET, SOCK_STREAM, 0); - if(sock == INVALID_SOCKET) - { - fprintf(stderr, "Could not create gdbstub server socket: %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - sockaddr_in_t sin = {0}; - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_family = AF_INET; - sin.sin_port = htons(GDBSTUB_PORT); - - if(bind(sock, (sockaddr_t*) &sin, sizeof(sin)) == SOCKET_ERROR) - { - fprintf(stderr, "Could not bind gdbstub server socket: %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - if(listen(sock, 1) == SOCKET_ERROR) - { - fprintf(stderr, "Could not listen gdbstub server socket: %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - gdbstub_server_socket = sock; + socket_t sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock == INVALID_SOCKET) + { + fprintf(stderr, "Could not create gdbstub server socket: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + sockaddr_in_t sin = {0}; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_family = AF_INET; + sin.sin_port = htons(GDBSTUB_PORT); + + if(bind(sock, (sockaddr_t*) &sin, sizeof(sin)) == SOCKET_ERROR) + { + fprintf(stderr, "Could not bind gdbstub server socket: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + if(listen(sock, 1) == SOCKET_ERROR) + { + fprintf(stderr, "Could not listen gdbstub server socket: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + gdbstub_server_socket = sock; } /* -* Wait for a client (gdb) to connect to this server -*/ + * Wait for a client (gdb) to connect to this server + */ void gdbstub_wait_for_connection() { - sockaddr_in_t csin = {0}; - socket_t c_socket; - socklen_t sinsize = sizeof(csin); - - c_socket = accept(gdbstub_server_socket, (sockaddr_t*) &csin, &sinsize); - if(c_socket == INVALID_SOCKET) - { - fprintf(stderr, "Could not accept client on gdbstub server: %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - gdb_socket = c_socket; + sockaddr_in_t csin = {0}; + socket_t c_socket; + socklen_t sinsize = sizeof(csin); + + c_socket = accept(gdbstub_server_socket, (sockaddr_t*) &csin, &sinsize); + if(c_socket == INVALID_SOCKET) + { + fprintf(stderr, "Could not accept client on gdbstub server: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + gdb_socket = c_socket; + + gdbstub_thread_gdb(); +} + +void gdbstub_thread_gdb() +{ + while(1) + { + char packet[PACKET_BUFFER_SIZE]; + ssize_t packet_size; + gdbstub_recv_packet(packet, &packet_size); + // printf("<-: %.*s\n", (int) packet_size, packet); + + // Analyse packet to respond correctly + if(packet[0] == '?') + { + char* resp = "S05"; + gdbstub_send_packet(resp, 3); + } + else if(packet[0] == 'g') + { + char resp[32 * 8 + 8 + 1] = {0}; + + // All general purpose registers in host byte order as chars + for(size_t i = 0; i < 32; i++) + { + uint32_t value = htonl(cpu0->regs.x[i]); + snprintf(resp + i * 8, 9, "%08x", value); + } + + // PC register + uint32_t pc = htonl(cpu0->pc); + snprintf(resp + 32 * 8, 9, "%08x", pc); + + // Final packet size, send packet + size_t size = 32 * 8 + 8; + gdbstub_send_packet(resp, size); + } + else gdbstub_send_unsupported(); + } } diff --git a/src/option.c b/src/option.c index 7c36ed8..8acfdee 100644 --- a/src/option.c +++ b/src/option.c @@ -181,7 +181,7 @@ static void print_help() printf(" " OPTION_SEPARATOR "h, " OPTION_SEPARATOR "?, --help\t\tPrint this help message\n"); printf(" " OPTION_SEPARATOR "v, --version\t\t\tPrint version information\n"); printf(" " OPTION_SEPARATOR "m, --memory\t\t\tSet the simulated memory size, in MiB (max 4096)\n"); - printf(" " OPTION_SEPARATOR "s, --gdb-stub\t\t\tLaunch a gdb stub server, for remote gdb debugging\n"); + printf(" " OPTION_SEPARATOR "s, --gdb-stub\t\tLaunch a gdb stub server (remote gdb debugging)\n"); } static void print_version()