|
|
@ -108,9 +108,9 @@ static std::string EscapeXML(std::string_view data) {
|
|
|
|
return escaped;
|
|
|
|
return escaped;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
|
|
|
|
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_, Kernel::KProcess* debug_process_)
|
|
|
|
: DebuggerFrontend(backend_), system{system_} {
|
|
|
|
: DebuggerFrontend(backend_), system{system_}, debug_process{debug_process_} {
|
|
|
|
if (system.ApplicationProcess()->Is64Bit()) {
|
|
|
|
if (GetProcess()->Is64Bit()) {
|
|
|
|
arch = std::make_unique<GDBStubA64>();
|
|
|
|
arch = std::make_unique<GDBStubA64>();
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
arch = std::make_unique<GDBStubA32>();
|
|
|
|
arch = std::make_unique<GDBStubA32>();
|
|
|
@ -276,7 +276,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
|
|
|
|
const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
|
|
|
|
const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<u8> mem(size);
|
|
|
|
std::vector<u8> mem(size);
|
|
|
|
if (system.ApplicationMemory().ReadBlock(addr, mem.data(), size)) {
|
|
|
|
if (GetMemory().ReadBlock(addr, mem.data(), size)) {
|
|
|
|
// Restore any bytes belonging to replaced instructions.
|
|
|
|
// Restore any bytes belonging to replaced instructions.
|
|
|
|
auto it = replaced_instructions.lower_bound(addr);
|
|
|
|
auto it = replaced_instructions.lower_bound(addr);
|
|
|
|
for (; it != replaced_instructions.end() && it->first < addr + size; it++) {
|
|
|
|
for (; it != replaced_instructions.end() && it->first < addr + size; it++) {
|
|
|
@ -310,8 +310,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
|
|
|
|
const auto mem_substr{std::string_view(command).substr(mem_sep)};
|
|
|
|
const auto mem_substr{std::string_view(command).substr(mem_sep)};
|
|
|
|
const auto mem{Common::HexStringToVector(mem_substr, false)};
|
|
|
|
const auto mem{Common::HexStringToVector(mem_substr, false)};
|
|
|
|
|
|
|
|
|
|
|
|
if (system.ApplicationMemory().WriteBlock(addr, mem.data(), size)) {
|
|
|
|
if (GetMemory().WriteBlock(addr, mem.data(), size)) {
|
|
|
|
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, size);
|
|
|
|
Core::InvalidateInstructionCacheRange(GetProcess(), addr, size);
|
|
|
|
SendReply(GDB_STUB_REPLY_OK);
|
|
|
|
SendReply(GDB_STUB_REPLY_OK);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
SendReply(GDB_STUB_REPLY_ERR);
|
|
|
|
SendReply(GDB_STUB_REPLY_ERR);
|
|
|
@ -353,7 +353,7 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
|
|
|
|
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
|
|
|
|
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
|
|
|
|
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
|
|
|
|
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
|
|
|
|
|
|
|
|
|
|
|
|
if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) {
|
|
|
|
if (!GetMemory().IsValidVirtualAddressRange(addr, size)) {
|
|
|
|
SendReply(GDB_STUB_REPLY_ERR);
|
|
|
|
SendReply(GDB_STUB_REPLY_ERR);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -362,22 +362,20 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
|
|
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
switch (type) {
|
|
|
|
case BreakpointType::Software:
|
|
|
|
case BreakpointType::Software:
|
|
|
|
replaced_instructions[addr] = system.ApplicationMemory().Read32(addr);
|
|
|
|
replaced_instructions[addr] = GetMemory().Read32(addr);
|
|
|
|
system.ApplicationMemory().Write32(addr, arch->BreakpointInstruction());
|
|
|
|
GetMemory().Write32(addr, arch->BreakpointInstruction());
|
|
|
|
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32));
|
|
|
|
Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32));
|
|
|
|
success = true;
|
|
|
|
success = true;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case BreakpointType::WriteWatch:
|
|
|
|
case BreakpointType::WriteWatch:
|
|
|
|
success = system.ApplicationProcess()->InsertWatchpoint(addr, size,
|
|
|
|
success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
|
|
|
|
Kernel::DebugWatchpointType::Write);
|
|
|
|
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case BreakpointType::ReadWatch:
|
|
|
|
case BreakpointType::ReadWatch:
|
|
|
|
success = system.ApplicationProcess()->InsertWatchpoint(addr, size,
|
|
|
|
success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
|
|
|
|
Kernel::DebugWatchpointType::Read);
|
|
|
|
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case BreakpointType::AccessWatch:
|
|
|
|
case BreakpointType::AccessWatch:
|
|
|
|
success = system.ApplicationProcess()->InsertWatchpoint(
|
|
|
|
success =
|
|
|
|
addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
|
|
|
|
GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case BreakpointType::Hardware:
|
|
|
|
case BreakpointType::Hardware:
|
|
|
|
default:
|
|
|
|
default:
|
|
|
@ -400,7 +398,7 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
|
|
|
|
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
|
|
|
|
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
|
|
|
|
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
|
|
|
|
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
|
|
|
|
|
|
|
|
|
|
|
|
if (!system.ApplicationMemory().IsValidVirtualAddressRange(addr, size)) {
|
|
|
|
if (!GetMemory().IsValidVirtualAddressRange(addr, size)) {
|
|
|
|
SendReply(GDB_STUB_REPLY_ERR);
|
|
|
|
SendReply(GDB_STUB_REPLY_ERR);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -411,24 +409,22 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
|
|
|
|
case BreakpointType::Software: {
|
|
|
|
case BreakpointType::Software: {
|
|
|
|
const auto orig_insn{replaced_instructions.find(addr)};
|
|
|
|
const auto orig_insn{replaced_instructions.find(addr)};
|
|
|
|
if (orig_insn != replaced_instructions.end()) {
|
|
|
|
if (orig_insn != replaced_instructions.end()) {
|
|
|
|
system.ApplicationMemory().Write32(addr, orig_insn->second);
|
|
|
|
GetMemory().Write32(addr, orig_insn->second);
|
|
|
|
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), addr, sizeof(u32));
|
|
|
|
Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32));
|
|
|
|
replaced_instructions.erase(addr);
|
|
|
|
replaced_instructions.erase(addr);
|
|
|
|
success = true;
|
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case BreakpointType::WriteWatch:
|
|
|
|
case BreakpointType::WriteWatch:
|
|
|
|
success = system.ApplicationProcess()->RemoveWatchpoint(addr, size,
|
|
|
|
success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
|
|
|
|
Kernel::DebugWatchpointType::Write);
|
|
|
|
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case BreakpointType::ReadWatch:
|
|
|
|
case BreakpointType::ReadWatch:
|
|
|
|
success = system.ApplicationProcess()->RemoveWatchpoint(addr, size,
|
|
|
|
success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
|
|
|
|
Kernel::DebugWatchpointType::Read);
|
|
|
|
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case BreakpointType::AccessWatch:
|
|
|
|
case BreakpointType::AccessWatch:
|
|
|
|
success = system.ApplicationProcess()->RemoveWatchpoint(
|
|
|
|
success =
|
|
|
|
addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
|
|
|
|
GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case BreakpointType::Hardware:
|
|
|
|
case BreakpointType::Hardware:
|
|
|
|
default:
|
|
|
|
default:
|
|
|
@ -466,10 +462,10 @@ void GDBStub::HandleQuery(std::string_view command) {
|
|
|
|
const auto target_xml{arch->GetTargetXML()};
|
|
|
|
const auto target_xml{arch->GetTargetXML()};
|
|
|
|
SendReply(PaginateBuffer(target_xml, command.substr(30)));
|
|
|
|
SendReply(PaginateBuffer(target_xml, command.substr(30)));
|
|
|
|
} else if (command.starts_with("Offsets")) {
|
|
|
|
} else if (command.starts_with("Offsets")) {
|
|
|
|
const auto main_offset = Core::FindMainModuleEntrypoint(system.ApplicationProcess());
|
|
|
|
const auto main_offset = Core::FindMainModuleEntrypoint(GetProcess());
|
|
|
|
SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset)));
|
|
|
|
SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset)));
|
|
|
|
} else if (command.starts_with("Xfer:libraries:read::")) {
|
|
|
|
} else if (command.starts_with("Xfer:libraries:read::")) {
|
|
|
|
auto modules = Core::FindModules(system.ApplicationProcess());
|
|
|
|
auto modules = Core::FindModules(GetProcess());
|
|
|
|
|
|
|
|
|
|
|
|
std::string buffer;
|
|
|
|
std::string buffer;
|
|
|
|
buffer += R"(<?xml version="1.0"?>)";
|
|
|
|
buffer += R"(<?xml version="1.0"?>)";
|
|
|
@ -483,7 +479,7 @@ void GDBStub::HandleQuery(std::string_view command) {
|
|
|
|
SendReply(PaginateBuffer(buffer, command.substr(21)));
|
|
|
|
SendReply(PaginateBuffer(buffer, command.substr(21)));
|
|
|
|
} else if (command.starts_with("fThreadInfo")) {
|
|
|
|
} else if (command.starts_with("fThreadInfo")) {
|
|
|
|
// beginning of list
|
|
|
|
// beginning of list
|
|
|
|
const auto& threads = system.ApplicationProcess()->GetThreadList();
|
|
|
|
const auto& threads = GetProcess()->GetThreadList();
|
|
|
|
std::vector<std::string> thread_ids;
|
|
|
|
std::vector<std::string> thread_ids;
|
|
|
|
for (const auto& thread : threads) {
|
|
|
|
for (const auto& thread : threads) {
|
|
|
|
thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId()));
|
|
|
|
thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId()));
|
|
|
@ -497,7 +493,7 @@ void GDBStub::HandleQuery(std::string_view command) {
|
|
|
|
buffer += R"(<?xml version="1.0"?>)";
|
|
|
|
buffer += R"(<?xml version="1.0"?>)";
|
|
|
|
buffer += "<threads>";
|
|
|
|
buffer += "<threads>";
|
|
|
|
|
|
|
|
|
|
|
|
const auto& threads = system.ApplicationProcess()->GetThreadList();
|
|
|
|
const auto& threads = GetProcess()->GetThreadList();
|
|
|
|
for (const auto& thread : threads) {
|
|
|
|
for (const auto& thread : threads) {
|
|
|
|
auto thread_name{Core::GetThreadName(&thread)};
|
|
|
|
auto thread_name{Core::GetThreadName(&thread)};
|
|
|
|
if (!thread_name) {
|
|
|
|
if (!thread_name) {
|
|
|
@ -613,7 +609,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
|
|
|
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
|
|
|
|
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
|
|
|
|
std::string reply;
|
|
|
|
std::string reply;
|
|
|
|
|
|
|
|
|
|
|
|
auto* process = system.ApplicationProcess();
|
|
|
|
auto* process = GetProcess();
|
|
|
|
auto& page_table = process->GetPageTable();
|
|
|
|
auto& page_table = process->GetPageTable();
|
|
|
|
|
|
|
|
|
|
|
|
const char* commands = "Commands:\n"
|
|
|
|
const char* commands = "Commands:\n"
|
|
|
@ -714,7 +710,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
|
|
|
|
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
|
|
|
|
auto& threads{system.ApplicationProcess()->GetThreadList()};
|
|
|
|
auto& threads{GetProcess()->GetThreadList()};
|
|
|
|
for (auto& thread : threads) {
|
|
|
|
for (auto& thread : threads) {
|
|
|
|
if (thread.GetThreadId() == thread_id) {
|
|
|
|
if (thread.GetThreadId() == thread_id) {
|
|
|
|
return std::addressof(thread);
|
|
|
|
return std::addressof(thread);
|
|
|
@ -783,4 +779,12 @@ void GDBStub::SendStatus(char status) {
|
|
|
|
backend.WriteToClient(buf);
|
|
|
|
backend.WriteToClient(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Kernel::KProcess* GDBStub::GetProcess() {
|
|
|
|
|
|
|
|
return debug_process;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Core::Memory::Memory& GDBStub::GetMemory() {
|
|
|
|
|
|
|
|
return GetProcess()->GetMemory();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace Core
|
|
|
|
} // namespace Core
|
|
|
|