Add a trivial gdb stub, runnable but not yet fully working

This commit is contained in:
Konstantin Nazarov 2024-12-08 23:32:45 +00:00
parent 71bbc1e65d
commit 963e001946
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
5 changed files with 285 additions and 3 deletions

View file

@ -12,12 +12,14 @@ add_library(vm_lib)
target_sources(vm_lib target_sources(vm_lib
PRIVATE PRIVATE
src/vm.cpp src/vm.cpp
src/debug.cpp
PUBLIC PUBLIC
FILE_SET HEADERS FILE_SET HEADERS
BASE_DIRS src BASE_DIRS src
FILES FILES
src/vm.hpp src/vm.hpp
src/debug.hpp
) )
add_executable(rve src/rve.cpp) add_executable(rve src/rve.cpp)

View file

@ -21,6 +21,7 @@ pkgs.gcc13Stdenv.mkDerivation rec {
#riscv-pkgs.buildPackages.gcc #riscv-pkgs.buildPackages.gcc
pkgsCross.riscv32-embedded.buildPackages.gcc pkgsCross.riscv32-embedded.buildPackages.gcc
pkgsCross.riscv32-embedded.buildPackages.binutils pkgsCross.riscv32-embedded.buildPackages.binutils
pkgsCross.riscv32-embedded.buildPackages.gdb
]; ];
hardeningDisable = [ "all" ]; hardeningDisable = [ "all" ];

217
src/debug.cpp Normal file
View file

@ -0,0 +1,217 @@
#include "debug.hpp"
GDBStub::GDBStub(int port) : server_fd(-1), client_fd(-1), running(false) {
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
throw std::runtime_error("Failed to create socket.");
}
sockaddr_in server_addr = {};
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
throw std::runtime_error("Failed to bind socket.");
}
if (listen(server_fd, 1) == -1) {
throw std::runtime_error("Failed to listen on socket.");
}
std::cout << "Waiting for GDB connection on port " << port << "..." << std::endl;
client_fd = accept(server_fd, nullptr, nullptr);
if (client_fd == -1) {
throw std::runtime_error("Failed to accept connection.");
}
std::cout << "GDB connected!" << std::endl;
running = true;
}
GDBStub::~GDBStub() {
if (client_fd != -1) {
close(client_fd);
}
if (server_fd != -1) {
close(server_fd);
}
}
void GDBStub::run() {
try {
while (running) {
std::string packet = receive_packet();
std::cout << "received packet: " << packet << "\n";
handle_packet(packet);
}
} catch (const std::exception &e) {
std::cerr << "Error: " << e.what() << std::endl;
}
}
void GDBStub::stop() {
running = false;
}
void GDBStub::send_ack() {
if (send(client_fd, "+", 1, 0) == -1) {
throw std::runtime_error("Failed to send acknowledgment.");
}
}
void GDBStub::wait_ack() {
char ack;
ssize_t len = recv(client_fd, &ack, 1, 0);
if (len <= 0) {
throw std::runtime_error("Failed to receive acknowledgment from GDB.");
}
if (ack != '+') {
throw std::runtime_error("Negative acknowledgment received.");
}
}
void GDBStub::send_packet(const std::string &packet) {
uint8_t checksum = 0;
for (char c : packet) {
checksum += c;
}
std::ostringstream response;
response << '$' << packet << '#' << std::hex << std::setfill('0') << std::setw(2) << (checksum & 0xFF);
const std::string response_str = response.str();
if (send(client_fd, response_str.c_str(), response_str.size(), 0) == -1) {
throw std::runtime_error("Failed to send packet to GDB.");
}
wait_ack();
}
std::string GDBStub::receive_packet() {
char buffer[BUFFER_SIZE];
std::string packet;
bool found_start = false;
while (true) {
ssize_t len = recv(client_fd, buffer, sizeof(buffer), 0);
if (len <= 0) {
throw std::runtime_error("Connection closed or error in receiving.");
}
for (ssize_t i = 0; i < len; ++i) {
char c = buffer[i];
if (!found_start) {
if (c == '$') {
found_start = true;
packet.clear();
}
} else {
if (c == '#') {
send_ack();
// Ignore checksum for simplicity
return packet;
} else {
packet += c;
}
}
}
}
}
void GDBStub::handle_packet(const std::string &packet) {
switch (packet[0]) {
case '?':
send_packet("S05"); // TODO: real stop reason
break;
case 'g': // Read all registers
send_packet(read_registers());
break;
case 'p': {
// Getting specific register
int regnum = std::stoi(packet.substr(1), nullptr, 16);
uint32_t reg_value = 0;
std::ostringstream response;
response << std::hex << std::setfill('0') << std::setw(8) << reg_value;
send_packet(response.str());
break;
}
case 'G':
send_packet("OK"); // TODO: write registers
break;
case 'm': {
size_t addr, length;
if (parse_memory_request(packet, addr, length)) {
send_packet(read_memory(addr, length));
} else {
send_packet("E01"); // Error
}
break;
}
case 'M':
send_packet("OK"); // TODO: write memory
break;
case 'c':
send_packet("S05"); // TODO: continue execution
break;
case 's':
send_packet("S05"); // TODO: step execution
break;
case 'q':
std::cout << "received q\n";
if (packet.find("qSupported") == 0) {
std::cout << "received qSupported\n";
send_packet("PacketSize=4096"); // Example capability
} else {
send_packet(""); // Empty response
}
break;
default:
send_packet(""); // Unsupported packet
break;
}
}
bool GDBStub::parse_memory_request(const std::string &packet, size_t &addr, size_t &length) {
size_t comma_pos = packet.find(',');
if (comma_pos == std::string::npos) {
return false;
}
try {
addr = std::stoul(packet.substr(1, comma_pos - 1), nullptr, 16);
length = std::stoul(packet.substr(comma_pos + 1), nullptr, 16);
} catch (...) {
return false;
}
return true;
}
std::string GDBStub::read_registers() {
std::ostringstream out;
for (int i = 0; i < 32; ++i) {
out << std::string(8, '0'); // TODO: stub
}
return out.str();
}
std::string GDBStub::read_memory(size_t addr, size_t length) {
std::ostringstream out;
for (size_t i = 0; i < length; ++i) {
out << "00"; // TODO: stub
}
return out.str();
}

