|
|
@ -49,28 +49,14 @@ static u8 CalculateChecksum(std::string_view data) { |
|
|
|
|
|
|
|
|
static std::string EscapeGDB(std::string_view data) { |
|
|
static std::string EscapeGDB(std::string_view data) { |
|
|
std::string escaped; |
|
|
std::string escaped; |
|
|
escaped.reserve(data.size()); |
|
|
|
|
|
|
|
|
|
|
|
for (char c : data) { |
|
|
|
|
|
|
|
|
for (char const c : data) |
|
|
switch (c) { |
|
|
switch (c) { |
|
|
case '#': |
|
|
|
|
|
escaped += "}\x03"; |
|
|
|
|
|
break; |
|
|
|
|
|
case '$': |
|
|
|
|
|
escaped += "}\x04"; |
|
|
|
|
|
break; |
|
|
|
|
|
case '*': |
|
|
|
|
|
escaped += "}\x0a"; |
|
|
|
|
|
break; |
|
|
|
|
|
case '}': |
|
|
|
|
|
escaped += "}\x5d"; |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
escaped += c; |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
case '#': escaped += "}\x03"; break; |
|
|
|
|
|
case '$': escaped += "}\x04"; break; |
|
|
|
|
|
case '*': escaped += "}\x0a"; break; |
|
|
|
|
|
case '}': escaped += "}\x5d"; break; |
|
|
|
|
|
default: escaped += c; break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return escaped; |
|
|
return escaped; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -82,38 +68,26 @@ static std::string EscapeXML(std::string_view data) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
std::string escaped; |
|
|
std::string escaped; |
|
|
escaped.reserve(data.size()); |
|
|
|
|
|
|
|
|
|
|
|
for (char32_t c : converted) { |
|
|
|
|
|
|
|
|
for (char32_t const c : converted) |
|
|
switch (c) { |
|
|
switch (c) { |
|
|
case '&': |
|
|
|
|
|
escaped += "&"; |
|
|
|
|
|
break; |
|
|
|
|
|
case '"': |
|
|
|
|
|
escaped += """; |
|
|
|
|
|
break; |
|
|
|
|
|
case '<': |
|
|
|
|
|
escaped += "<"; |
|
|
|
|
|
break; |
|
|
|
|
|
case '>': |
|
|
|
|
|
escaped += ">"; |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
case '&': escaped += "&"; break; |
|
|
|
|
|
case '"': escaped += """; break; |
|
|
|
|
|
case '<': escaped += "<"; break; |
|
|
|
|
|
case '>': escaped += ">"; break; |
|
|
default: |
|
|
default: |
|
|
if (c > 0x7f) { |
|
|
if (c > 0x7f) { |
|
|
escaped += fmt::format("&#{};", static_cast<u32>(c)); |
|
|
|
|
|
|
|
|
escaped += fmt::format("&#{};", u32(c)); |
|
|
} else { |
|
|
} else { |
|
|
escaped += static_cast<char>(c); |
|
|
|
|
|
|
|
|
escaped += char(c); |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return escaped; |
|
|
return escaped; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_, Kernel::KProcess* debug_process_) |
|
|
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_, Kernel::KProcess* debug_process_) |
|
|
: DebuggerFrontend(backend_), system{system_}, debug_process{debug_process_} { |
|
|
: DebuggerFrontend(backend_), system{system_}, debug_process{debug_process_} { |
|
|
if (GetProcess()->Is64Bit()) { |
|
|
|
|
|
|
|
|
if (debug_process->Is64Bit()) { |
|
|
arch = std::make_unique<GDBStubA64>(); |
|
|
arch = std::make_unique<GDBStubA64>(); |
|
|
} else { |
|
|
} else { |
|
|
arch = std::make_unique<GDBStubA32>(); |
|
|
arch = std::make_unique<GDBStubA32>(); |
|
|
@ -148,56 +122,42 @@ void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) { |
|
|
std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) { |
|
|
std::vector<DebuggerAction> actions; |
|
|
|
|
|
current_command.insert(current_command.end(), data.begin(), data.end()); |
|
|
current_command.insert(current_command.end(), data.begin(), data.end()); |
|
|
|
|
|
|
|
|
while (current_command.size() != 0) { |
|
|
|
|
|
|
|
|
std::vector<DebuggerAction> actions; |
|
|
|
|
|
while (!current_command.empty()) |
|
|
ProcessData(actions); |
|
|
ProcessData(actions); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return actions; |
|
|
return actions; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) { |
|
|
void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) { |
|
|
const char c{current_command[0]}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char c = current_command[0]; |
|
|
// Acknowledgement
|
|
|
// Acknowledgement
|
|
|
if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) { |
|
|
if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) { |
|
|
current_command.erase(current_command.begin()); |
|
|
current_command.erase(current_command.begin()); |
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Interrupt
|
|
|
// Interrupt
|
|
|
if (c == GDB_STUB_INT3) { |
|
|
|
|
|
|
|
|
} else if (c == GDB_STUB_INT3) { |
|
|
LOG_INFO(Debug_GDBStub, "Received interrupt"); |
|
|
LOG_INFO(Debug_GDBStub, "Received interrupt"); |
|
|
current_command.erase(current_command.begin()); |
|
|
current_command.erase(current_command.begin()); |
|
|
actions.push_back(DebuggerAction::Interrupt); |
|
|
actions.push_back(DebuggerAction::Interrupt); |
|
|
SendStatus(GDB_STUB_ACK); |
|
|
SendStatus(GDB_STUB_ACK); |
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Otherwise, require the data to be the start of a command
|
|
|
// Otherwise, require the data to be the start of a command
|
|
|
if (c != GDB_STUB_START) { |
|
|
|
|
|
|
|
|
} else if (c != GDB_STUB_START) { |
|
|
LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data()); |
|
|
LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data()); |
|
|
current_command.clear(); |
|
|
current_command.clear(); |
|
|
SendStatus(GDB_STUB_NACK); |
|
|
SendStatus(GDB_STUB_NACK); |
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Continue reading until command is complete
|
|
|
|
|
|
while (CommandEnd() == current_command.end()) { |
|
|
|
|
|
const auto new_data{backend.ReadFromClient()}; |
|
|
|
|
|
current_command.insert(current_command.end(), new_data.begin(), new_data.end()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Execute and respond to GDB
|
|
|
|
|
|
const auto command{DetachCommand()}; |
|
|
|
|
|
|
|
|
|
|
|
if (command) { |
|
|
|
|
|
SendStatus(GDB_STUB_ACK); |
|
|
|
|
|
ExecuteCommand(*command, actions); |
|
|
|
|
|
} else { |
|
|
} else { |
|
|
SendStatus(GDB_STUB_NACK); |
|
|
|
|
|
|
|
|
// Continue reading until command is complete
|
|
|
|
|
|
while (CommandEnd() == current_command.end()) { |
|
|
|
|
|
const auto new_data{backend.ReadFromClient()}; |
|
|
|
|
|
current_command.insert(current_command.end(), new_data.begin(), new_data.end()); |
|
|
|
|
|
} |
|
|
|
|
|
// Execute and respond to GDB
|
|
|
|
|
|
if (auto const cmd = DetachCommand(); cmd) { |
|
|
|
|
|
SendStatus(GDB_STUB_ACK); |
|
|
|
|
|
ExecuteCommand(*cmd, actions); |
|
|
|
|
|
} else { |
|
|
|
|
|
SendStatus(GDB_STUB_NACK); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -207,9 +167,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction |
|
|
if (packet.length() == 0) { |
|
|
if (packet.length() == 0) { |
|
|
SendReply(GDB_STUB_REPLY_ERR); |
|
|
SendReply(GDB_STUB_REPLY_ERR); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (packet.starts_with("vCont")) { |
|
|
|
|
|
|
|
|
} else if (packet.starts_with("vCont")) { |
|
|
HandleVCont(packet.substr(5), actions); |
|
|
HandleVCont(packet.substr(5), actions); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
@ -218,14 +176,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction |
|
|
|
|
|
|
|
|
switch (packet[0]) { |
|
|
switch (packet[0]) { |
|
|
case 'H': { |
|
|
case 'H': { |
|
|
Kernel::KThread* thread{nullptr}; |
|
|
|
|
|
s64 thread_id{strtoll(command.data() + 1, nullptr, 16)}; |
|
|
|
|
|
if (thread_id >= 1) { |
|
|
|
|
|
thread = GetThreadByID(thread_id); |
|
|
|
|
|
} else { |
|
|
|
|
|
thread = backend.GetActiveThread(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
s64 thread_id = strtoll(command.data() + 1, nullptr, 16); |
|
|
|
|
|
Kernel::KThread* thread = thread_id >= 1 ? GetThreadByID(thread_id) : backend.GetActiveThread(); |
|
|
if (thread) { |
|
|
if (thread) { |
|
|
SendReply(GDB_STUB_REPLY_OK); |
|
|
SendReply(GDB_STUB_REPLY_OK); |
|
|
backend.SetActiveThread(thread); |
|
|
backend.SetActiveThread(thread); |
|
|
@ -235,7 +187,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
case 'T': { |
|
|
case 'T': { |
|
|
s64 thread_id{strtoll(command.data(), nullptr, 16)}; |
|
|
|
|
|
|
|
|
s64 thread_id = strtoll(command.data(), nullptr, 16); |
|
|
if (GetThreadByID(thread_id)) { |
|
|
if (GetThreadByID(thread_id)) { |
|
|
SendReply(GDB_STUB_REPLY_OK); |
|
|
SendReply(GDB_STUB_REPLY_OK); |
|
|
} else { |
|
|
} else { |
|
|
@ -262,27 +214,26 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction |
|
|
SendReply(GDB_STUB_REPLY_OK); |
|
|
SendReply(GDB_STUB_REPLY_OK); |
|
|
break; |
|
|
break; |
|
|
case 'p': { |
|
|
case 'p': { |
|
|
const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))}; |
|
|
|
|
|
|
|
|
const size_t reg = size_t(strtoll(command.data(), nullptr, 16)); |
|
|
SendReply(arch->RegRead(backend.GetActiveThread(), reg)); |
|
|
SendReply(arch->RegRead(backend.GetActiveThread(), reg)); |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
case 'P': { |
|
|
case 'P': { |
|
|
const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1}; |
|
|
|
|
|
const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))}; |
|
|
|
|
|
|
|
|
const auto sep = std::find(command.begin(), command.end(), '=') - command.begin() + 1; |
|
|
|
|
|
const size_t reg = size_t(strtoll(command.data(), nullptr, 16)); |
|
|
arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep)); |
|
|
arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep)); |
|
|
SendReply(GDB_STUB_REPLY_OK); |
|
|
SendReply(GDB_STUB_REPLY_OK); |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
case 'm': { |
|
|
case 'm': { |
|
|
const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; |
|
|
const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; |
|
|
const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))}; |
|
|
|
|
|
const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))}; |
|
|
|
|
|
|
|
|
const size_t addr = size_t(strtoll(command.data(), nullptr, 16)); |
|
|
|
|
|
const size_t size = size_t(strtoll(command.data() + sep, nullptr, 16)); |
|
|
|
|
|
|
|
|
std::vector<u8> mem(size); |
|
|
std::vector<u8> mem(size); |
|
|
if (GetMemory().ReadBlock(addr, mem.data(), size)) { |
|
|
|
|
|
|
|
|
if (debug_process->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); |
|
|
|
|
|
for (; it != replaced_instructions.end() && it->first < addr + size; it++) { |
|
|
|
|
|
|
|
|
for (auto it = replaced_instructions.lower_bound(addr); it != replaced_instructions.end() && it->first < addr + size; it++) { |
|
|
// Get the bytes of the instruction we previously replaced.
|
|
|
// Get the bytes of the instruction we previously replaced.
|
|
|
const u32 original_bytes = it->second; |
|
|
const u32 original_bytes = it->second; |
|
|
|
|
|
|
|
|
@ -307,14 +258,14 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction |
|
|
const auto size_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; |
|
|
const auto size_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; |
|
|
const auto mem_sep{std::find(command.begin(), command.end(), ':') - command.begin() + 1}; |
|
|
const auto mem_sep{std::find(command.begin(), command.end(), ':') - command.begin() + 1}; |
|
|
|
|
|
|
|
|
const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))}; |
|
|
|
|
|
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))}; |
|
|
|
|
|
|
|
|
const size_t addr{size_t(strtoll(command.data(), nullptr, 16))}; |
|
|
|
|
|
const size_t size{size_t(strtoll(command.data() + size_sep, nullptr, 16))}; |
|
|
|
|
|
|
|
|
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 (GetMemory().WriteBlock(addr, mem.data(), size)) { |
|
|
|
|
|
Core::InvalidateInstructionCacheRange(GetProcess(), addr, size); |
|
|
|
|
|
|
|
|
if (debug_process->GetMemory().WriteBlock(addr, mem.data(), size)) { |
|
|
|
|
|
Core::InvalidateInstructionCacheRange(debug_process, addr, size); |
|
|
SendReply(GDB_STUB_REPLY_OK); |
|
|
SendReply(GDB_STUB_REPLY_OK); |
|
|
} else { |
|
|
} else { |
|
|
SendReply(GDB_STUB_REPLY_ERR); |
|
|
SendReply(GDB_STUB_REPLY_ERR); |
|
|
@ -349,14 +300,13 @@ enum class BreakpointType { |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
void GDBStub::HandleBreakpointInsert(std::string_view command) { |
|
|
void GDBStub::HandleBreakpointInsert(std::string_view command) { |
|
|
const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))}; |
|
|
|
|
|
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; |
|
|
|
|
|
const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') - |
|
|
|
|
|
command.begin() + 1}; |
|
|
|
|
|
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))}; |
|
|
|
|
|
|
|
|
|
|
|
if (!GetMemory().IsValidVirtualAddressRange(addr, size)) { |
|
|
|
|
|
|
|
|
const auto type = BreakpointType(strtoll(command.data(), nullptr, 16)); |
|
|
|
|
|
const auto addr_sep = std::find(command.begin(), command.end(), ',') - command.begin() + 1; |
|
|
|
|
|
const auto size_sep = std::find(command.begin() + addr_sep, command.end(), ',') - command.begin() + 1; |
|
|
|
|
|
const size_t addr = size_t(strtoll(command.data() + addr_sep, nullptr, 16)); |
|
|
|
|
|
const size_t size = size_t(strtoll(command.data() + size_sep, nullptr, 16)); |
|
|
|
|
|
|
|
|
|
|
|
if (!debug_process->GetMemory().IsValidVirtualAddressRange(addr, size)) { |
|
|
SendReply(GDB_STUB_REPLY_ERR); |
|
|
SendReply(GDB_STUB_REPLY_ERR); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
@ -365,20 +315,19 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) { |
|
|
|
|
|
|
|
|
switch (type) { |
|
|
switch (type) { |
|
|
case BreakpointType::Software: |
|
|
case BreakpointType::Software: |
|
|
replaced_instructions[addr] = GetMemory().Read32(addr); |
|
|
|
|
|
GetMemory().Write32(addr, arch->BreakpointInstruction()); |
|
|
|
|
|
Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32)); |
|
|
|
|
|
|
|
|
replaced_instructions[addr] = debug_process->GetMemory().Read32(addr); |
|
|
|
|
|
debug_process->GetMemory().Write32(addr, arch->BreakpointInstruction()); |
|
|
|
|
|
Core::InvalidateInstructionCacheRange(debug_process, addr, sizeof(u32)); |
|
|
success = true; |
|
|
success = true; |
|
|
break; |
|
|
break; |
|
|
case BreakpointType::WriteWatch: |
|
|
case BreakpointType::WriteWatch: |
|
|
success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); |
|
|
|
|
|
|
|
|
success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); |
|
|
break; |
|
|
break; |
|
|
case BreakpointType::ReadWatch: |
|
|
case BreakpointType::ReadWatch: |
|
|
success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); |
|
|
|
|
|
|
|
|
success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); |
|
|
break; |
|
|
break; |
|
|
case BreakpointType::AccessWatch: |
|
|
case BreakpointType::AccessWatch: |
|
|
success = |
|
|
|
|
|
GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite); |
|
|
|
|
|
|
|
|
success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite); |
|
|
break; |
|
|
break; |
|
|
case BreakpointType::Hardware: |
|
|
case BreakpointType::Hardware: |
|
|
default: |
|
|
default: |
|
|
@ -393,41 +342,37 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void GDBStub::HandleBreakpointRemove(std::string_view command) { |
|
|
|
|
|
const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))}; |
|
|
|
|
|
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; |
|
|
|
|
|
const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') - |
|
|
|
|
|
command.begin() + 1}; |
|
|
|
|
|
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))}; |
|
|
|
|
|
|
|
|
void GDBStub::HandleBreakpointRemove(std::string_view sv) { |
|
|
|
|
|
const auto type = BreakpointType(strtoll(sv.data(), nullptr, 16)); |
|
|
|
|
|
const auto addr_sep = std::find(sv.begin(), sv.end(), ',') - sv.begin() + 1; |
|
|
|
|
|
const auto size_sep = std::find(sv.begin() + addr_sep, sv.end(), ',') - sv.begin() + 1; |
|
|
|
|
|
const size_t addr = size_t(strtoll(sv.data() + addr_sep, nullptr, 16)); |
|
|
|
|
|
const size_t size = size_t(strtoll(sv.data() + size_sep, nullptr, 16)); |
|
|
|
|
|
|
|
|
if (!GetMemory().IsValidVirtualAddressRange(addr, size)) { |
|
|
|
|
|
|
|
|
if (!debug_process->GetMemory().IsValidVirtualAddressRange(addr, size)) { |
|
|
SendReply(GDB_STUB_REPLY_ERR); |
|
|
SendReply(GDB_STUB_REPLY_ERR); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
bool success{}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool success = false; |
|
|
switch (type) { |
|
|
switch (type) { |
|
|
case BreakpointType::Software: { |
|
|
case BreakpointType::Software: { |
|
|
const auto orig_insn{replaced_instructions.find(addr)}; |
|
|
|
|
|
if (orig_insn != replaced_instructions.end()) { |
|
|
|
|
|
GetMemory().Write32(addr, orig_insn->second); |
|
|
|
|
|
Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32)); |
|
|
|
|
|
|
|
|
if (auto const orig_insn = replaced_instructions.find(addr); orig_insn != replaced_instructions.end()) { |
|
|
|
|
|
debug_process->GetMemory().Write32(addr, orig_insn->second); |
|
|
|
|
|
Core::InvalidateInstructionCacheRange(debug_process, addr, sizeof(u32)); |
|
|
replaced_instructions.erase(addr); |
|
|
replaced_instructions.erase(addr); |
|
|
success = true; |
|
|
success = true; |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
case BreakpointType::WriteWatch: |
|
|
case BreakpointType::WriteWatch: |
|
|
success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); |
|
|
|
|
|
|
|
|
success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); |
|
|
break; |
|
|
break; |
|
|
case BreakpointType::ReadWatch: |
|
|
case BreakpointType::ReadWatch: |
|
|
success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); |
|
|
|
|
|
|
|
|
success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); |
|
|
break; |
|
|
break; |
|
|
case BreakpointType::AccessWatch: |
|
|
case BreakpointType::AccessWatch: |
|
|
success = |
|
|
|
|
|
GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite); |
|
|
|
|
|
|
|
|
success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite); |
|
|
break; |
|
|
break; |
|
|
case BreakpointType::Hardware: |
|
|
case BreakpointType::Hardware: |
|
|
default: |
|
|
default: |
|
|
@ -454,22 +399,21 @@ static std::string PaginateBuffer(std::string_view buffer, std::string_view requ |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void GDBStub::HandleQuery(std::string_view command) { |
|
|
|
|
|
if (command.starts_with("TStatus")) { |
|
|
|
|
|
|
|
|
void GDBStub::HandleQuery(std::string_view sv) { |
|
|
|
|
|
if (sv.starts_with("TStatus")) { |
|
|
// no tracepoint support
|
|
|
// no tracepoint support
|
|
|
SendReply("T0"); |
|
|
SendReply("T0"); |
|
|
} else if (command.starts_with("Supported")) { |
|
|
|
|
|
|
|
|
} else if (sv.starts_with("Supported")) { |
|
|
SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;" |
|
|
SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;" |
|
|
"vContSupported+;QStartNoAckMode+"); |
|
|
"vContSupported+;QStartNoAckMode+"); |
|
|
} else if (command.starts_with("Xfer:features:read:target.xml:")) { |
|
|
|
|
|
|
|
|
} else if (sv.starts_with("Xfer:features:read:target.xml:")) { |
|
|
const auto target_xml{arch->GetTargetXML()}; |
|
|
const auto target_xml{arch->GetTargetXML()}; |
|
|
SendReply(PaginateBuffer(target_xml, command.substr(30))); |
|
|
|
|
|
} else if (command.starts_with("Offsets")) { |
|
|
|
|
|
const auto main_offset = Core::FindMainModuleEntrypoint(GetProcess()); |
|
|
|
|
|
|
|
|
SendReply(PaginateBuffer(target_xml, sv.substr(30))); |
|
|
|
|
|
} else if (sv.starts_with("Offsets")) { |
|
|
|
|
|
const auto main_offset = Core::FindMainModuleEntrypoint(debug_process); |
|
|
SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset))); |
|
|
SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset))); |
|
|
} else if (command.starts_with("Xfer:libraries:read::")) { |
|
|
|
|
|
auto modules = Core::FindModules(GetProcess()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else if (sv.starts_with("Xfer:libraries:read::")) { |
|
|
|
|
|
auto modules = Core::FindModules(debug_process); |
|
|
std::string buffer; |
|
|
std::string buffer; |
|
|
buffer += R"(<?xml version="1.0"?>)"; |
|
|
buffer += R"(<?xml version="1.0"?>)"; |
|
|
buffer += "<library-list>"; |
|
|
buffer += "<library-list>"; |
|
|
@ -479,24 +423,23 @@ void GDBStub::HandleQuery(std::string_view command) { |
|
|
} |
|
|
} |
|
|
buffer += "</library-list>"; |
|
|
buffer += "</library-list>"; |
|
|
|
|
|
|
|
|
SendReply(PaginateBuffer(buffer, command.substr(21))); |
|
|
|
|
|
} else if (command.starts_with("fThreadInfo")) { |
|
|
|
|
|
|
|
|
SendReply(PaginateBuffer(buffer, sv.substr(21))); |
|
|
|
|
|
} else if (sv.starts_with("fThreadInfo")) { |
|
|
// beginning of list
|
|
|
// beginning of list
|
|
|
const auto& threads = GetProcess()->GetThreadList(); |
|
|
|
|
|
|
|
|
const auto& threads = debug_process->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())); |
|
|
} |
|
|
|
|
|
SendReply(fmt::format("m{}", fmt::join(thread_ids, ","))); |
|
|
SendReply(fmt::format("m{}", fmt::join(thread_ids, ","))); |
|
|
} else if (command.starts_with("sThreadInfo")) { |
|
|
|
|
|
|
|
|
} else if (sv.starts_with("sThreadInfo")) { |
|
|
// end of list
|
|
|
// end of list
|
|
|
SendReply("l"); |
|
|
SendReply("l"); |
|
|
} else if (command.starts_with("Xfer:threads:read::")) { |
|
|
|
|
|
|
|
|
} else if (sv.starts_with("Xfer:threads:read::")) { |
|
|
std::string buffer; |
|
|
std::string buffer; |
|
|
buffer += R"(<?xml version="1.0"?>)"; |
|
|
buffer += R"(<?xml version="1.0"?>)"; |
|
|
buffer += "<threads>"; |
|
|
buffer += "<threads>"; |
|
|
|
|
|
|
|
|
const auto& threads = GetProcess()->GetThreadList(); |
|
|
|
|
|
|
|
|
const auto& threads = debug_process->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) { |
|
|
@ -510,109 +453,95 @@ void GDBStub::HandleQuery(std::string_view command) { |
|
|
|
|
|
|
|
|
buffer += "</threads>"; |
|
|
buffer += "</threads>"; |
|
|
|
|
|
|
|
|
SendReply(PaginateBuffer(buffer, command.substr(19))); |
|
|
|
|
|
} else if (command.starts_with("Attached")) { |
|
|
|
|
|
|
|
|
SendReply(PaginateBuffer(buffer, sv.substr(19))); |
|
|
|
|
|
} else if (sv.starts_with("Attached")) { |
|
|
SendReply("0"); |
|
|
SendReply("0"); |
|
|
} else if (command.starts_with("StartNoAckMode")) { |
|
|
|
|
|
|
|
|
} else if (sv.starts_with("StartNoAckMode")) { |
|
|
no_ack = true; |
|
|
no_ack = true; |
|
|
SendReply(GDB_STUB_REPLY_OK); |
|
|
SendReply(GDB_STUB_REPLY_OK); |
|
|
} else if (command.starts_with("Rcmd,")) { |
|
|
|
|
|
HandleRcmd(Common::HexStringToVector(command.substr(5), false)); |
|
|
|
|
|
|
|
|
} else if (sv.starts_with("Rcmd,")) { |
|
|
|
|
|
HandleRcmd(Common::HexStringToVector(sv.substr(5), false)); |
|
|
} else { |
|
|
} else { |
|
|
SendReply(GDB_STUB_REPLY_EMPTY); |
|
|
SendReply(GDB_STUB_REPLY_EMPTY); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) { |
|
|
|
|
|
if (command == "?") { |
|
|
|
|
|
// Continuing and stepping are supported
|
|
|
|
|
|
// (signal is ignored, but required for GDB to use vCont)
|
|
|
|
|
|
|
|
|
void GDBStub::HandleVCont(std::string_view sv, std::vector<DebuggerAction>& actions) { |
|
|
|
|
|
// Continuing and stepping are supported (signal is ignored, but required for GDB to use vCont)
|
|
|
|
|
|
if (sv == "?") { |
|
|
SendReply("vCont;c;C;s;S"); |
|
|
SendReply("vCont;c;C;s;S"); |
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Kernel::KThread* stepped_thread{nullptr}; |
|
|
|
|
|
bool lock_execution{true}; |
|
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> entries; |
|
|
|
|
|
boost::split(entries, command.substr(1), boost::is_any_of(";")); |
|
|
|
|
|
for (const auto& thread_action : entries) { |
|
|
|
|
|
std::vector<std::string> parts; |
|
|
|
|
|
boost::split(parts, thread_action, boost::is_any_of(":")); |
|
|
|
|
|
|
|
|
|
|
|
if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) { |
|
|
|
|
|
lock_execution = false; |
|
|
|
|
|
} |
|
|
|
|
|
if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) { |
|
|
|
|
|
stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16)); |
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
Kernel::KThread* stepped_thread = nullptr; |
|
|
|
|
|
bool lock_execution = true; |
|
|
|
|
|
std::vector<std::string> entries; |
|
|
|
|
|
boost::split(entries, sv.substr(1), boost::is_any_of(";")); |
|
|
|
|
|
for (auto const& thread_action : entries) { |
|
|
|
|
|
std::vector<std::string> parts; |
|
|
|
|
|
boost::split(parts, thread_action, boost::is_any_of(":")); |
|
|
|
|
|
if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) |
|
|
|
|
|
lock_execution = false; |
|
|
|
|
|
if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) |
|
|
|
|
|
stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16)); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (stepped_thread) { |
|
|
|
|
|
backend.SetActiveThread(stepped_thread); |
|
|
|
|
|
actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked |
|
|
|
|
|
: DebuggerAction::StepThreadUnlocked); |
|
|
|
|
|
} else { |
|
|
|
|
|
actions.push_back(DebuggerAction::Continue); |
|
|
|
|
|
|
|
|
if (stepped_thread) { |
|
|
|
|
|
backend.SetActiveThread(stepped_thread); |
|
|
|
|
|
actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked : DebuggerAction::StepThreadUnlocked); |
|
|
|
|
|
} else { |
|
|
|
|
|
actions.push_back(DebuggerAction::Continue); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) { |
|
|
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) { |
|
|
constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{ |
|
|
|
|
|
{"----- Free ------", Kernel::Svc::MemoryState::Free}, |
|
|
|
|
|
{"Io ", Kernel::Svc::MemoryState::Io}, |
|
|
|
|
|
{"Static ", Kernel::Svc::MemoryState::Static}, |
|
|
|
|
|
{"Code ", Kernel::Svc::MemoryState::Code}, |
|
|
|
|
|
{"CodeData ", Kernel::Svc::MemoryState::CodeData}, |
|
|
|
|
|
{"Normal ", Kernel::Svc::MemoryState::Normal}, |
|
|
|
|
|
{"Shared ", Kernel::Svc::MemoryState::Shared}, |
|
|
|
|
|
{"AliasCode ", Kernel::Svc::MemoryState::AliasCode}, |
|
|
|
|
|
{"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData}, |
|
|
|
|
|
{"Ipc ", Kernel::Svc::MemoryState::Ipc}, |
|
|
|
|
|
{"Stack ", Kernel::Svc::MemoryState::Stack}, |
|
|
|
|
|
{"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal}, |
|
|
|
|
|
{"Transferred ", Kernel::Svc::MemoryState::Transferred}, |
|
|
|
|
|
{"SharedTransferred", Kernel::Svc::MemoryState::SharedTransferred}, |
|
|
|
|
|
{"SharedCode ", Kernel::Svc::MemoryState::SharedCode}, |
|
|
|
|
|
{"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible}, |
|
|
|
|
|
{"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc}, |
|
|
|
|
|
{"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc}, |
|
|
|
|
|
{"Kernel ", Kernel::Svc::MemoryState::Kernel}, |
|
|
|
|
|
{"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode}, |
|
|
|
|
|
{"CodeOut ", Kernel::Svc::MemoryState::CodeOut}, |
|
|
|
|
|
{"Coverage ", Kernel::Svc::MemoryState::Coverage}, |
|
|
|
|
|
}}; |
|
|
|
|
|
for (size_t i = 0; i < MemoryStateNames.size(); i++) { |
|
|
|
|
|
if (std::get<1>(MemoryStateNames[i]) == state) { |
|
|
|
|
|
return std::get<0>(MemoryStateNames[i]); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
#define MEMORY_STATE_LIST \
|
|
|
|
|
|
MEMORY_STATE_ELEM(Free) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(Io) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(Static) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(Code) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(CodeData) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(Normal) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(Shared) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(AliasCode) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(AliasCodeData) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(Ipc) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(Stack) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(ThreadLocal) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(Transferred) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(SharedTransferred) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(SharedCode) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(Inaccessible) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(NonSecureIpc) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(NonDeviceIpc) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(Kernel) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(GeneratedCode) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(CodeOut) \ |
|
|
|
|
|
MEMORY_STATE_ELEM(Coverage) |
|
|
|
|
|
switch (state) { |
|
|
|
|
|
#define MEMORY_STATE_ELEM(elem) case Kernel::Svc::MemoryState::elem: return #elem;
|
|
|
|
|
|
MEMORY_STATE_LIST |
|
|
|
|
|
#undef MEMORY_STATE_LIST
|
|
|
|
|
|
default: return "Unknown"; |
|
|
} |
|
|
} |
|
|
return "Unknown "; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) { |
|
|
static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) { |
|
|
if (info.state == Kernel::Svc::MemoryState::Free) { |
|
|
if (info.state == Kernel::Svc::MemoryState::Free) { |
|
|
return " "; |
|
|
return " "; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
switch (info.permission) { |
|
|
|
|
|
case Kernel::Svc::MemoryPermission::ReadExecute: |
|
|
|
|
|
return "r-x"; |
|
|
|
|
|
case Kernel::Svc::MemoryPermission::Read: |
|
|
|
|
|
return "r--"; |
|
|
|
|
|
case Kernel::Svc::MemoryPermission::ReadWrite: |
|
|
|
|
|
return "rw-"; |
|
|
|
|
|
default: |
|
|
|
|
|
return "---"; |
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
switch (info.permission) { |
|
|
|
|
|
case Kernel::Svc::MemoryPermission::ReadExecute: return "r-x"; |
|
|
|
|
|
case Kernel::Svc::MemoryPermission::Read: return "r--"; |
|
|
|
|
|
case Kernel::Svc::MemoryPermission::ReadWrite: return "rw-"; |
|
|
|
|
|
default: return "---"; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void GDBStub::HandleRcmd(const std::vector<u8>& command) { |
|
|
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 = GetProcess(); |
|
|
|
|
|
auto& page_table = process->GetPageTable(); |
|
|
|
|
|
|
|
|
auto& page_table = debug_process->GetPageTable(); |
|
|
if (command_str == "fastmem" || command_str == "get fastmem") { |
|
|
if (command_str == "fastmem" || command_str == "get fastmem") { |
|
|
if (Settings::IsFastmemEnabled()) { |
|
|
if (Settings::IsFastmemEnabled()) { |
|
|
const auto& impl = page_table.GetImpl(); |
|
|
const auto& impl = page_table.GetImpl(); |
|
|
@ -627,11 +556,13 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { |
|
|
reply = "Fastmem is not enabled.\n"; |
|
|
reply = "Fastmem is not enabled.\n"; |
|
|
} |
|
|
} |
|
|
} else if (command_str == "info" || command_str == "get info") { |
|
|
} else if (command_str == "info" || command_str == "get info") { |
|
|
auto modules = Core::FindModules(process); |
|
|
|
|
|
|
|
|
auto modules = Core::FindModules(debug_process); |
|
|
|
|
|
|
|
|
reply = fmt::format("Process: {:#x} ({})\n" |
|
|
reply = fmt::format("Process: {:#x} ({})\n" |
|
|
"Program Id: {:#018x}\n", |
|
|
"Program Id: {:#018x}\n", |
|
|
process->GetProcessId(), process->GetName(), process->GetProgramId()); |
|
|
|
|
|
|
|
|
debug_process->GetProcessId(), |
|
|
|
|
|
debug_process->GetName(), |
|
|
|
|
|
debug_process->GetProgramId()); |
|
|
reply += fmt::format( |
|
|
reply += fmt::format( |
|
|
"Layout:\n" |
|
|
"Layout:\n" |
|
|
" Alias: {:#012x} - {:#012x}\n" |
|
|
" Alias: {:#012x} - {:#012x}\n" |
|
|
@ -648,10 +579,8 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { |
|
|
GetInteger(page_table.GetStackRegionStart()), |
|
|
GetInteger(page_table.GetStackRegionStart()), |
|
|
GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1); |
|
|
GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1); |
|
|
|
|
|
|
|
|
for (const auto& [vaddr, name] : modules) { |
|
|
|
|
|
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, |
|
|
|
|
|
GetInteger(Core::GetModuleEnd(process, vaddr)), name); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
for (const auto& [vaddr, name] : modules) |
|
|
|
|
|
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, GetInteger(Core::GetModuleEnd(debug_process, vaddr)), name); |
|
|
} else if (command_str == "mappings" || command_str == "get mappings") { |
|
|
} else if (command_str == "mappings" || command_str == "get mappings") { |
|
|
reply = "Mappings:\n"; |
|
|
reply = "Mappings:\n"; |
|
|
VAddr cur_addr = 0; |
|
|
VAddr cur_addr = 0; |
|
|
@ -683,10 +612,8 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size; |
|
|
const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size; |
|
|
if (next_address <= cur_addr) { |
|
|
|
|
|
|
|
|
if (next_address <= cur_addr) |
|
|
break; |
|
|
break; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
cur_addr = next_address; |
|
|
cur_addr = next_address; |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
@ -698,20 +625,16 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { |
|
|
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { |
|
|
auto& threads{GetProcess()->GetThreadList()}; |
|
|
|
|
|
for (auto& thread : threads) { |
|
|
|
|
|
if (thread.GetThreadId() == thread_id) { |
|
|
|
|
|
|
|
|
auto& threads = debug_process->GetThreadList(); |
|
|
|
|
|
for (auto& thread : threads) |
|
|
|
|
|
if (thread.GetThreadId() == thread_id) |
|
|
return std::addressof(thread); |
|
|
return std::addressof(thread); |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return nullptr; |
|
|
return nullptr; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
std::vector<char>::const_iterator GDBStub::CommandEnd() const { |
|
|
std::vector<char>::const_iterator GDBStub::CommandEnd() const { |
|
|
// Find the end marker
|
|
|
// Find the end marker
|
|
|
const auto end{std::find(current_command.begin(), current_command.end(), GDB_STUB_END)}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto end = std::find(current_command.begin(), current_command.end(), GDB_STUB_END); |
|
|
// Require the checksum to be present
|
|
|
// Require the checksum to be present
|
|
|
return (std::min)(end + 2, current_command.end()); |
|
|
return (std::min)(end + 2, current_command.end()); |
|
|
} |
|
|
} |
|
|
@ -737,8 +660,7 @@ std::optional<std::string> GDBStub::DetachCommand() { |
|
|
|
|
|
|
|
|
// Verify checksum
|
|
|
// Verify checksum
|
|
|
if (calculated != received) { |
|
|
if (calculated != received) { |
|
|
LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}", |
|
|
|
|
|
calculated, received); |
|
|
|
|
|
|
|
|
LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}", calculated, received); |
|
|
return std::nullopt; |
|
|
return std::nullopt; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -746,9 +668,8 @@ std::optional<std::string> GDBStub::DetachCommand() { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void GDBStub::SendReply(std::string_view data) { |
|
|
void GDBStub::SendReply(std::string_view data) { |
|
|
const auto escaped{EscapeGDB(data)}; |
|
|
|
|
|
const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END, |
|
|
|
|
|
CalculateChecksum(escaped))}; |
|
|
|
|
|
|
|
|
const auto escaped = EscapeGDB(data); |
|
|
|
|
|
const auto output = fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END, CalculateChecksum(escaped)); |
|
|
LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output); |
|
|
LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output); |
|
|
|
|
|
|
|
|
// C++ string support is complete rubbish
|
|
|
// C++ string support is complete rubbish
|
|
|
@ -758,21 +679,11 @@ void GDBStub::SendReply(std::string_view data) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void GDBStub::SendStatus(char status) { |
|
|
void GDBStub::SendStatus(char status) { |
|
|
if (no_ack) { |
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
if (!no_ack) { |
|
|
|
|
|
std::array<u8, 1> buf = {u8(status)}; |
|
|
|
|
|
LOG_TRACE(Debug_GDBStub, "Writing status: {}", status); |
|
|
|
|
|
backend.WriteToClient(buf); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
std::array<u8, 1> buf = {static_cast<u8>(status)}; |
|
|
|
|
|
LOG_TRACE(Debug_GDBStub, "Writing status: {}", status); |
|
|
|
|
|
backend.WriteToClient(buf); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Kernel::KProcess* GDBStub::GetProcess() { |
|
|
|
|
|
return debug_process; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Core::Memory::Memory& GDBStub::GetMemory() { |
|
|
|
|
|
return GetProcess()->GetMemory(); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} // namespace Core
|
|
|
} // namespace Core
|