[GDBStub] Fix debugger showing invalid symbols after pausing/continuing

This commit is contained in:
emoose 2024-10-09 01:14:40 +01:00
parent c17261abf7
commit 220324b463
2 changed files with 68 additions and 35 deletions

View File

@ -181,29 +181,33 @@ std::unique_ptr<GDBStub> GDBStub::Create(Emulator* emulator, int listen_port) {
} }
bool GDBStub::Initialize() { bool GDBStub::Initialize() {
socket_ = xe::SocketServer::Create( socket_ = xe::SocketServer::Create(listen_port_,
listen_port_, [this](std::unique_ptr<Socket> client) { Listen(client); }); [this](std::unique_ptr<Socket> socket) {
GDBClient client;
client.socket = std::move(socket);
Listen(client);
});
UpdateCache(); UpdateCache();
return true; return true;
} }
void GDBStub::Listen(std::unique_ptr<Socket>& client) { void GDBStub::Listen(GDBClient& client) {
// Client is connected - pause execution // Client is connected - pause execution
ExecutionPause(); ExecutionPause();
UpdateCache(); UpdateCache();
client->set_nonblocking(true); client.socket->set_nonblocking(true);
std::string receive_buffer; std::string receive_buffer;
while (!stop_thread_) { while (!stop_thread_) {
if (!client->is_connected()) { if (!client.socket->is_connected()) {
break; break;
} }
if (!ProcessIncomingData(client, receive_buffer)) { if (!ProcessIncomingData(client)) {
if (!client->is_connected()) { if (!client.socket->is_connected()) {
break; break;
} }
// No data available, can do other work or sleep // No data available, can do other work or sleep
@ -240,8 +244,7 @@ void GDBStub::Listen(std::unique_ptr<Socket>& client) {
} }
} }
void GDBStub::SendPacket(std::unique_ptr<Socket>& client, void GDBStub::SendPacket(GDBClient& client, const std::string& data) {
const std::string& data) {
std::stringstream ss; std::stringstream ss;
ss << char(ControlCode::PacketStart) << data << char(ControlCode::PacketEnd); ss << char(ControlCode::PacketStart) << data << char(ControlCode::PacketEnd);
@ -253,7 +256,7 @@ void GDBStub::SendPacket(std::unique_ptr<Socket>& client,
ss << std::hex << std::setw(2) << std::setfill('0') << (checksum & 0xff); ss << std::hex << std::setw(2) << std::setfill('0') << (checksum & 0xff);
std::string packet = ss.str(); std::string packet = ss.str();
client->Send(packet.c_str(), packet.size()); client.socket->Send(packet.c_str(), packet.size());
} }
#ifdef DEBUG #ifdef DEBUG
@ -293,16 +296,15 @@ std::string GetPacketFriendlyName(const std::string& packetCommand) {
} }
#endif #endif
bool GDBStub::ProcessIncomingData(std::unique_ptr<Socket>& client, bool GDBStub::ProcessIncomingData(GDBClient& client) {
std::string& receive_buffer) {
char buffer[1024]; char buffer[1024];
size_t received = client->Receive(buffer, sizeof(buffer)); size_t received = client.socket->Receive(buffer, sizeof(buffer));
if (received == uint64_t(-1) || // disconnected if (received == uint64_t(-1) || // disconnected
received == 0) { received == 0) {
return false; return false;
} }
receive_buffer.append(buffer, received); client.receive_buffer.append(buffer, received);
// Hacky interrupt '\03' packet handling, some reason checksum isn't // Hacky interrupt '\03' packet handling, some reason checksum isn't
// attached to this? // attached to this?
@ -312,16 +314,16 @@ bool GDBStub::ProcessIncomingData(std::unique_ptr<Socket>& client,
// to try again // to try again
size_t packet_end; size_t packet_end;
while (isInterrupt || while (isInterrupt ||
(packet_end = receive_buffer.find('#')) != std::string::npos) { (packet_end = client.receive_buffer.find('#')) != std::string::npos) {
if (isInterrupt || packet_end + 2 < receive_buffer.length()) { if (isInterrupt || packet_end + 2 < client.receive_buffer.length()) {
std::string current_packet; std::string current_packet;
if (isInterrupt) { if (isInterrupt) {
current_packet = char(ControlCode::Interrupt); current_packet = char(ControlCode::Interrupt);
receive_buffer = ""; client.receive_buffer = "";
isInterrupt = false; isInterrupt = false;
} else { } else {
current_packet = receive_buffer.substr(0, packet_end + 3); current_packet = client.receive_buffer.substr(0, packet_end + 3);
receive_buffer = receive_buffer.substr(packet_end + 3); client.receive_buffer = client.receive_buffer.substr(packet_end + 3);
} }
GDBCommand command; GDBCommand command;
@ -334,13 +336,18 @@ bool GDBStub::ProcessIncomingData(std::unique_ptr<Socket>& client,
command.data); command.data);
#endif #endif
ControlCode result = ControlCode::Ack; if (!client.no_ack_mode) {
client->Send(&result, 1); ControlCode result = ControlCode::Ack;
std::string response = HandleGDBCommand(command); client.socket->Send(&result, 1);
}
std::string response = HandleGDBCommand(client, command);
SendPacket(client, response); SendPacket(client, response);
} else { } else {
ControlCode result = ControlCode::Nack; if (!client.no_ack_mode) {
client->Send(&result, 1); ControlCode result = ControlCode::Nack;
client.socket->Send(&result, 1);
}
} }
} else { } else {
break; break;
@ -653,7 +660,7 @@ std::string GDBStub::ExecutionPause() {
return kGdbReplyError; return kGdbReplyError;
} }
processor_->Pause(); processor_->Pause();
return kGdbReplyOK; return "";
} }
std::string GDBStub::ExecutionContinue() { std::string GDBStub::ExecutionContinue() {
@ -661,7 +668,7 @@ std::string GDBStub::ExecutionContinue() {
debugging::DebugPrint("GDBStub: ExecutionContinue\n"); debugging::DebugPrint("GDBStub: ExecutionContinue\n");
#endif #endif
processor_->Continue(); processor_->Continue();
return kGdbReplyOK; return "";
} }
std::string GDBStub::ExecutionStep() { std::string GDBStub::ExecutionStep() {
@ -674,7 +681,7 @@ std::string GDBStub::ExecutionStep() {
processor_->StepGuestInstruction(cache_.last_bp_thread_id); processor_->StepGuestInstruction(cache_.last_bp_thread_id);
} }
return kGdbReplyOK; return "";
} }
std::string GDBStub::MemoryRead(const std::string& data) { std::string GDBStub::MemoryRead(const std::string& data) {
@ -739,7 +746,7 @@ std::string GDBStub::MemoryWrite(const std::string& data) {
} }
// Check if they're trying to write to an executable function // Check if they're trying to write to an executable function
if (auto* exe_addr = processor_->LookupFunction(addr)) { if (processor_->LookupFunction(addr) != nullptr) {
// TODO: allow the write and ask processor to recompile if no breakpoints // TODO: allow the write and ask processor to recompile if no breakpoints
// are set there? // are set there?
return kGdbReplyError; // error for now as writes here won't have an effect return kGdbReplyError; // error for now as writes here won't have an effect
@ -790,6 +797,14 @@ std::string GDBStub::BuildThreadList() {
return buffer; return buffer;
} }
std::string GDBStub::QueryPacket(GDBClient& client, const std::string& data) {
if (data == "StartNoAckMode") {
client.no_ack_mode = true;
return kGdbReplyOK;
}
return kGdbReplyError;
}
std::string GDBStub::GetThreadStateReply(uint32_t thread_id, std::string GDBStub::GetThreadStateReply(uint32_t thread_id,
SignalCode signal) { SignalCode signal) {
auto* thread = cache_.thread_info(thread_id); auto* thread = cache_.thread_info(thread_id);
@ -822,6 +837,11 @@ bool GDBStub::CreateCodeBreakpoint(uint64_t address) {
debugging::DebugPrint("GDBStub: Adding breakpoint: {:X}\n", address); debugging::DebugPrint("GDBStub: Adding breakpoint: {:X}\n", address);
#endif #endif
auto* exe_addr = processor_->LookupFunction((uint32_t)address);
if (!exe_addr) {
return false; // TODO: move this check to Breakpoint?
}
auto& state = cache_.breakpoints; auto& state = cache_.breakpoints;
auto breakpoint = std::make_unique<Breakpoint>( auto breakpoint = std::make_unique<Breakpoint>(
processor_, Breakpoint::AddressType::kGuest, address, processor_, Breakpoint::AddressType::kGuest, address,
@ -969,7 +989,8 @@ void GDBStub::OnBreakpointHit(Breakpoint* breakpoint,
UpdateCache(); UpdateCache();
} }
std::string GDBStub::HandleGDBCommand(const GDBCommand& command) { std::string GDBStub::HandleGDBCommand(GDBClient& client,
const GDBCommand& command) {
static const std::unordered_map<std::string, static const std::unordered_map<std::string,
std::function<std::string(const GDBCommand&)>> std::function<std::string(const GDBCommand&)>>
command_map = { command_map = {
@ -1021,6 +1042,12 @@ std::string GDBStub::HandleGDBCommand(const GDBCommand& command) {
{"G", {"G",
[&](const GDBCommand& cmd) { return RegisterWriteAll(cmd.data); }}, [&](const GDBCommand& cmd) { return RegisterWriteAll(cmd.data); }},
// Query / setting change
{"Q",
[&](const GDBCommand& cmd) {
return QueryPacket(client, cmd.data);
}},
// Attach to specific process ID - IDA used to send this, but doesn't // Attach to specific process ID - IDA used to send this, but doesn't
// after some changes? // after some changes?
{"vAttach", [&](const GDBCommand& cmd) { return "S05"; }}, {"vAttach", [&](const GDBCommand& cmd) { return "S05"; }},
@ -1099,7 +1126,7 @@ std::string GDBStub::HandleGDBCommand(const GDBCommand& command) {
{"qSupported", {"qSupported",
[&](const GDBCommand& cmd) { [&](const GDBCommand& cmd) {
return "PacketSize=1024;qXfer:features:read+;qXfer:threads:read+;" return "PacketSize=1024;qXfer:features:read+;qXfer:threads:read+;"
"qXfer:memory-map:read+"; "qXfer:memory-map:read+;QStartNoAckMode+";
}}, }},
// Thread list (IDA requests this but ignores in favor of qXfer?) // Thread list (IDA requests this but ignores in favor of qXfer?)
{"qfThreadInfo", {"qfThreadInfo",

View File

@ -72,15 +72,20 @@ class GDBStub : public cpu::DebugListener {
uint8_t checksum{}; uint8_t checksum{};
}; };
struct GDBClient {
std::unique_ptr<Socket> socket;
bool no_ack_mode = false;
std::string receive_buffer;
};
explicit GDBStub(Emulator* emulator, int listen_port); explicit GDBStub(Emulator* emulator, int listen_port);
bool Initialize(); bool Initialize();
void Listen(std::unique_ptr<Socket>& client); void Listen(GDBClient& client);
void SendPacket(std::unique_ptr<Socket>& client, const std::string& data); void SendPacket(GDBClient& client, const std::string& data);
bool ProcessIncomingData(std::unique_ptr<Socket>& client, bool ProcessIncomingData(GDBClient& client);
std::string& receive_buffer);
bool ParsePacket(const std::string& packet, GDBCommand& out_cmd); bool ParsePacket(const std::string& packet, GDBCommand& out_cmd);
std::string HandleGDBCommand(const GDBCommand& command); std::string HandleGDBCommand(GDBClient& client, const GDBCommand& command);
void UpdateCache(); void UpdateCache();
@ -98,6 +103,7 @@ class GDBStub : public cpu::DebugListener {
std::string MemoryRead(const std::string& data); std::string MemoryRead(const std::string& data);
std::string MemoryWrite(const std::string& data); std::string MemoryWrite(const std::string& data);
std::string BuildThreadList(); std::string BuildThreadList();
std::string QueryPacket(GDBClient& client, const std::string& data);
std::string GetThreadStateReply(uint32_t thread_id, SignalCode signal); std::string GetThreadStateReply(uint32_t thread_id, SignalCode signal);