From 425a78ec1bff140c17d628768495142170c46368 Mon Sep 17 00:00:00 2001 From: Rozlette Date: Sun, 21 Jan 2018 10:53:09 -0600 Subject: [PATCH] gdbstub: Update registers and sizes for aarch64 This gets gdbstub working at least to the point where clients can communicate with it. What works: - Reading/writing GPRegs - Reading/writing memory - Interrupting the emulated program and continuing What does NOT work: - Breakpoints. Sizes have been updated to u64, but support will need to be added in the interpreter for them to work. - VRegs. Mostly because my gdb was having issues with 128-bit regs for some reason. However, the current u128 representation is a bit awkward to use and should probably be updated first. --- src/core/gdbstub/gdbstub.cpp | 266 ++++++++++++++++++++--------------- 1 file changed, 154 insertions(+), 112 deletions(-) diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 2f3ccb689..2405da0c6 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -57,9 +57,10 @@ const u32 SIGTERM = 15; const u32 MSG_WAITALL = 8; #endif -const u32 R15_REGISTER = 15; -const u32 CPSR_REGISTER = 25; -const u32 FPSCR_REGISTER = 58; +const u32 X30_REGISTER = 30; +const u32 SP_REGISTER = 31; +const u32 PC_REGISTER = 32; +const u32 CPSR_REGISTER = 33; // For sample XML files see the GDB source /gdb/features // GDB also wants the l character at the start @@ -68,48 +69,62 @@ static const char* target_xml = R"(l - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + )"; @@ -143,12 +158,12 @@ WSADATA InitData; struct Breakpoint { bool active; PAddr addr; - u32 len; + u64 len; }; -static std::map breakpoints_execute; -static std::map breakpoints_read; -static std::map breakpoints_write; +static std::map breakpoints_execute; +static std::map breakpoints_read; +static std::map breakpoints_write; /** * Turns hex string character into the equivalent byte. @@ -197,6 +212,21 @@ static u32 HexToInt(const u8* src, size_t len) { return output; } +/** + * Converts input hex string characters into an array of equivalent of u8 bytes. + * + * @param src Pointer to array of output hex string characters. + * @param len Length of src array. + */ +static u64 HexToLong(const u8* src, size_t len) { + u64 output = 0; + while (len-- > 0) { + output = (output << 4) | HexCharToValue(src[0]); + src++; + } + return output; +} + /** * Converts input array of u8 bytes into their equivalent hex string characters. * @@ -234,8 +264,21 @@ static void GdbHexToMem(u8* dest, const u8* src, size_t len) { */ static void IntToGdbHex(u8* dest, u32 v) { for (int i = 0; i < 8; i += 2) { - dest[i + 1] = NibbleToHex(v >> (4 * i)); - dest[i] = NibbleToHex(v >> (4 * (i + 1))); + dest[i + 1] = NibbleToHex(static_cast(v >> (4 * i))); + dest[i] = NibbleToHex(static_cast(v >> (4 * (i + 1)))); + } +} + +/** + * 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(v >> (4 * i))); + dest[i] = NibbleToHex(static_cast(v >> (4 * (i + 1)))); } } @@ -255,6 +298,22 @@ static u32 GdbHexToInt(const u8* src) { return output; } +/** + * 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. static u8 ReadByte() { u8 c; @@ -277,7 +336,7 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) { * * @param type Type of breakpoint list. */ -static std::map& GetBreakpointList(BreakpointType type) { +static std::map& GetBreakpointList(BreakpointType type) { switch (type) { case BreakpointType::Execute: return breakpoints_execute; @@ -297,19 +356,19 @@ static std::map& GetBreakpointList(BreakpointType type) { * @param addr Address of breakpoint. */ static void RemoveBreakpoint(BreakpointType type, PAddr addr) { - std::map& p = GetBreakpointList(type); + std::map& p = GetBreakpointList(type); - auto bp = p.find(static_cast(addr)); + auto bp = p.find(static_cast(addr)); if (bp != p.end()) { LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: %08x bytes at %08x of type %d\n", bp->second.len, bp->second.addr, type); - p.erase(static_cast(addr)); + p.erase(static_cast(addr)); } } BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) { - std::map& p = GetBreakpointList(type); - auto next_breakpoint = p.lower_bound(static_cast(addr)); + std::map& p = GetBreakpointList(type); + auto next_breakpoint = p.lower_bound(static_cast(addr)); BreakpointAddress breakpoint; if (next_breakpoint != p.end()) { @@ -328,11 +387,11 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) { return false; } - std::map& p = GetBreakpointList(type); + std::map& p = GetBreakpointList(type); - auto bp = p.find(static_cast(addr)); + auto bp = p.find(static_cast(addr)); if (bp != p.end()) { - u32 len = bp->second.len; + u64 len = bp->second.len; // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints // no matter if it's a 4-byte or 2-byte instruction. When you execute a @@ -419,7 +478,7 @@ static void HandleQuery() { SendReply("T0"); } else if (strncmp(query, "Supported", strlen("Supported")) == 0) { // PacketSize needs to be large enough for target xml - SendReply("PacketSize=800;qXfer:features:read+"); + SendReply("PacketSize=2000;qXfer:features:read+"); } else if (strncmp(query, "Xfer:features:read:target.xml:", strlen("Xfer:features:read:target.xml:")) == 0) { SendReply(target_xml); @@ -450,10 +509,7 @@ static void SendSignal(u32 signal) { latest_signal = signal; - std::string buffer = - Common::StringFromFormat("T%02x%02x:%08x;%02x:%08x;", latest_signal, 15, - htonl(static_cast(Core::CPU().GetPC())), 13, - htonl(static_cast(Core::CPU().GetReg(13)))); + std::string buffer = Common::StringFromFormat("T%02x", latest_signal); LOG_DEBUG(Debug_GDBStub, "Response: %s", buffer.c_str()); SendReply(buffer.c_str()); } @@ -539,16 +595,12 @@ static void ReadRegister() { id |= HexCharToValue(command_buffer[2]); } - if (id <= R15_REGISTER) { - IntToGdbHex(reply, static_cast(Core::CPU().GetReg(static_cast(id)))); + if (id <= SP_REGISTER) { + LongToGdbHex(reply, Core::CPU().GetReg(static_cast(id))); + } else if (id == PC_REGISTER) { + LongToGdbHex(reply, Core::CPU().GetPC()); } else if (id == CPSR_REGISTER) { IntToGdbHex(reply, Core::CPU().GetCPSR()); - } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { - IntToGdbHex(reply, Core::CPU().GetVFPReg( - id - CPSR_REGISTER - - 1)); // VFP registers should start at 26, so one after CSPR_REGISTER - } else if (id == FPSCR_REGISTER) { - UNIMPLEMENTED(); } else { return SendReply("E01"); } @@ -563,21 +615,19 @@ static void ReadRegisters() { u8* bufptr = buffer; - for (int reg = 0; reg <= R15_REGISTER; reg++) { - IntToGdbHex(bufptr + reg * CHAR_BIT, static_cast(Core::CPU().GetReg(reg))); + for (int reg = 0; reg <= SP_REGISTER; reg++) { + LongToGdbHex(bufptr + reg * 16, Core::CPU().GetReg(reg)); } - bufptr += (16 * CHAR_BIT); + bufptr += (32 * 16); + + LongToGdbHex(bufptr, Core::CPU().GetPC()); + + bufptr += 16; IntToGdbHex(bufptr, Core::CPU().GetCPSR()); - bufptr += CHAR_BIT; - - for (int reg = 0; reg <= 31; reg++) { - IntToGdbHex(bufptr + reg * CHAR_BIT, Core::CPU().GetVFPReg(reg)); - } - - bufptr += (32 * CHAR_BIT); + bufptr += 8; SendReply(reinterpret_cast(buffer)); } @@ -593,14 +643,12 @@ static void WriteRegister() { id |= HexCharToValue(command_buffer[2]); } - if (id <= R15_REGISTER) { - Core::CPU().SetReg(id, GdbHexToInt(buffer_ptr)); + if (id <= SP_REGISTER) { + Core::CPU().SetReg(id, GdbHexToLong(buffer_ptr)); + } else if (id == PC_REGISTER) { + Core::CPU().SetPC(GdbHexToLong(buffer_ptr)); } else if (id == CPSR_REGISTER) { Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr)); - } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { - Core::CPU().SetVFPReg(id - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr)); - } else if (id == FPSCR_REGISTER) { - UNIMPLEMENTED(); } else { return SendReply("E01"); } @@ -615,20 +663,14 @@ static void WriteRegisters() { if (command_buffer[0] != 'G') return SendReply("E01"); - for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) { - if (reg <= R15_REGISTER) { - Core::CPU().SetReg(reg, GdbHexToInt(buffer_ptr + i * CHAR_BIT)); + for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) { + if (reg <= SP_REGISTER) { + Core::CPU().SetReg(reg, GdbHexToLong(buffer_ptr + i * 16)); + } else if (reg == PC_REGISTER) { + Core::CPU().SetPC(GdbHexToLong(buffer_ptr + i * 16)); } else if (reg == CPSR_REGISTER) { - Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr + i * CHAR_BIT)); - } else if (reg == CPSR_REGISTER - 1) { - // Dummy FPA register, ignore - } else if (reg < CPSR_REGISTER) { - // Dummy FPA registers, ignore - i += 2; - } else if (reg > CPSR_REGISTER && reg < FPSCR_REGISTER) { - Core::CPU().SetVFPReg(reg - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr + i * CHAR_BIT)); - i++; // Skip padding - } else if (reg == FPSCR_REGISTER) { + Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr + i * 16)); + } else { UNIMPLEMENTED(); } } @@ -642,13 +684,13 @@ static void ReadMemory() { auto start_offset = command_buffer + 1; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - VAddr addr = HexToInt(start_offset, static_cast(addr_pos - start_offset)); + VAddr addr = HexToLong(start_offset, static_cast(addr_pos - start_offset)); start_offset = addr_pos + 1; - u32 len = - HexToInt(start_offset, static_cast((command_buffer + command_length) - start_offset)); + u64 len = + HexToLong(start_offset, static_cast((command_buffer + command_length) - start_offset)); - LOG_DEBUG(Debug_GDBStub, "gdb: addr: %08x len: %08x\n", addr, len); + LOG_DEBUG(Debug_GDBStub, "gdb: addr: %016llx len: %016llx\n", addr, len); if (len * 2 > sizeof(reply)) { SendReply("E01"); @@ -670,11 +712,11 @@ static void ReadMemory() { static void WriteMemory() { auto start_offset = command_buffer + 1; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - VAddr addr = HexToInt(start_offset, static_cast(addr_pos - start_offset)); + VAddr addr = HexToLong(start_offset, static_cast(addr_pos - start_offset)); start_offset = addr_pos + 1; auto len_pos = std::find(start_offset, command_buffer + command_length, ':'); - u32 len = HexToInt(start_offset, static_cast(len_pos - start_offset)); + u64 len = HexToLong(start_offset, static_cast(len_pos - start_offset)); if (!Memory::IsValidVirtualAddress(addr)) { return SendReply("E00"); @@ -727,8 +769,8 @@ static void Continue() { * @param addr Address of breakpoint. * @param len Length of breakpoint. */ -static bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) { - std::map& p = GetBreakpointList(type); +static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) { + std::map& p = GetBreakpointList(type); Breakpoint breakpoint; breakpoint.active = true; @@ -767,11 +809,11 @@ static void AddBreakpoint() { auto start_offset = command_buffer + 3; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - PAddr addr = HexToInt(start_offset, static_cast(addr_pos - start_offset)); + PAddr addr = HexToLong(start_offset, static_cast(addr_pos - start_offset)); start_offset = addr_pos + 1; - u32 len = - HexToInt(start_offset, static_cast((command_buffer + command_length) - start_offset)); + u64 len = + HexToLong(start_offset, static_cast((command_buffer + command_length) - start_offset)); if (type == BreakpointType::Access) { // Access is made up of Read and Write types, so add both breakpoints @@ -816,7 +858,7 @@ static void RemoveBreakpoint() { auto start_offset = command_buffer + 3; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - PAddr addr = HexToInt(start_offset, static_cast(addr_pos - start_offset)); + PAddr addr = HexToLong(start_offset, static_cast(addr_pos - start_offset)); if (type == BreakpointType::Access) { // Access is made up of Read and Write types, so add both breakpoints