[GDBStub] Fix debugger showing invalid symbols after pausing/continuing
This commit is contained in:
parent
c17261abf7
commit
220324b463
|
@ -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",
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue