GDB Modernization:
- Can be used in either DynCom or Dynarmic mode - Added support for threads - Proper support for FPU registers - Fix for NibbleToHex conversion that used to produce false error codes - Fix for clang-format failing under Windows
This commit is contained in:
		| @@ -321,7 +321,7 @@ if (CLANG_FORMAT) | |||||||
|     set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") |     set(CCOMMENT "Running clang format against all the .h and .cpp files in src/") | ||||||
|     if (WIN32) |     if (WIN32) | ||||||
|         add_custom_target(clang-format |         add_custom_target(clang-format | ||||||
|             COMMAND powershell.exe -Command "${CLANG_FORMAT} -i @(Get-ChildItem -Recurse ${SRCS}/* -Include \'*.h\', \'*.cpp\')" |             COMMAND powershell.exe -Command "Get-ChildItem ${SRCS}/* -Include *.cpp,*.h -Recurse | Foreach {${CLANG_FORMAT} -i $_.fullname}" | ||||||
|             COMMENT ${CCOMMENT}) |             COMMENT ${CCOMMENT}) | ||||||
|     elseif(MINGW) |     elseif(MINGW) | ||||||
|         add_custom_target(clang-format |         add_custom_target(clang-format | ||||||
|   | |||||||
| @@ -22,13 +22,6 @@ | |||||||
|         <string>GDB</string> |         <string>GDB</string> | ||||||
|        </property> |        </property> | ||||||
|        <layout class="QVBoxLayout" name="verticalLayout_3"> |        <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||||
|         <item> |  | ||||||
|          <widget class="QLabel" name="label_1"> |  | ||||||
|           <property name="text"> |  | ||||||
|            <string>The GDB Stub only works correctly when the CPU JIT is off.</string> |  | ||||||
|           </property> |  | ||||||
|          </widget> |  | ||||||
|         </item> |  | ||||||
|         <item> |         <item> | ||||||
|          <layout class="QHBoxLayout" name="horizontalLayout_1"> |          <layout class="QHBoxLayout" name="horizontalLayout_1"> | ||||||
|           <item> |           <item> | ||||||
|   | |||||||
| @@ -87,6 +87,8 @@ static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit, void* user_arg) { | |||||||
|     jit->SetCpsr(state->Cpsr); |     jit->SetCpsr(state->Cpsr); | ||||||
|     jit->ExtRegs() = state->ExtReg; |     jit->ExtRegs() = state->ExtReg; | ||||||
|     jit->SetFpscr(state->VFP[VFP_FPSCR]); |     jit->SetFpscr(state->VFP[VFP_FPSCR]); | ||||||
|  |  | ||||||
|  |     state->ServeBreak(); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool IsReadOnlyMemory(u32 vaddr) { | static bool IsReadOnlyMemory(u32 vaddr) { | ||||||
| @@ -233,6 +235,7 @@ void ARM_Dynarmic::ClearInstructionCache() { | |||||||
|     for (const auto& j : jits) { |     for (const auto& j : jits) { | ||||||
|         j.second->ClearCache(); |         j.second->ClearCache(); | ||||||
|     } |     } | ||||||
|  |     interpreter_state->instruction_cache.clear(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void ARM_Dynarmic::InvalidateCacheRange(u32 start_address, size_t length) { | void ARM_Dynarmic::InvalidateCacheRange(u32 start_address, size_t length) { | ||||||
|   | |||||||
| @@ -147,6 +147,7 @@ void ARM_DynCom::ExecuteInstructions(u64 num_instructions) { | |||||||
|     state->NumInstrsToExecute = num_instructions; |     state->NumInstrsToExecute = num_instructions; | ||||||
|     unsigned ticks_executed = InterpreterMainLoop(state.get()); |     unsigned ticks_executed = InterpreterMainLoop(state.get()); | ||||||
|     CoreTiming::AddTicks(ticks_executed); |     CoreTiming::AddTicks(ticks_executed); | ||||||
|  |     state.get()->ServeBreak(); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::unique_ptr<ARM_Interface::ThreadContext> ARM_DynCom::NewContext() const { | std::unique_ptr<ARM_Interface::ThreadContext> ARM_DynCom::NewContext() const { | ||||||
|   | |||||||
| @@ -956,7 +956,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { | |||||||
|     if (GDBStub::IsServerEnabled()) {                                                              \ |     if (GDBStub::IsServerEnabled()) {                                                              \ | ||||||
|         if (GDBStub::IsMemoryBreak() || (breakpoint_data.type != GDBStub::BreakpointType::None &&  \ |         if (GDBStub::IsMemoryBreak() || (breakpoint_data.type != GDBStub::BreakpointType::None &&  \ | ||||||
|                                          PC == breakpoint_data.address)) {                         \ |                                          PC == breakpoint_data.address)) {                         \ | ||||||
|             GDBStub::Break();                                                                      \ |             cpu->RecordBreak(breakpoint_data);                                                     \ | ||||||
|             goto END;                                                                              \ |             goto END;                                                                              \ | ||||||
|         }                                                                                          \ |         }                                                                                          \ | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -21,6 +21,8 @@ | |||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/arm/skyeye_common/arm_regformat.h" | #include "core/arm/skyeye_common/arm_regformat.h" | ||||||
|  | #include "core/core.h" | ||||||
|  | #include "core/gdbstub/gdbstub.h" | ||||||
|  |  | ||||||
| // Signal levels | // Signal levels | ||||||
| enum { LOW = 0, HIGH = 1, LOWHIGH = 1, HIGHLOW = 2 }; | enum { LOW = 0, HIGH = 1, LOWHIGH = 1, HIGHLOW = 2 }; | ||||||
| @@ -189,6 +191,26 @@ public: | |||||||
|         return TFlag ? 2 : 4; |         return TFlag ? 2 : 4; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     void RecordBreak(GDBStub::BreakpointAddress bkpt) { | ||||||
|  |         last_bkpt = bkpt; | ||||||
|  |         last_bkpt_hit = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void ServeBreak() { | ||||||
|  |         if (GDBStub::IsServerEnabled()) { | ||||||
|  |             if (last_bkpt_hit) { | ||||||
|  |                 Reg[15] = last_bkpt.address; | ||||||
|  |             } | ||||||
|  |             Kernel::Thread* thread = Kernel::GetCurrentThread(); | ||||||
|  |             Core::CPU().SaveContext(thread->context); | ||||||
|  |             if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) { | ||||||
|  |                 last_bkpt_hit = false; | ||||||
|  |                 GDBStub::Break(); | ||||||
|  |                 GDBStub::SendTrap(thread, 5); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     std::array<u32, 16> Reg{}; // The current register file |     std::array<u32, 16> Reg{}; // The current register file | ||||||
|     std::array<u32, 2> Reg_usr{}; |     std::array<u32, 2> Reg_usr{}; | ||||||
|     std::array<u32, 2> Reg_svc{};   // R13_SVC R14_SVC |     std::array<u32, 2> Reg_svc{};   // R13_SVC R14_SVC | ||||||
| @@ -246,4 +268,7 @@ private: | |||||||
|  |  | ||||||
|     u32 exclusive_tag; // The address for which the local monitor is in exclusive access mode |     u32 exclusive_tag; // The address for which the local monitor is in exclusive access mode | ||||||
|     bool exclusive_state; |     bool exclusive_state; | ||||||
|  |  | ||||||
|  |     GDBStub::BreakpointAddress last_bkpt{}; | ||||||
|  |     bool last_bkpt_hit; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -46,8 +46,7 @@ System::ResultStatus System::RunLoop(bool tight_loop) { | |||||||
|         // execute. Otherwise, get out of the loop function. |         // execute. Otherwise, get out of the loop function. | ||||||
|         if (GDBStub::GetCpuHaltFlag()) { |         if (GDBStub::GetCpuHaltFlag()) { | ||||||
|             if (GDBStub::GetCpuStepFlag()) { |             if (GDBStub::GetCpuStepFlag()) { | ||||||
|                 GDBStub::SetCpuStepFlag(false); |                 tight_loop = false; | ||||||
|                 tight_loop = 1; |  | ||||||
|             } else { |             } else { | ||||||
|                 return ResultStatus::Success; |                 return ResultStatus::Success; | ||||||
|             } |             } | ||||||
| @@ -70,6 +69,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (GDBStub::IsServerEnabled()) { | ||||||
|  |         GDBStub::SetCpuStepFlag(false); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     HW::Update(); |     HW::Update(); | ||||||
|     Reschedule(); |     Reschedule(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -35,6 +35,7 @@ | |||||||
| #include "core/arm/arm_interface.h" | #include "core/arm/arm_interface.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
|  | #include "core/hle/kernel/process.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
|  |  | ||||||
| @@ -57,9 +58,12 @@ const u32 SIGTERM = 15; | |||||||
| const u32 MSG_WAITALL = 8; | const u32 MSG_WAITALL = 8; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| const u32 R15_REGISTER = 15; | const u32 SP_REGISTER = 13; | ||||||
|  | const u32 LR_REGISTER = 14; | ||||||
|  | const u32 PC_REGISTER = 15; | ||||||
| const u32 CPSR_REGISTER = 25; | const u32 CPSR_REGISTER = 25; | ||||||
| const u32 FPSCR_REGISTER = 58; | const u32 D0_REGISTER = 26; | ||||||
|  | const u32 FPSCR_REGISTER = 42; | ||||||
|  |  | ||||||
| // For sample XML files see the GDB source /gdb/features | // For sample XML files see the GDB source /gdb/features | ||||||
| // GDB also wants the l character at the start | // GDB also wants the l character at the start | ||||||
| @@ -122,15 +126,17 @@ static u8 command_buffer[GDB_BUFFER_SIZE]; | |||||||
| static u32 command_length; | static u32 command_length; | ||||||
|  |  | ||||||
| static u32 latest_signal = 0; | static u32 latest_signal = 0; | ||||||
| static bool step_break = false; |  | ||||||
| static bool memory_break = false; | static bool memory_break = false; | ||||||
|  |  | ||||||
|  | Kernel::Thread* current_thread = nullptr; | ||||||
|  |  | ||||||
| // Binding to a port within the reserved ports range (0-1023) requires root permissions, | // Binding to a port within the reserved ports range (0-1023) requires root permissions, | ||||||
| // so default to a port outside of that range. | // so default to a port outside of that range. | ||||||
| static u16 gdbstub_port = 24689; | static u16 gdbstub_port = 24689; | ||||||
|  |  | ||||||
| static bool halt_loop = true; | static bool halt_loop = true; | ||||||
| static bool step_loop = false; | static bool step_loop = false; | ||||||
|  | static bool send_trap = false; | ||||||
|  |  | ||||||
| // If set to false, the server will never be started and no | // If set to false, the server will never be started and no | ||||||
| // gdbstub-related functions will be executed. | // gdbstub-related functions will be executed. | ||||||
| @@ -144,12 +150,79 @@ struct Breakpoint { | |||||||
|     bool active; |     bool active; | ||||||
|     PAddr addr; |     PAddr addr; | ||||||
|     u32 len; |     u32 len; | ||||||
|  |     std::array<u8, 4> inst; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static std::map<u32, Breakpoint> breakpoints_execute; | static std::map<u32, Breakpoint> breakpoints_execute; | ||||||
| static std::map<u32, Breakpoint> breakpoints_read; | static std::map<u32, Breakpoint> breakpoints_read; | ||||||
| static std::map<u32, Breakpoint> breakpoints_write; | static std::map<u32, Breakpoint> breakpoints_write; | ||||||
|  |  | ||||||
|  | static Kernel::Thread* FindThreadById(int id) { | ||||||
|  |     const auto& threads = Kernel::GetThreadList(); | ||||||
|  |     for (auto& thread : threads) { | ||||||
|  |         if (thread->GetThreadId() == static_cast<u32>(id)) { | ||||||
|  |             return thread.get(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static u32 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) { | ||||||
|  |     if (!thread) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (id <= PC_REGISTER) { | ||||||
|  |         return thread->context.get()->GetCpuRegister(id); | ||||||
|  |     } else if (id == CPSR_REGISTER) { | ||||||
|  |         return thread->context.get()->GetCpsr(); | ||||||
|  |     } else { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void RegWrite(std::size_t id, u32 val, Kernel::Thread* thread = nullptr) { | ||||||
|  |     if (!thread) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (id <= PC_REGISTER) { | ||||||
|  |         return thread->context.get()->SetCpuRegister(id, val); | ||||||
|  |     } else if (id == CPSR_REGISTER) { | ||||||
|  |         return thread->context.get()->SetCpsr(val); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static u64 FpuRead(std::size_t id, Kernel::Thread* thread = nullptr) { | ||||||
|  |     if (!thread) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (id >= D0_REGISTER && id < FPSCR_REGISTER) { | ||||||
|  |         u64 ret = thread->context.get()->GetFpuRegister(2 * (id - D0_REGISTER)); | ||||||
|  |         ret |= static_cast<u64>(thread->context.get()->GetFpuRegister(2 * (id - D0_REGISTER) + 1)) | ||||||
|  |                << 32; | ||||||
|  |         return ret; | ||||||
|  |     } else if (id == FPSCR_REGISTER) { | ||||||
|  |         return thread->context.get()->GetFpscr(); | ||||||
|  |     } else { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void FpuWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr) { | ||||||
|  |     if (!thread) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (id >= D0_REGISTER && id < FPSCR_REGISTER) { | ||||||
|  |         thread->context.get()->SetFpuRegister(2 * (id - D0_REGISTER), (u32)val); | ||||||
|  |         thread->context.get()->SetFpuRegister(2 * (id - D0_REGISTER) + 1, val >> 32); | ||||||
|  |     } else if (id == FPSCR_REGISTER) { | ||||||
|  |         return thread->context.get()->SetFpscr(static_cast<u32>(val)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Turns hex string character into the equivalent byte. |  * Turns hex string character into the equivalent byte. | ||||||
|  * |  * | ||||||
| @@ -178,7 +251,7 @@ static u8 NibbleToHex(u8 n) { | |||||||
|     if (n < 0xA) { |     if (n < 0xA) { | ||||||
|         return '0' + n; |         return '0' + n; | ||||||
|     } else { |     } else { | ||||||
|         return 'A' + n - 0xA; |         return 'a' + n - 0xA; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -255,6 +328,35 @@ static u32 GdbHexToInt(const u8* src) { | |||||||
|     return output; |     return output; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Convert a u64 into a gdb-formatted hex string. | ||||||
|  |  * | ||||||
|  |  * @param dest Pointer to buffer to store output hex string characters. | ||||||
|  |  * @param v    Value to convert. | ||||||
|  |  */ | ||||||
|  | static void LongToGdbHex(u8* dest, u64 v) { | ||||||
|  |     for (int i = 0; i < 16; i += 2) { | ||||||
|  |         dest[i + 1] = NibbleToHex(static_cast<u8>(v >> (4 * i))); | ||||||
|  |         dest[i] = NibbleToHex(static_cast<u8>(v >> (4 * (i + 1)))); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Convert a gdb-formatted hex string into a u64. | ||||||
|  |  * | ||||||
|  |  * @param src Pointer to hex string. | ||||||
|  |  */ | ||||||
|  | static u64 GdbHexToLong(const u8* src) { | ||||||
|  |     u64 output = 0; | ||||||
|  |  | ||||||
|  |     for (int i = 0; i < 16; i += 2) { | ||||||
|  |         output = (output << 4) | HexCharToValue(src[15 - i - 1]); | ||||||
|  |         output = (output << 4) | HexCharToValue(src[15 - i]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return output; | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Read a byte from the gdb client. | /// Read a byte from the gdb client. | ||||||
| static u8 ReadByte() { | static u8 ReadByte() { | ||||||
|     u8 c; |     u8 c; | ||||||
| @@ -303,6 +405,8 @@ static void RemoveBreakpoint(BreakpointType type, PAddr addr) { | |||||||
|     if (bp != p.end()) { |     if (bp != p.end()) { | ||||||
|         LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:08x} bytes at {:08x} of type {}\n", |         LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:08x} bytes at {:08x} of type {}\n", | ||||||
|                   bp->second.len, bp->second.addr, static_cast<int>(type)); |                   bp->second.len, bp->second.addr, static_cast<int>(type)); | ||||||
|  |         Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size()); | ||||||
|  |         Core::CPU().ClearInstructionCache(); | ||||||
|         p.erase(addr); |         p.erase(addr); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -419,10 +523,32 @@ static void HandleQuery() { | |||||||
|         SendReply("T0"); |         SendReply("T0"); | ||||||
|     } else if (strncmp(query, "Supported", strlen("Supported")) == 0) { |     } else if (strncmp(query, "Supported", strlen("Supported")) == 0) { | ||||||
|         // PacketSize needs to be large enough for target xml |         // PacketSize needs to be large enough for target xml | ||||||
|         SendReply("PacketSize=800;qXfer:features:read+"); |         SendReply("PacketSize=2000;qXfer:features:read+;qXfer:threads:read+"); | ||||||
|     } else if (strncmp(query, "Xfer:features:read:target.xml:", |     } else if (strncmp(query, "Xfer:features:read:target.xml:", | ||||||
|                        strlen("Xfer:features:read:target.xml:")) == 0) { |                        strlen("Xfer:features:read:target.xml:")) == 0) { | ||||||
|         SendReply(target_xml); |         SendReply(target_xml); | ||||||
|  |     } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { | ||||||
|  |         std::string val = "m"; | ||||||
|  |         const auto& threads = Kernel::GetThreadList(); | ||||||
|  |         for (const auto& thread : threads) { | ||||||
|  |             val += fmt::format("{:x}", thread->GetThreadId()); | ||||||
|  |             val += ","; | ||||||
|  |         } | ||||||
|  |         val.pop_back(); | ||||||
|  |         SendReply(val.c_str()); | ||||||
|  |     } else if (strncmp(query, "sThreadInfo", strlen("sThreadInfo")) == 0) { | ||||||
|  |         SendReply("l"); | ||||||
|  |     } else if (strncmp(query, "Xfer:threads:read", strlen("Xfer:threads:read")) == 0) { | ||||||
|  |         std::string buffer; | ||||||
|  |         buffer += "l<?xml version=\"1.0\"?>"; | ||||||
|  |         buffer += "<threads>"; | ||||||
|  |         const auto& threads = Kernel::GetThreadList(); | ||||||
|  |         for (const auto& thread : threads) { | ||||||
|  |             buffer += fmt::format(R"*(<thread id="{:x}" name="Thread {:x}"></thread>)*", | ||||||
|  |                                   thread->GetThreadId(), thread->GetThreadId()); | ||||||
|  |         } | ||||||
|  |         buffer += "</threads>"; | ||||||
|  |         SendReply(buffer.c_str()); | ||||||
|     } else { |     } else { | ||||||
|         SendReply(""); |         SendReply(""); | ||||||
|     } |     } | ||||||
| @@ -430,11 +556,34 @@ static void HandleQuery() { | |||||||
|  |  | ||||||
| /// Handle set thread command from gdb client. | /// Handle set thread command from gdb client. | ||||||
| static void HandleSetThread() { | static void HandleSetThread() { | ||||||
|     if (memcmp(command_buffer, "Hg0", 3) == 0 || memcmp(command_buffer, "Hc-1", 4) == 0 || |     int thread_id = -1; | ||||||
|         memcmp(command_buffer, "Hc0", 4) == 0 || memcmp(command_buffer, "Hc1", 4) == 0) { |     if (command_buffer[2] != '-') { | ||||||
|         return SendReply("OK"); |         thread_id = static_cast<int>(HexToInt(command_buffer + 2, command_length - 2)); | ||||||
|  |     } | ||||||
|  |     if (thread_id >= 1) { | ||||||
|  |         current_thread = FindThreadById(thread_id); | ||||||
|  |     } | ||||||
|  |     if (!current_thread) { | ||||||
|  |         thread_id = 1; | ||||||
|  |         current_thread = FindThreadById(thread_id); | ||||||
|  |     } | ||||||
|  |     if (current_thread) { | ||||||
|  |         SendReply("OK"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     SendReply("E01"); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// Handle thread alive command from gdb client. | ||||||
|  | static void HandleThreadAlive() { | ||||||
|  |     int thread_id = static_cast<int>(HexToInt(command_buffer + 1, command_length - 1)); | ||||||
|  |     if (thread_id == 0) { | ||||||
|  |         thread_id = 1; | ||||||
|  |     } | ||||||
|  |     if (FindThreadById(thread_id)) { | ||||||
|  |         SendReply("OK"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|     SendReply("E01"); |     SendReply("E01"); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -443,16 +592,31 @@ static void HandleSetThread() { | |||||||
|  * |  * | ||||||
|  * @param signal Signal to be sent to client. |  * @param signal Signal to be sent to client. | ||||||
|  */ |  */ | ||||||
| static void SendSignal(u32 signal) { | static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) { | ||||||
|     if (gdbserver_socket == -1) { |     if (gdbserver_socket == -1) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     latest_signal = signal; |     latest_signal = signal; | ||||||
|  |  | ||||||
|     std::string buffer = |     if (!thread) { | ||||||
|         Common::StringFromFormat("T%02x%02x:%08x;%02x:%08x;", latest_signal, 15, |         full = false; | ||||||
|                                  htonl(Core::CPU().GetPC()), 13, htonl(Core::CPU().GetReg(13))); |     } | ||||||
|  |  | ||||||
|  |     std::string buffer; | ||||||
|  |     if (full) { | ||||||
|  |         buffer = Common::StringFromFormat("T%02x%02x:%08x;%02x:%08x;%02x:%08x", latest_signal, | ||||||
|  |                                           PC_REGISTER, htonl(Core::CPU().GetPC()), SP_REGISTER, | ||||||
|  |                                           htonl(Core::CPU().GetReg(SP_REGISTER)), LR_REGISTER, | ||||||
|  |                                           htonl(Core::CPU().GetReg(LR_REGISTER))); | ||||||
|  |     } else { | ||||||
|  |         buffer = Common::StringFromFormat("T%02x", latest_signal); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (thread) { | ||||||
|  |         buffer += Common::StringFromFormat(";thread:%x;", thread->GetThreadId()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     LOG_DEBUG(Debug_GDBStub, "Response: {}", buffer); |     LOG_DEBUG(Debug_GDBStub, "Response: {}", buffer); | ||||||
|     SendReply(buffer.c_str()); |     SendReply(buffer.c_str()); | ||||||
| } | } | ||||||
| @@ -469,7 +633,7 @@ static void ReadCommand() { | |||||||
|     } else if (c == 0x03) { |     } else if (c == 0x03) { | ||||||
|         LOG_INFO(Debug_GDBStub, "gdb: found break command\n"); |         LOG_INFO(Debug_GDBStub, "gdb: found break command\n"); | ||||||
|         halt_loop = true; |         halt_loop = true; | ||||||
|         SendSignal(SIGTRAP); |         SendSignal(current_thread, SIGTRAP); | ||||||
|         return; |         return; | ||||||
|     } else if (c != GDB_STUB_START) { |     } else if (c != GDB_STUB_START) { | ||||||
|         LOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02x}\n", c); |         LOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte {:02x}\n", c); | ||||||
| @@ -539,17 +703,14 @@ static void ReadRegister() { | |||||||
|         id |= HexCharToValue(command_buffer[2]); |         id |= HexCharToValue(command_buffer[2]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (id <= R15_REGISTER) { |     if (id <= PC_REGISTER) { | ||||||
|         IntToGdbHex(reply, Core::CPU().GetReg(id)); |         IntToGdbHex(reply, RegRead(id, current_thread)); | ||||||
|     } else if (id == CPSR_REGISTER) { |     } else if (id == CPSR_REGISTER) { | ||||||
|         IntToGdbHex(reply, Core::CPU().GetCPSR()); |         IntToGdbHex(reply, RegRead(id, current_thread)); | ||||||
|     } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { |     } else if (id >= D0_REGISTER && id < FPSCR_REGISTER) { | ||||||
|         IntToGdbHex(reply, Core::CPU().GetVFPReg( |         LongToGdbHex(reply, FpuRead(id, current_thread)); | ||||||
|                                id - CPSR_REGISTER - |  | ||||||
|                                1)); // VFP registers should start at 26, so one after CSPR_REGISTER |  | ||||||
|     } else if (id == FPSCR_REGISTER) { |     } else if (id == FPSCR_REGISTER) { | ||||||
|         IntToGdbHex(reply, Core::CPU().GetVFPSystemReg(VFP_FPSCR)); // Get FPSCR |         IntToGdbHex(reply, static_cast<u32>(FpuRead(id, current_thread))); | ||||||
|         IntToGdbHex(reply + 8, 0); |  | ||||||
|     } else { |     } else { | ||||||
|         return SendReply("E01"); |         return SendReply("E01"); | ||||||
|     } |     } | ||||||
| @@ -564,23 +725,23 @@ static void ReadRegisters() { | |||||||
|  |  | ||||||
|     u8* bufptr = buffer; |     u8* bufptr = buffer; | ||||||
|  |  | ||||||
|     for (u32 reg = 0; reg <= R15_REGISTER; reg++) { |     for (u32 reg = 0; reg <= PC_REGISTER; reg++) { | ||||||
|         IntToGdbHex(bufptr + reg * CHAR_BIT, Core::CPU().GetReg(reg)); |         IntToGdbHex(bufptr + reg * 8, RegRead(reg, current_thread)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bufptr += (16 * CHAR_BIT); |     bufptr += 16 * 8; | ||||||
|  |  | ||||||
|     IntToGdbHex(bufptr, Core::CPU().GetCPSR()); |     IntToGdbHex(bufptr, RegRead(CPSR_REGISTER, current_thread)); | ||||||
|  |  | ||||||
|     bufptr += CHAR_BIT; |     bufptr += 8; | ||||||
|  |  | ||||||
|     for (u32 reg = 0; reg <= 31; reg++) { |     for (u32 reg = D0_REGISTER; reg < FPSCR_REGISTER; reg++) { | ||||||
|         IntToGdbHex(bufptr + reg * CHAR_BIT, Core::CPU().GetVFPReg(reg)); |         LongToGdbHex(bufptr + reg * 16, FpuRead(reg, current_thread)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     bufptr += (32 * CHAR_BIT); |     bufptr += 16 * 16; | ||||||
|  |  | ||||||
|     IntToGdbHex(bufptr, Core::CPU().GetVFPSystemReg(VFP_FPSCR)); |     IntToGdbHex(bufptr, static_cast<u32>(FpuRead(FPSCR_REGISTER, current_thread))); | ||||||
|  |  | ||||||
|     SendReply(reinterpret_cast<char*>(buffer)); |     SendReply(reinterpret_cast<char*>(buffer)); | ||||||
| } | } | ||||||
| @@ -596,18 +757,20 @@ static void WriteRegister() { | |||||||
|         id |= HexCharToValue(command_buffer[2]); |         id |= HexCharToValue(command_buffer[2]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (id <= R15_REGISTER) { |     if (id <= PC_REGISTER) { | ||||||
|         Core::CPU().SetReg(id, GdbHexToInt(buffer_ptr)); |         RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); | ||||||
|     } else if (id == CPSR_REGISTER) { |     } else if (id == CPSR_REGISTER) { | ||||||
|         Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr)); |         RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); | ||||||
|     } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { |     } else if (id >= D0_REGISTER && id < FPSCR_REGISTER) { | ||||||
|         Core::CPU().SetVFPReg(id - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr)); |         FpuWrite(id, GdbHexToLong(buffer_ptr), current_thread); | ||||||
|     } else if (id == FPSCR_REGISTER) { |     } else if (id == FPSCR_REGISTER) { | ||||||
|         Core::CPU().SetVFPSystemReg(VFP_FPSCR, GdbHexToInt(buffer_ptr)); |         FpuWrite(id, GdbHexToInt(buffer_ptr), current_thread); | ||||||
|     } else { |     } else { | ||||||
|         return SendReply("E01"); |         return SendReply("E01"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     Core::CPU().LoadContext(current_thread->context); | ||||||
|  |  | ||||||
|     SendReply("OK"); |     SendReply("OK"); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -619,23 +782,25 @@ static void WriteRegisters() { | |||||||
|         return SendReply("E01"); |         return SendReply("E01"); | ||||||
|  |  | ||||||
|     for (u32 i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) { |     for (u32 i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) { | ||||||
|         if (reg <= R15_REGISTER) { |         if (reg <= PC_REGISTER) { | ||||||
|             Core::CPU().SetReg(reg, GdbHexToInt(buffer_ptr + i * CHAR_BIT)); |             RegWrite(reg, GdbHexToInt(buffer_ptr + i * 8)); | ||||||
|         } else if (reg == CPSR_REGISTER) { |         } else if (reg == CPSR_REGISTER) { | ||||||
|             Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr + i * CHAR_BIT)); |             RegWrite(reg, GdbHexToInt(buffer_ptr + i * 8)); | ||||||
|         } else if (reg == CPSR_REGISTER - 1) { |         } else if (reg == CPSR_REGISTER - 1) { | ||||||
|             // Dummy FPA register, ignore |             // Dummy FPA register, ignore | ||||||
|         } else if (reg < CPSR_REGISTER) { |         } else if (reg < CPSR_REGISTER) { | ||||||
|             // Dummy FPA registers, ignore |             // Dummy FPA registers, ignore | ||||||
|             i += 2; |             i += 2; | ||||||
|         } else if (reg > CPSR_REGISTER && reg < FPSCR_REGISTER) { |         } else if (reg >= D0_REGISTER && reg < FPSCR_REGISTER) { | ||||||
|             Core::CPU().SetVFPReg(reg - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr + i * CHAR_BIT)); |             FpuWrite(reg, GdbHexToLong(buffer_ptr + i * 16)); | ||||||
|             i++; // Skip padding |             i++; // Skip padding | ||||||
|         } else if (reg == FPSCR_REGISTER) { |         } else if (reg == FPSCR_REGISTER) { | ||||||
|             Core::CPU().SetVFPSystemReg(VFP_FPSCR, GdbHexToInt(buffer_ptr + i * CHAR_BIT)); |             FpuWrite(reg, GdbHexToInt(buffer_ptr + i * 8)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     Core::CPU().LoadContext(current_thread->context); | ||||||
|  |  | ||||||
|     SendReply("OK"); |     SendReply("OK"); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -687,24 +852,26 @@ static void WriteMemory() { | |||||||
|  |  | ||||||
|     GdbHexToMem(data.data(), len_pos + 1, len); |     GdbHexToMem(data.data(), len_pos + 1, len); | ||||||
|     Memory::WriteBlock(addr, data.data(), len); |     Memory::WriteBlock(addr, data.data(), len); | ||||||
|  |     Core::CPU().ClearInstructionCache(); | ||||||
|     SendReply("OK"); |     SendReply("OK"); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Break(bool is_memory_break) { | void Break(bool is_memory_break) { | ||||||
|     if (!halt_loop) { |     send_trap = true; | ||||||
|         halt_loop = true; |  | ||||||
|         SendSignal(SIGTRAP); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     memory_break = is_memory_break; |     memory_break = is_memory_break; | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Tell the CPU that it should perform a single step. | /// Tell the CPU that it should perform a single step. | ||||||
| static void Step() { | static void Step() { | ||||||
|  |     if (command_length > 1) { | ||||||
|  |         RegWrite(PC_REGISTER, GdbHexToInt(command_buffer + 1), current_thread); | ||||||
|  |         Core::CPU().LoadContext(current_thread->context); | ||||||
|  |     } | ||||||
|     step_loop = true; |     step_loop = true; | ||||||
|     halt_loop = true; |     halt_loop = true; | ||||||
|     step_break = true; |     send_trap = true; | ||||||
|     SendSignal(SIGTRAP); |     Core::CPU().ClearInstructionCache(); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool IsMemoryBreak() { | bool IsMemoryBreak() { | ||||||
| @@ -718,9 +885,9 @@ bool IsMemoryBreak() { | |||||||
| /// Tell the CPU to continue executing. | /// Tell the CPU to continue executing. | ||||||
| static void Continue() { | static void Continue() { | ||||||
|     memory_break = false; |     memory_break = false; | ||||||
|     step_break = false; |  | ||||||
|     step_loop = false; |     step_loop = false; | ||||||
|     halt_loop = false; |     halt_loop = false; | ||||||
|  |     Core::CPU().ClearInstructionCache(); | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -737,6 +904,10 @@ static bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) { | |||||||
|     breakpoint.active = true; |     breakpoint.active = true; | ||||||
|     breakpoint.addr = addr; |     breakpoint.addr = addr; | ||||||
|     breakpoint.len = len; |     breakpoint.len = len; | ||||||
|  |     Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); | ||||||
|  |     static constexpr std::array<u8, 4> btrap{0x70, 0x00, 0x20, 0xe1}; | ||||||
|  |     Memory::WriteBlock(addr, btrap.data(), btrap.size()); | ||||||
|  |     Core::CPU().ClearInstructionCache(); | ||||||
|     p.insert({addr, breakpoint}); |     p.insert({addr, breakpoint}); | ||||||
|  |  | ||||||
|     LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:08x} bytes at {:08x}\n", |     LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:08x} bytes at {:08x}\n", | ||||||
| @@ -857,7 +1028,7 @@ void HandlePacket() { | |||||||
|         HandleSetThread(); |         HandleSetThread(); | ||||||
|         break; |         break; | ||||||
|     case '?': |     case '?': | ||||||
|         SendSignal(latest_signal); |         SendSignal(current_thread, latest_signal); | ||||||
|         break; |         break; | ||||||
|     case 'k': |     case 'k': | ||||||
|         Shutdown(); |         Shutdown(); | ||||||
| @@ -894,6 +1065,9 @@ void HandlePacket() { | |||||||
|     case 'Z': |     case 'Z': | ||||||
|         AddBreakpoint(); |         AddBreakpoint(); | ||||||
|         break; |         break; | ||||||
|  |     case 'T': | ||||||
|  |         HandleThreadAlive(); | ||||||
|  |         break; | ||||||
|     default: |     default: | ||||||
|         SendReply(""); |         SendReply(""); | ||||||
|         break; |         break; | ||||||
| @@ -1038,4 +1212,15 @@ bool GetCpuStepFlag() { | |||||||
| void SetCpuStepFlag(bool is_step) { | void SetCpuStepFlag(bool is_step) { | ||||||
|     step_loop = is_step; |     step_loop = is_step; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void SendTrap(Kernel::Thread* thread, int trap) { | ||||||
|  |     if (send_trap) { | ||||||
|  |         if (!halt_loop || current_thread == thread) { | ||||||
|  |             current_thread = thread; | ||||||
|  |             SendSignal(thread, trap); | ||||||
|  |         } | ||||||
|  |         halt_loop = true; | ||||||
|  |         send_trap = false; | ||||||
|  |     } | ||||||
|  | } | ||||||
| }; // namespace GDBStub | }; // namespace GDBStub | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "core/hle/kernel/thread.h" | ||||||
|  |  | ||||||
| namespace GDBStub { | namespace GDBStub { | ||||||
|  |  | ||||||
| @@ -91,4 +92,12 @@ bool GetCpuStepFlag(); | |||||||
|  * @param is_step |  * @param is_step | ||||||
|  */ |  */ | ||||||
| void SetCpuStepFlag(bool is_step); | void SetCpuStepFlag(bool is_step); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Send trap signal from thread back to the gdbstub server. | ||||||
|  |  * | ||||||
|  |  * @param thread Sending thread. | ||||||
|  |  * @param trap Trap no. | ||||||
|  |  */ | ||||||
|  | void SendTrap(Kernel::Thread* thread, int trap); | ||||||
| } // namespace GDBStub | } // namespace GDBStub | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jarek Syrylak
					Jarek Syrylak