46
src/debug.hpp Normal file
View file

@ -0,0 +1,46 @@
#pragma once
#include <iostream>
#include <cstring>
#include <stdexcept>
#include <thread>
#include <atomic>
#include <vector>
#include <sstream>
#include <iomanip>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
class GDBStub {
public:
GDBStub(int port);
~GDBStub();
void run();
void stop();
private:
int server_fd;
int client_fd;
std::atomic<bool> running;
// Buffer size for GDB packet handling
static constexpr size_t BUFFER_SIZE = 4096;
void send_packet(const std::string &packet);
void send_ack();
void wait_ack();
std::string receive_packet();
void handle_packet(const std::string &packet);
bool parse_memory_request(const std::string &packet, size_t &addr, size_t &length);
std::string read_registers();
std::string read_memory(size_t addr, size_t length);
};

View file

@ -1,4 +1,6 @@
#include "vm.hpp" #include "vm.hpp"
#include "debug.hpp"
#include <iostream> #include <iostream>
#include <cstdlib> #include <cstdlib>
#include <cassert> #include <cassert>
@ -34,13 +36,20 @@ int main(int argc, char* argv[]) {
const size_t MEMORY_SIZE = 128*1024; const size_t MEMORY_SIZE = 128*1024;
uint8_t memory[MEMORY_SIZE] = {0}; uint8_t memory[MEMORY_SIZE] = {0};
if (argc <= 1) { bool debug = false;
std::string program_filename = "";
for (int i = 1; i < argc; ++i) {
if (std::string(argv[i]) == "--debug")
debug = true;
else
program_filename = argv[i];
}
if (program_filename == "") {
std::cerr << "Expected filename of program to execute" << std::endl; std::cerr << "Expected filename of program to execute" << std::endl;
return 1; return 1;
} }
std::string program_filename = argv[1];
try { try {
load_program(program_filename, memory, MEMORY_SIZE); load_program(program_filename, memory, MEMORY_SIZE);
} catch (const std::exception &e) { } catch (const std::exception &e) {
@ -61,5 +70,12 @@ int main(int argc, char* argv[]) {
std::cout << "result: " << *res << std::endl; std::cout << "result: " << *res << std::endl;
if (debug) {
// to debug, do: "set debug remote 1" in gdb
// and then "target remote :1234"
GDBStub stub(1234);
stub.run();
}
return 0; return 0;
} }