Add a trivial gdb stub, runnable but not yet fully working
This commit is contained in:
parent
71bbc1e65d
commit
963e001946
5 changed files with 285 additions and 3 deletions
|
@ -12,12 +12,14 @@ add_library(vm_lib)
|
|||
target_sources(vm_lib
|
||||
PRIVATE
|
||||
src/vm.cpp
|
||||
src/debug.cpp
|
||||
|
||||
PUBLIC
|
||||
FILE_SET HEADERS
|
||||
BASE_DIRS src
|
||||
FILES
|
||||
src/vm.hpp
|
||||
src/debug.hpp
|
||||
)
|
||||
|
||||
add_executable(rve src/rve.cpp)
|
||||
|
|
1
rve.nix
1
rve.nix
|
@ -21,6 +21,7 @@ pkgs.gcc13Stdenv.mkDerivation rec {
|
|||
#riscv-pkgs.buildPackages.gcc
|
||||
pkgsCross.riscv32-embedded.buildPackages.gcc
|
||||
pkgsCross.riscv32-embedded.buildPackages.binutils
|
||||
pkgsCross.riscv32-embedded.buildPackages.gdb
|
||||
];
|
||||
|
||||
hardeningDisable = [ "all" ];
|
||||
|
|
217
src/debug.cpp
Normal file
217
src/debug.cpp
Normal 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
46
src/debug.hpp
Normal 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);
|
||||
};
|
22
src/rve.cpp
22
src/rve.cpp
|
@ -1,4 +1,6 @@
|
|||
#include "vm.hpp"
|
||||
#include "debug.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
|
@ -34,13 +36,20 @@ int main(int argc, char* argv[]) {
|
|||
const size_t MEMORY_SIZE = 128*1024;
|
||||
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;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string program_filename = argv[1];
|
||||
|
||||
try {
|
||||
load_program(program_filename, memory, MEMORY_SIZE);
|
||||
} catch (const std::exception &e) {
|
||||
|
@ -61,5 +70,12 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue