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
|
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)
|
||||||
|
|
1
rve.nix
1
rve.nix
|
@ -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
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 "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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue