diff --git a/src/xenia/debug/gdb/gdbstub.cc b/src/xenia/debug/gdb/gdbstub.cc index 930a77998..49488bc07 100644 --- a/src/xenia/debug/gdb/gdbstub.cc +++ b/src/xenia/debug/gdb/gdbstub.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2022 Ben Vanik. All rights reserved. * + * Copyright 2024 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -52,7 +52,8 @@ constexpr const char* kGdbReplyError = "E01"; constexpr int kSignalSigtrap = 5; -constexpr char memory_map[] = +// must start with l for debugger to accept it +constexpr char kMemoryMapXml[] = R"(l @@ -66,9 +67,8 @@ constexpr char memory_map[] = )"; -// must start with l for debugger to accept it // TODO: add power-altivec.xml (and update ReadRegister to support it) -constexpr char target_xml[] = +constexpr char kTargetXml[] = R"(l @@ -152,26 +152,6 @@ constexpr char target_xml[] = )"; -std::string u64_to_padded_hex(uint64_t value) { - return fmt::format("{:016x}", value); -} - -std::string u32_to_padded_hex(uint32_t value) { - return fmt::format("{:08x}", value); -} - -template -T hex_to(std::string_view val) { - T result; - std::from_chars(val.data(), val.data() + val.size(), result, 16); - - return result; -} - -constexpr auto& hex_to_u8 = hex_to; -constexpr auto& hex_to_u32 = hex_to; -constexpr auto& hex_to_u64 = hex_to; - std::string to_hexbyte(uint8_t i) { std::string result = "00"; uint8_t i1 = i & 0xF; @@ -193,60 +173,6 @@ uint8_t from_hexchar(char c) { return 0; } -std::string GDBStub::ReadRegister(xe::cpu::ThreadDebugInfo* thread, - uint32_t rid) { - // Send registers as 32-bit, otherwise some debuggers will switch to 64-bit - // mode (eg. IDA will switch to 64-bit and refuse to allow decompiler to work - // with it) - // - // TODO: add altivec/VMX registers here... - // - // ids from gdb/features/rs6000/powerpc-64.c - switch (rid) { - // pc - case 64: { - // If we recently hit a BP then debugger is likely asking for registers - // for it - // - // Lie about the PC and say it's the BP address, since PC might not always - // match - if (cache_.notify_bp_guest_address != -1) { - auto ret = u32_to_padded_hex((uint32_t)cache_.notify_bp_guest_address); - cache_.notify_bp_guest_address = -1; - return ret; - } - // Search for first frame that has guest_pc attached, GDB doesn't care - // about host - for (auto& frame : thread->frames) { - if (frame.guest_pc != 0) { - return u32_to_padded_hex((uint32_t)frame.guest_pc); - } - } - return u32_to_padded_hex(0); - } - case 65: - return u32_to_padded_hex((uint32_t)thread->guest_context.msr); - case 66: - return u32_to_padded_hex((uint32_t)thread->guest_context.cr()); - case 67: - return u32_to_padded_hex((uint32_t)thread->guest_context.lr); - case 68: - return u32_to_padded_hex((uint32_t)thread->guest_context.ctr); - // xer - case 69: - return std::string(8, 'x'); - // fpscr - case 70: - return u32_to_padded_hex(thread->guest_context.fpscr.value); - default: - if (rid > 70) return ""; - return (rid > 31) ? u64_to_padded_hex(*(uint64_t*)&( - thread->guest_context.f[rid - 32])) // fpr - : u32_to_padded_hex( - (uint32_t)thread->guest_context.r[rid]); // gpr - } -} - GDBStub::GDBStub(Emulator* emulator, int listen_port) : emulator_(emulator), processor_(emulator->processor()), @@ -362,7 +288,8 @@ bool GDBStub::ProcessIncomingData(std::unique_ptr& client, std::string& receive_buffer) { char buffer[1024]; size_t received = client->Receive(buffer, sizeof(buffer)); - if (received == -1 || received == 0) { + if (received == uint64_t(-1) || // disconnected + received == 0) { return false; } @@ -533,15 +460,75 @@ void GDBStub::UpdateCache() { } } +std::string GDBStub::ReadRegister(xe::cpu::ThreadDebugInfo* thread, + uint32_t rid) { + // Send registers as 32-bit, otherwise some debuggers will switch to 64-bit + // mode (eg. IDA will switch to 64-bit and refuse to allow decompiler to work + // with it) + // + // TODO: add altivec/VMX registers here... + // + // ids from gdb/features/rs6000/powerpc-64.c + switch (rid) { + // pc + case 64: { + // If we recently hit a BP then debugger is likely asking for registers + // for it + // + // Lie about the PC and say it's the BP addr, since the PC (derived from + // X64 host addr) might not match expected BP addr + if (cache_.notify_bp_guest_address != -1) { + auto ret = string_util::to_hex_string( + (uint32_t)cache_.notify_bp_guest_address); + cache_.notify_bp_guest_address = -1; + return ret; + } + // Search for first frame that has guest_pc attached, GDB doesn't care + // about host + for (auto& frame : thread->frames) { + if (frame.guest_pc != 0) { + return string_util::to_hex_string((uint32_t)frame.guest_pc); + } + } + return string_util::to_hex_string((uint32_t)0); + } + case 65: + return string_util::to_hex_string((uint32_t)thread->guest_context.msr); + case 66: + return string_util::to_hex_string((uint32_t)thread->guest_context.cr()); + case 67: + return string_util::to_hex_string((uint32_t)thread->guest_context.lr); + case 68: + return string_util::to_hex_string((uint32_t)thread->guest_context.ctr); + // xer + case 69: + return std::string(8, 'x'); + case 70: + return string_util::to_hex_string(thread->guest_context.fpscr.value); + } + + if (rid > 70) { + return ""; + } + + // fpr + if (rid > 31) { + return string_util::to_hex_string(thread->guest_context.f[rid - 32]); + } + + // gpr + return string_util::to_hex_string((uint32_t)thread->guest_context.r[rid]); +} + std::string GDBStub::ReadRegister(const std::string& data) { auto* thread = cache_.cur_thread_info(); if (!thread) { return kGdbReplyError; } - uint32_t rid = hex_to_u32(data); + uint32_t rid = string_util::from_string(data, true); std::string result = ReadRegister(thread, rid); if (result.empty()) { - return kGdbReplyError; // TODO: is this error correct? + return kGdbReplyError; } return result; } @@ -592,8 +579,8 @@ std::string GDBStub::ExecutionStep() { std::string GDBStub::ReadMemory(const std::string& data) { auto s = data.find(','); - uint32_t addr = hex_to_u32(data.substr(0, s)); - uint32_t len = hex_to_u32(data.substr(s + 1)); + uint32_t addr = string_util::from_string(data.substr(0, s), true); + uint32_t len = string_util::from_string(data.substr(s + 1), true); std::string result; result.reserve(len * 2); @@ -621,10 +608,6 @@ std::string GDBStub::ReadMemory(const std::string& data) { return result; } -std::string GDBStub::BuildMemoryMap() { return memory_map; } - -std::string GDBStub::BuildTargetXml() { return target_xml; } - std::string GDBStub::BuildThreadList() { std::string buffer; buffer += "l"; @@ -661,10 +644,9 @@ std::string GDBStub::GetThreadStateReply(uint32_t thread_id, uint8_t signal) { pc_value = cache_.notify_bp_guest_address; } - return fmt::format( - "T{:02x}{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER, - u32_to_padded_hex((uint32_t)pc_value), LR_REGISTER, - u32_to_padded_hex((uint32_t)thread->guest_context.lr), thread_id); + return fmt::format("T{:02x}{:02x}:{:08x};{:02x}:{:08x};thread:{:x};", + signal, PC_REGISTER, uint32_t(pc_value), LR_REGISTER, + uint32_t(thread->guest_context.lr), thread_id); } return "S05"; } @@ -925,15 +907,15 @@ std::string GDBStub::HandleGDBCommand(const GDBCommand& command) { } auto sub_cmd = param.substr(0, param.find(':')); if (sub_cmd == "features") { - return BuildTargetXml(); + return std::string(kTargetXml); } else if (sub_cmd == "memory-map") { - return BuildMemoryMap(); + return std::string(kMemoryMapXml); } else if (sub_cmd == "threads") { return BuildThreadList(); } return std::string(kGdbReplyError); }}, - // Supported features (TODO: memory map) + // Supported features {"qSupported", [&](const GDBCommand& cmd) { return "PacketSize=1024;qXfer:features:read+;qXfer:threads:read+;" diff --git a/src/xenia/debug/gdb/gdbstub.h b/src/xenia/debug/gdb/gdbstub.h index cf35c6ea6..c803421dd 100644 --- a/src/xenia/debug/gdb/gdbstub.h +++ b/src/xenia/debug/gdb/gdbstub.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2022 Ben Vanik. All rights reserved. * + * Copyright 2024 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -69,8 +69,6 @@ class GDBStub : public cpu::DebugListener { std::string ExecutionContinue(); std::string ExecutionStep(); std::string ReadMemory(const std::string& data); - std::string BuildMemoryMap(); - std::string BuildTargetXml(); std::string BuildThreadList(); std::string GetThreadStateReply(uint32_t thread_id, uint8_t signal);