[GDBStub] Return error for code / unimpl. reg writes, small fixups
This commit is contained in:
parent
e260d0a110
commit
c17261abf7
|
@ -40,21 +40,9 @@ using xe::kernel::XModule;
|
||||||
using xe::kernel::XObject;
|
using xe::kernel::XObject;
|
||||||
using xe::kernel::XThread;
|
using xe::kernel::XThread;
|
||||||
|
|
||||||
enum class GdbStubControl : char {
|
|
||||||
Ack = '+',
|
|
||||||
Nack = '-',
|
|
||||||
PacketStart = '$',
|
|
||||||
PacketEnd = '#',
|
|
||||||
Interrupt = '\03',
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr const char* kGdbReplyOK = "OK";
|
constexpr const char* kGdbReplyOK = "OK";
|
||||||
constexpr const char* kGdbReplyError = "E01";
|
constexpr const char* kGdbReplyError = "E01";
|
||||||
|
|
||||||
constexpr int kSignalSigill = 4; // Illegal instruction
|
|
||||||
constexpr int kSignalSigtrap = 5; // Trace trap
|
|
||||||
constexpr int kSignalSigsegv = 11; // Segmentation fault
|
|
||||||
|
|
||||||
// must start with l for debugger to accept it
|
// must start with l for debugger to accept it
|
||||||
constexpr char kMemoryMapXml[] =
|
constexpr char kMemoryMapXml[] =
|
||||||
R"(l<?xml version="1.0"?>
|
R"(l<?xml version="1.0"?>
|
||||||
|
@ -70,7 +58,7 @@ constexpr char kMemoryMapXml[] =
|
||||||
</memory-map>
|
</memory-map>
|
||||||
)";
|
)";
|
||||||
|
|
||||||
// TODO: add power-altivec.xml (and update ReadRegister to support it)
|
// TODO: add power-altivec.xml (and update RegisterRead to support it)
|
||||||
constexpr char kTargetXml[] =
|
constexpr char kTargetXml[] =
|
||||||
R"(l<?xml version="1.0"?>
|
R"(l<?xml version="1.0"?>
|
||||||
<!DOCTYPE target SYSTEM "gdb-target.dtd">
|
<!DOCTYPE target SYSTEM "gdb-target.dtd">
|
||||||
|
@ -229,13 +217,13 @@ void GDBStub::Listen(std::unique_ptr<Socket>& client) {
|
||||||
cache_.cur_thread_id = cache_.notify_thread_id;
|
cache_.cur_thread_id = cache_.notify_thread_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sig_num = kSignalSigtrap;
|
SignalCode signal = SignalCode::SIGTRAP;
|
||||||
if (cache_.notify_exception_code.has_value()) {
|
if (cache_.notify_exception_code.has_value()) {
|
||||||
if (cache_.notify_exception_code ==
|
if (cache_.notify_exception_code ==
|
||||||
xe::Exception::Code::kIllegalInstruction) {
|
xe::Exception::Code::kIllegalInstruction) {
|
||||||
sig_num = kSignalSigill;
|
signal = SignalCode::SIGILL;
|
||||||
} else {
|
} else {
|
||||||
sig_num = kSignalSigsegv;
|
signal = SignalCode::SIGSEGV;
|
||||||
}
|
}
|
||||||
|
|
||||||
cache_.notify_exception_code.reset();
|
cache_.notify_exception_code.reset();
|
||||||
|
@ -243,7 +231,7 @@ void GDBStub::Listen(std::unique_ptr<Socket>& client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SendPacket(client,
|
SendPacket(client,
|
||||||
GetThreadStateReply(cache_.notify_thread_id, sig_num));
|
GetThreadStateReply(cache_.notify_thread_id, signal));
|
||||||
cache_.notify_thread_id = -1;
|
cache_.notify_thread_id = -1;
|
||||||
cache_.notify_stopped = false;
|
cache_.notify_stopped = false;
|
||||||
}
|
}
|
||||||
|
@ -255,8 +243,7 @@ void GDBStub::Listen(std::unique_ptr<Socket>& client) {
|
||||||
void GDBStub::SendPacket(std::unique_ptr<Socket>& client,
|
void GDBStub::SendPacket(std::unique_ptr<Socket>& client,
|
||||||
const std::string& data) {
|
const std::string& data) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << char(GdbStubControl::PacketStart) << data
|
ss << char(ControlCode::PacketStart) << data << char(ControlCode::PacketEnd);
|
||||||
<< char(GdbStubControl::PacketEnd);
|
|
||||||
|
|
||||||
uint8_t checksum = 0;
|
uint8_t checksum = 0;
|
||||||
for (char c : data) {
|
for (char c : data) {
|
||||||
|
@ -271,7 +258,7 @@ void GDBStub::SendPacket(std::unique_ptr<Socket>& client,
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
std::string GetPacketFriendlyName(const std::string& packetCommand) {
|
std::string GetPacketFriendlyName(const std::string& packetCommand) {
|
||||||
static const std::unordered_map<std::string, std::string> command_names = {
|
static const std::unordered_map<std::string, std::string> kGdbCommandNames = {
|
||||||
{"?", "StartupQuery"},
|
{"?", "StartupQuery"},
|
||||||
{"!", "EnableExtendedMode"},
|
{"!", "EnableExtendedMode"},
|
||||||
{"p", "RegRead"},
|
{"p", "RegRead"},
|
||||||
|
@ -297,8 +284,8 @@ std::string GetPacketFriendlyName(const std::string& packetCommand) {
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string packet_name = "";
|
std::string packet_name = "";
|
||||||
auto it = command_names.find(packetCommand);
|
auto it = kGdbCommandNames.find(packetCommand);
|
||||||
if (it != command_names.end()) {
|
if (it != kGdbCommandNames.end()) {
|
||||||
packet_name = it->second;
|
packet_name = it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,8 +306,7 @@ bool GDBStub::ProcessIncomingData(std::unique_ptr<Socket>& client,
|
||||||
|
|
||||||
// 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?
|
||||||
bool isInterrupt =
|
bool isInterrupt = buffer[0] == char(ControlCode::Interrupt) && received == 1;
|
||||||
buffer[0] == char(GdbStubControl::Interrupt) && received == 1;
|
|
||||||
|
|
||||||
// Check if we've received a full packet yet, if not exit and allow caller
|
// Check if we've received a full packet yet, if not exit and allow caller
|
||||||
// to try again
|
// to try again
|
||||||
|
@ -330,7 +316,7 @@ bool GDBStub::ProcessIncomingData(std::unique_ptr<Socket>& client,
|
||||||
if (isInterrupt || packet_end + 2 < receive_buffer.length()) {
|
if (isInterrupt || packet_end + 2 < receive_buffer.length()) {
|
||||||
std::string current_packet;
|
std::string current_packet;
|
||||||
if (isInterrupt) {
|
if (isInterrupt) {
|
||||||
current_packet = char(GdbStubControl::Interrupt);
|
current_packet = char(ControlCode::Interrupt);
|
||||||
receive_buffer = "";
|
receive_buffer = "";
|
||||||
isInterrupt = false;
|
isInterrupt = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -348,12 +334,12 @@ bool GDBStub::ProcessIncomingData(std::unique_ptr<Socket>& client,
|
||||||
command.data);
|
command.data);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GdbStubControl result = GdbStubControl::Ack;
|
ControlCode result = ControlCode::Ack;
|
||||||
client->Send(&result, 1);
|
client->Send(&result, 1);
|
||||||
std::string response = HandleGDBCommand(command);
|
std::string response = HandleGDBCommand(command);
|
||||||
SendPacket(client, response);
|
SendPacket(client, response);
|
||||||
} else {
|
} else {
|
||||||
GdbStubControl result = GdbStubControl::Nack;
|
ControlCode result = ControlCode::Nack;
|
||||||
client->Send(&result, 1);
|
client->Send(&result, 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -390,23 +376,23 @@ bool GDBStub::ParsePacket(const std::string& packet, GDBCommand& out_cmd) {
|
||||||
char c = ReadCharFromBuffer();
|
char c = ReadCharFromBuffer();
|
||||||
|
|
||||||
// Expecting start of packet '$'
|
// Expecting start of packet '$'
|
||||||
if (c != char(GdbStubControl::PacketStart)) {
|
if (c != char(ControlCode::PacketStart)) {
|
||||||
// gdb starts conversation with + for some reason
|
// gdb starts conversation with + for some reason
|
||||||
if (c == char(GdbStubControl::Ack)) {
|
if (c == char(ControlCode::Ack)) {
|
||||||
c = ReadCharFromBuffer();
|
c = ReadCharFromBuffer();
|
||||||
}
|
}
|
||||||
// and IDA sometimes has double +, grr
|
// and IDA sometimes has double +, grr
|
||||||
if (c == char(GdbStubControl::Ack)) {
|
if (c == char(ControlCode::Ack)) {
|
||||||
c = ReadCharFromBuffer();
|
c = ReadCharFromBuffer();
|
||||||
}
|
}
|
||||||
// Interrupt is special, handle it without checking checksum
|
// Interrupt is special, handle it without checking checksum
|
||||||
if (c == char(GdbStubControl::Interrupt)) {
|
if (c == char(ControlCode::Interrupt)) {
|
||||||
out_cmd.cmd = char(GdbStubControl::Interrupt);
|
out_cmd.cmd = char(ControlCode::Interrupt);
|
||||||
out_cmd.data = "";
|
out_cmd.data = "";
|
||||||
out_cmd.checksum = 0;
|
out_cmd.checksum = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c != char(GdbStubControl::PacketStart)) {
|
if (c != char(ControlCode::PacketStart)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -423,7 +409,7 @@ bool GDBStub::ParsePacket(const std::string& packet, GDBCommand& out_cmd) {
|
||||||
c = ReadCharFromBuffer();
|
c = ReadCharFromBuffer();
|
||||||
|
|
||||||
// If we reach the end of the buffer or hit '#', stop
|
// If we reach the end of the buffer or hit '#', stop
|
||||||
if (c == '\0' || c == char(GdbStubControl::PacketEnd)) {
|
if (c == '\0' || c == char(ControlCode::PacketEnd)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,12 +474,14 @@ std::string GDBStub::RegisterRead(xe::cpu::ThreadDebugInfo* thread,
|
||||||
// mode (eg. IDA will switch to 64-bit and refuse to allow decompiler to work
|
// mode (eg. IDA will switch to 64-bit and refuse to allow decompiler to work
|
||||||
// with it)
|
// with it)
|
||||||
//
|
//
|
||||||
// TODO: add altivec/VMX registers here...
|
// TODO: add altivec/VMX registers here
|
||||||
|
// TODO: add cvar to allow switch to 64-bit mode? (unsure if any x360 opcodes
|
||||||
|
// use the upper 32-bits?)
|
||||||
//
|
//
|
||||||
// ids from gdb/features/rs6000/powerpc-64.c
|
// ids from gdb/features/rs6000/powerpc-64.c
|
||||||
switch (rid) {
|
switch (RegisterIndex(rid)) {
|
||||||
// pc
|
case RegisterIndex::PC: {
|
||||||
case 64: {
|
//
|
||||||
// If we recently hit a BP then debugger is likely asking for registers
|
// If we recently hit a BP then debugger is likely asking for registers
|
||||||
// for it
|
// for it
|
||||||
//
|
//
|
||||||
|
@ -514,71 +502,69 @@ std::string GDBStub::RegisterRead(xe::cpu::ThreadDebugInfo* thread,
|
||||||
}
|
}
|
||||||
return string_util::to_hex_string((uint32_t)0);
|
return string_util::to_hex_string((uint32_t)0);
|
||||||
}
|
}
|
||||||
case 65:
|
case RegisterIndex::MSR:
|
||||||
return string_util::to_hex_string((uint32_t)thread->guest_context.msr);
|
return string_util::to_hex_string((uint32_t)thread->guest_context.msr);
|
||||||
case 66:
|
case RegisterIndex::CR:
|
||||||
return string_util::to_hex_string((uint32_t)thread->guest_context.cr());
|
return string_util::to_hex_string((uint32_t)thread->guest_context.cr());
|
||||||
case 67:
|
case RegisterIndex::LR:
|
||||||
return string_util::to_hex_string((uint32_t)thread->guest_context.lr);
|
return string_util::to_hex_string((uint32_t)thread->guest_context.lr);
|
||||||
case 68:
|
case RegisterIndex::CTR:
|
||||||
return string_util::to_hex_string((uint32_t)thread->guest_context.ctr);
|
return string_util::to_hex_string((uint32_t)thread->guest_context.ctr);
|
||||||
// xer
|
case RegisterIndex::XER:
|
||||||
case 69:
|
|
||||||
return std::string(8, 'x');
|
return std::string(8, 'x');
|
||||||
case 70:
|
case RegisterIndex::FPSCR:
|
||||||
return string_util::to_hex_string(thread->guest_context.fpscr.value);
|
return string_util::to_hex_string(thread->guest_context.fpscr.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rid > 70) {
|
if (rid >= int(RegisterIndex::PC)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// fpr
|
// fpr
|
||||||
if (rid > 31) {
|
if (rid >= int(RegisterIndex::FPR0)) {
|
||||||
return string_util::to_hex_string(thread->guest_context.f[rid - 32]);
|
return string_util::to_hex_string(thread->guest_context.f[rid - 32]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// gpr
|
// gpr
|
||||||
return string_util::to_hex_string((uint32_t)thread->guest_context.r[rid]);
|
return string_util::to_hex_string((uint32_t)thread->guest_context.r[rid]);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GDBStub::RegisterWrite(xe::cpu::ThreadDebugInfo* thread,
|
std::string GDBStub::RegisterWrite(xe::cpu::ThreadDebugInfo* thread,
|
||||||
uint32_t rid, const std::string_view value) {
|
uint32_t rid, const std::string_view value) {
|
||||||
|
// Have to update the main thread context, and the ThreadDebugInfo context
|
||||||
auto* guest_context = thread->thread->thread_state()->context();
|
auto* guest_context = thread->thread->thread_state()->context();
|
||||||
switch (rid) {
|
switch (RegisterIndex(rid)) {
|
||||||
// pc
|
case RegisterIndex::PC:
|
||||||
case 64:
|
return kGdbReplyError; // TODO: figure a way to change this
|
||||||
return kGdbReplyOK; // TODO: figure a way to change this
|
case RegisterIndex::MSR:
|
||||||
case 65:
|
|
||||||
guest_context->msr = string_util::from_string<uint32_t>(value, true);
|
guest_context->msr = string_util::from_string<uint32_t>(value, true);
|
||||||
thread->guest_context.msr = guest_context->msr;
|
thread->guest_context.msr = guest_context->msr;
|
||||||
return kGdbReplyOK;
|
return kGdbReplyOK;
|
||||||
case 66:
|
case RegisterIndex::CR:
|
||||||
// CR
|
return kGdbReplyError; // TODO: figure a way to change this
|
||||||
return kGdbReplyOK; // TODO: figure a way to change this
|
case RegisterIndex::LR:
|
||||||
case 67:
|
|
||||||
guest_context->lr = string_util::from_string<uint32_t>(value, true);
|
guest_context->lr = string_util::from_string<uint32_t>(value, true);
|
||||||
thread->guest_context.lr = guest_context->lr;
|
thread->guest_context.lr = guest_context->lr;
|
||||||
return kGdbReplyOK;
|
return kGdbReplyOK;
|
||||||
case 68:
|
case RegisterIndex::CTR:
|
||||||
guest_context->ctr = string_util::from_string<uint32_t>(value, true);
|
guest_context->ctr = string_util::from_string<uint32_t>(value, true);
|
||||||
thread->guest_context.ctr = guest_context->ctr;
|
thread->guest_context.ctr = guest_context->ctr;
|
||||||
return kGdbReplyOK;
|
return kGdbReplyOK;
|
||||||
// xer
|
case RegisterIndex::XER:
|
||||||
case 69:
|
return kGdbReplyError;
|
||||||
return kGdbReplyOK;
|
case RegisterIndex::FPSCR:
|
||||||
case 70:
|
|
||||||
guest_context->fpscr.value =
|
guest_context->fpscr.value =
|
||||||
string_util::from_string<uint32_t>(value, true);
|
string_util::from_string<uint32_t>(value, true);
|
||||||
thread->guest_context.fpscr.value = guest_context->fpscr.value;
|
thread->guest_context.fpscr.value = guest_context->fpscr.value;
|
||||||
return kGdbReplyOK;
|
return kGdbReplyOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rid > 70) {
|
if (rid >= int(RegisterIndex::PC)) {
|
||||||
return kGdbReplyError;
|
return kGdbReplyError;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fpr
|
// fpr
|
||||||
if (rid > 31) {
|
if (rid >= int(RegisterIndex::FPR0)) {
|
||||||
guest_context->f[rid - 32] = string_util::from_string<double>(value, true);
|
guest_context->f[rid - 32] = string_util::from_string<double>(value, true);
|
||||||
thread->guest_context.f[rid - 32] = guest_context->f[rid - 32];
|
thread->guest_context.f[rid - 32] = guest_context->f[rid - 32];
|
||||||
return kGdbReplyOK;
|
return kGdbReplyOK;
|
||||||
|
@ -625,7 +611,7 @@ std::string GDBStub::RegisterReadAll() {
|
||||||
return kGdbReplyError;
|
return kGdbReplyError;
|
||||||
}
|
}
|
||||||
std::string result;
|
std::string result;
|
||||||
result.reserve(68 * 16 + 3 * 8);
|
result.reserve((39 * 8) + (32 * 16));
|
||||||
for (int i = 0; i < 71; ++i) {
|
for (int i = 0; i < 71; ++i) {
|
||||||
result += RegisterRead(thread, i);
|
result += RegisterRead(thread, i);
|
||||||
}
|
}
|
||||||
|
@ -638,6 +624,11 @@ std::string GDBStub::RegisterWriteAll(const std::string& data) {
|
||||||
return kGdbReplyError;
|
return kGdbReplyError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int expected_size = (39 * 8) + (32 * 16);
|
||||||
|
if (data.length() != expected_size) {
|
||||||
|
return kGdbReplyError;
|
||||||
|
}
|
||||||
|
|
||||||
int string_offset = 0;
|
int string_offset = 0;
|
||||||
for (int i = 0; i < 71; ++i) {
|
for (int i = 0; i < 71; ++i) {
|
||||||
int reg_size = 8; // 8 hex-nibbles per 32-bit register
|
int reg_size = 8; // 8 hex-nibbles per 32-bit register
|
||||||
|
@ -746,6 +737,14 @@ std::string GDBStub::MemoryWrite(const std::string& data) {
|
||||||
if (!heap) {
|
if (!heap) {
|
||||||
return kGdbReplyError;
|
return kGdbReplyError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if they're trying to write to an executable function
|
||||||
|
if (auto* exe_addr = processor_->LookupFunction(addr)) {
|
||||||
|
// TODO: allow the write and ask processor to recompile if no breakpoints
|
||||||
|
// are set there?
|
||||||
|
return kGdbReplyError; // error for now as writes here won't have an effect
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t protect = 0;
|
uint32_t protect = 0;
|
||||||
if (!heap->QueryProtect(addr, &protect) ||
|
if (!heap->QueryProtect(addr, &protect) ||
|
||||||
(protect & kMemoryProtectRead) != kMemoryProtectRead) {
|
(protect & kMemoryProtectRead) != kMemoryProtectRead) {
|
||||||
|
@ -791,10 +790,8 @@ std::string GDBStub::BuildThreadList() {
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GDBStub::GetThreadStateReply(uint32_t thread_id, uint8_t signal) {
|
std::string GDBStub::GetThreadStateReply(uint32_t thread_id,
|
||||||
constexpr int PC_REGISTER = 64;
|
SignalCode signal) {
|
||||||
constexpr int LR_REGISTER = 67;
|
|
||||||
|
|
||||||
auto* thread = cache_.thread_info(thread_id);
|
auto* thread = cache_.thread_info(thread_id);
|
||||||
|
|
||||||
if (thread_id != -1 && thread) {
|
if (thread_id != -1 && thread) {
|
||||||
|
@ -813,7 +810,8 @@ std::string GDBStub::GetThreadStateReply(uint32_t thread_id, uint8_t signal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt::format("T{:02x}{:02x}:{:08x};{:02x}:{:08x};thread:{:x};",
|
return fmt::format("T{:02x}{:02x}:{:08x};{:02x}:{:08x};thread:{:x};",
|
||||||
signal, PC_REGISTER, uint32_t(pc_value), LR_REGISTER,
|
uint8_t(signal), int(RegisterIndex::PC),
|
||||||
|
uint32_t(pc_value), int(RegisterIndex::LR),
|
||||||
uint32_t(thread->guest_context.lr), thread_id);
|
uint32_t(thread->guest_context.lr), thread_id);
|
||||||
}
|
}
|
||||||
return "S05";
|
return "S05";
|
||||||
|
|
|
@ -26,6 +26,28 @@ namespace debug {
|
||||||
namespace gdb {
|
namespace gdb {
|
||||||
|
|
||||||
class GDBStub : public cpu::DebugListener {
|
class GDBStub : public cpu::DebugListener {
|
||||||
|
enum class ControlCode : char {
|
||||||
|
Ack = '+',
|
||||||
|
Nack = '-',
|
||||||
|
PacketStart = '$',
|
||||||
|
PacketEnd = '#',
|
||||||
|
Interrupt = '\03',
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SignalCode : uint8_t { SIGILL = 4, SIGTRAP = 5, SIGSEGV = 11 };
|
||||||
|
|
||||||
|
enum class RegisterIndex : int {
|
||||||
|
GPR0 = 0,
|
||||||
|
FPR0 = 32,
|
||||||
|
PC = 64,
|
||||||
|
MSR = 65,
|
||||||
|
CR = 66,
|
||||||
|
LR = 67,
|
||||||
|
CTR = 68,
|
||||||
|
XER = 69,
|
||||||
|
FPSCR = 70
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~GDBStub();
|
virtual ~GDBStub();
|
||||||
|
|
||||||
|
@ -77,7 +99,7 @@ class GDBStub : public cpu::DebugListener {
|
||||||
std::string MemoryWrite(const std::string& data);
|
std::string MemoryWrite(const std::string& data);
|
||||||
std::string BuildThreadList();
|
std::string BuildThreadList();
|
||||||
|
|
||||||
std::string GetThreadStateReply(uint32_t thread_id, uint8_t signal);
|
std::string GetThreadStateReply(uint32_t thread_id, SignalCode signal);
|
||||||
|
|
||||||
bool CreateCodeBreakpoint(uint64_t address);
|
bool CreateCodeBreakpoint(uint64_t address);
|
||||||
void DeleteCodeBreakpoint(uint64_t address);
|
void DeleteCodeBreakpoint(uint64_t address);
|
||||||
|
@ -112,7 +134,6 @@ class GDBStub : public cpu::DebugListener {
|
||||||
std::vector<cpu::ThreadDebugInfo*> thread_debug_infos;
|
std::vector<cpu::ThreadDebugInfo*> thread_debug_infos;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
char kernel_call_filter[64] = {0};
|
|
||||||
std::vector<std::unique_ptr<cpu::Breakpoint>> all_breakpoints;
|
std::vector<std::unique_ptr<cpu::Breakpoint>> all_breakpoints;
|
||||||
std::unordered_map<uint32_t, cpu::Breakpoint*>
|
std::unordered_map<uint32_t, cpu::Breakpoint*>
|
||||||
code_breakpoints_by_guest_address;
|
code_breakpoints_by_guest_address;
|
||||||
|
|
Loading…
Reference in New Issue