diff options
| author | Hedges <hedges@resync.pl> | 2018-07-13 04:22:59 +0100 | 
|---|---|---|
| committer | bunnei <bunneidev@gmail.com> | 2018-07-12 20:22:59 -0700 | 
| commit | e066bc75b9443ffe39adc44a113eca8e899c6e80 (patch) | |
| tree | 78bd4a46cfa69c678ae7518260b0a91cc4227adf | |
| parent | ce23ae3edec94b4e28632c953449241b1a2c321e (diff) | |
More improvements to GDBStub (#653)
* More improvements to GDBStub
- Debugging of threads should work correctly with source and assembly level stepping and modifying registers and memory, meaning threads and callstacks are fully clickable in VS.
- List of modules is available to the client, with assumption that .nro and .nso are backed up by an .elf with symbols, while deconstructed ROMs keep N names.
- Initial support for floating point registers.
* Tidy up as requested in PR feedback
* Tidy up as requested in PR feedback
| -rw-r--r-- | src/common/string_util.cpp | 2 | ||||
| -rw-r--r-- | src/core/arm/unicorn/arm_unicorn.cpp | 4 | ||||
| -rw-r--r-- | src/core/gdbstub/gdbstub.cpp | 194 | ||||
| -rw-r--r-- | src/core/gdbstub/gdbstub.h | 8 | ||||
| -rw-r--r-- | src/core/loader/deconstructed_rom_directory.cpp | 3 | ||||
| -rw-r--r-- | src/core/loader/nca.cpp | 4 | ||||
| -rw-r--r-- | src/core/loader/nro.cpp | 4 | ||||
| -rw-r--r-- | src/core/loader/nso.cpp | 4 | 
8 files changed, 173 insertions, 50 deletions
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index ea9d8f77c..0027888c7 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -134,7 +134,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _      size_t dir_end = full_path.find_last_of("/"  // windows needs the : included for something like just "C:" to be considered a directory  #ifdef _WIN32 -                                            ":" +                                            "\\:"  #endif      );      if (std::string::npos == dir_end) diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index ce6c5616d..f239cf0ea 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -193,11 +193,11 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {          }          Kernel::Thread* thread = Kernel::GetCurrentThread();          SaveContext(thread->context); -        if (last_bkpt_hit) { +        if (last_bkpt_hit || (num_instructions == 1)) {              last_bkpt_hit = false;              GDBStub::Break(); +            GDBStub::SendTrap(thread, 5);          } -        GDBStub::SendTrap(thread, 5);      }  } diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 938852a1a..6062de13c 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -61,10 +61,16 @@ const u32 SIGTERM = 15;  const u32 MSG_WAITALL = 8;  #endif -const u32 X30_REGISTER = 30; +const u32 LR_REGISTER = 30;  const u32 SP_REGISTER = 31;  const u32 PC_REGISTER = 32;  const u32 CPSR_REGISTER = 33; +const u32 UC_ARM64_REG_Q0 = 34; +const u32 FPSCR_REGISTER = 66; + +// TODO/WiP - Used while working on support for FPU +const u32 TODO_DUMMY_REG_997 = 997; +const u32 TODO_DUMMY_REG_998 = 998;  // For sample XML files see the GDB source /gdb/features  // GDB also wants the l character at the start @@ -130,6 +136,8 @@ static const char* target_xml =      </flags>      <reg name="cpsr" bitsize="32" type="cpsr_flags"/>    </feature> +  <feature name="org.gnu.gdb.aarch64.fpu"> +  </feature>  </target>  )"; @@ -144,6 +152,7 @@ static u32 latest_signal = 0;  static bool memory_break = false;  static Kernel::Thread* current_thread = nullptr; +static u32 current_core = 0;  // Binding to a port within the reserved ports range (0-1023) requires root permissions,  // so default to a port outside of that range. @@ -171,13 +180,34 @@ static std::map<u64, Breakpoint> breakpoints_execute;  static std::map<u64, Breakpoint> breakpoints_read;  static std::map<u64, Breakpoint> breakpoints_write; +struct Module { +    std::string name; +    PAddr beg; +    PAddr end; +}; + +static std::vector<Module> modules; + +void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) { +    Module module; +    if (add_elf_ext) { +        Common::SplitPath(name, nullptr, &module.name, nullptr); +        module.name += ".elf"; +    } else { +        module.name = std::move(name); +    } +    module.beg = beg; +    module.end = end; +    modules.push_back(std::move(module)); +} +  static Kernel::Thread* FindThreadById(int id) { -    for (int core = 0; core < Core::NUM_CPU_CORES; core++) { -        auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); -        for (auto thread : threads) { +    for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { +        const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); +        for (auto& thread : threads) {              if (thread->GetThreadId() == id) { -                current_thread = thread.get(); -                return current_thread; +                current_core = core; +                return thread.get();              }          }      } @@ -197,6 +227,8 @@ static u64 RegRead(int id, Kernel::Thread* thread = nullptr) {          return thread->context.pc;      } else if (id == CPSR_REGISTER) {          return thread->context.cpsr; +    } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { +        return thread->context.fpu_registers[id - UC_ARM64_REG_Q0][0];      } else {          return 0;      } @@ -215,6 +247,8 @@ static void RegWrite(int id, u64 val, Kernel::Thread* thread = nullptr) {          thread->context.pc = val;      } else if (id == CPSR_REGISTER) {          thread->context.cpsr = val; +    } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { +        thread->context.fpu_registers[id - (CPSR_REGISTER + 1)][0] = val;      }  } @@ -534,7 +568,11 @@ static void HandleQuery() {          SendReply("T0");      } else if (strncmp(query, "Supported", strlen("Supported")) == 0) {          // PacketSize needs to be large enough for target xml -        SendReply("PacketSize=2000;qXfer:features:read+"); +        std::string buffer = "PacketSize=2000;qXfer:features:read+;qXfer:threads:read+"; +        if (!modules.empty()) { +            buffer += ";qXfer:libraries:read+"; +        } +        SendReply(buffer.c_str());      } else if (strncmp(query, "Xfer:features:read:target.xml:",                         strlen("Xfer:features:read:target.xml:")) == 0) {          SendReply(target_xml); @@ -543,9 +581,9 @@ static void HandleQuery() {          SendReply(buffer.c_str());      } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {          std::string val = "m"; -        for (int core = 0; core < Core::NUM_CPU_CORES; core++) { -            auto threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); -            for (auto thread : threads) { +        for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { +            const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); +            for (const auto& thread : threads) {                  val += fmt::format("{:x}", thread->GetThreadId());                  val += ",";              } @@ -554,6 +592,31 @@ static void HandleQuery() {          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>"; +        for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { +            const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); +            for (const auto& thread : threads) { +                buffer += +                    fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*", +                                thread->GetThreadId(), core, thread->GetThreadId()); +            } +        } +        buffer += "</threads>"; +        SendReply(buffer.c_str()); +    } else if (strncmp(query, "Xfer:libraries:read", strlen("Xfer:libraries:read")) == 0) { +        std::string buffer; +        buffer += "l<?xml version=\"1.0\"?>"; +        buffer += "<library-list>"; +        for (const auto& module : modules) { +            buffer += +                fmt::format(R"*("<library name = "{}"><segment address = "0x{:x}"/></library>)*", +                            module.name, module.beg); +        } +        buffer += "</library-list>"; +        SendReply(buffer.c_str());      } else {          SendReply("");      } @@ -561,33 +624,27 @@ static void HandleQuery() {  /// Handle set thread command from gdb client.  static void HandleSetThread() { -    if (memcmp(command_buffer, "Hc", 2) == 0 || memcmp(command_buffer, "Hg", 2) == 0) { -        int thread_id = -1; -        if (command_buffer[2] != '-') { -            thread_id = static_cast<int>(HexToInt( -                command_buffer + 2, -                command_length - 2 /*strlen(reinterpret_cast<char*>(command_buffer) + 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; -        } +    int thread_id = -1; +    if (command_buffer[2] != '-') { +        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 /*strlen(reinterpret_cast<char*>(command_buffer) + 1)*/)); +    int thread_id = static_cast<int>(HexToInt(command_buffer + 1, command_length - 1));      if (thread_id == 0) {          thread_id = 1;      } @@ -610,16 +667,23 @@ static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {      latest_signal = signal; +    if (!thread) { +        full = false; +    } +      std::string buffer;      if (full) { -        buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};", latest_signal, PC_REGISTER, -                             Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER, -                             Common::swap64(RegRead(SP_REGISTER, thread))); +        buffer = fmt::format("T{:02x}{:02x}:{:016x};{:02x}:{:016x};{:02x}:{:016x}", latest_signal, +                             PC_REGISTER, Common::swap64(RegRead(PC_REGISTER, thread)), SP_REGISTER, +                             Common::swap64(RegRead(SP_REGISTER, thread)), LR_REGISTER, +                             Common::swap64(RegRead(LR_REGISTER, thread)));      } else { -        buffer = fmt::format("T{:02x};", latest_signal); +        buffer = fmt::format("T{:02x}", latest_signal);      } -    buffer += fmt::format("thread:{:x};", thread->GetThreadId()); +    if (thread) { +        buffer += fmt::format(";thread:{:x};", thread->GetThreadId()); +    }      SendReply(buffer.c_str());  } @@ -711,8 +775,12 @@ static void ReadRegister() {          LongToGdbHex(reply, RegRead(id, current_thread));      } else if (id == CPSR_REGISTER) {          IntToGdbHex(reply, (u32)RegRead(id, current_thread)); +    } else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) { +        LongToGdbHex(reply, RegRead(id, current_thread)); +    } else if (id == FPSCR_REGISTER) { +        LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread));      } else { -        return SendReply("E01"); +        LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread));      }      SendReply(reinterpret_cast<char*>(reply)); @@ -729,7 +797,7 @@ static void ReadRegisters() {          LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread));      } -    bufptr += (32 * 16); +    bufptr += 32 * 16;      LongToGdbHex(bufptr, RegRead(PC_REGISTER, current_thread)); @@ -739,6 +807,16 @@ static void ReadRegisters() {      bufptr += 8; +    for (int reg = UC_ARM64_REG_Q0; reg <= UC_ARM64_REG_Q0 + 31; reg++) { +        LongToGdbHex(bufptr + reg * 16, RegRead(reg, current_thread)); +    } + +    bufptr += 32 * 32; + +    LongToGdbHex(bufptr, RegRead(TODO_DUMMY_REG_998, current_thread)); + +    bufptr += 8; +      SendReply(reinterpret_cast<char*>(buffer));  } @@ -759,10 +837,17 @@ static void WriteRegister() {          RegWrite(id, GdbHexToLong(buffer_ptr), current_thread);      } else if (id == CPSR_REGISTER) {          RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); +    } else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) { +        RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); +    } else if (id == FPSCR_REGISTER) { +        RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread);      } else { -        return SendReply("E01"); +        RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread);      } +    // Update Unicorn context skipping scheduler, no running threads at this point +    Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context); +      SendReply("OK");  } @@ -773,18 +858,25 @@ static void WriteRegisters() {      if (command_buffer[0] != 'G')          return SendReply("E01"); -    for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) { +    for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) {          if (reg <= SP_REGISTER) {              RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread);          } else if (reg == PC_REGISTER) {              RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread);          } else if (reg == CPSR_REGISTER) {              RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread); +        } else if (reg >= UC_ARM64_REG_Q0 && reg < FPSCR_REGISTER) { +            RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); +        } else if (reg == FPSCR_REGISTER) { +            RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread);          } else {              UNIMPLEMENTED();          }      } +    // Update Unicorn context skipping scheduler, no running threads at this point +    Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context); +      SendReply("OK");  } @@ -806,6 +898,10 @@ static void ReadMemory() {          SendReply("E01");      } +    if (addr < Memory::PROCESS_IMAGE_VADDR || addr >= Memory::MAP_REGION_VADDR_END) { +        return SendReply("E00"); +    } +      if (!Memory::IsValidVirtualAddress(addr)) {          return SendReply("E00");      } @@ -840,16 +936,18 @@ static void WriteMemory() {  }  void Break(bool is_memory_break) { -    if (!halt_loop) { -        halt_loop = true; -        send_trap = true; -    } +    send_trap = true;      memory_break = is_memory_break;  }  /// Tell the CPU that it should perform a single step.  static void Step() { +    if (command_length > 1) { +        RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread); +        // Update Unicorn context skipping scheduler, no running threads at this point +        Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context); +    }      step_loop = true;      halt_loop = true;      send_trap = true; @@ -1090,6 +1188,8 @@ static void Init(u16 port) {      breakpoints_read.clear();      breakpoints_write.clear(); +    modules.clear(); +      // Start gdb server      LOG_INFO(Debug_GDBStub, "Starting GDB server on port {}...", port); @@ -1192,8 +1292,12 @@ void SetCpuStepFlag(bool 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; -        SendSignal(thread, trap);      }  }  }; // namespace GDBStub diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h index f2418c9e4..a6b50c26c 100644 --- a/src/core/gdbstub/gdbstub.h +++ b/src/core/gdbstub/gdbstub.h @@ -6,6 +6,7 @@  #pragma once +#include <string>  #include "common/common_types.h"  #include "core/hle/kernel/thread.h" @@ -51,6 +52,9 @@ bool IsServerEnabled();  /// Returns true if there is an active socket connection.  bool IsConnected(); +/// Register module. +void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true); +  /**   * Signal to the gdbstub server that it should halt CPU execution.   * @@ -80,10 +84,10 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointTy   */  bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type); -// If set to true, the CPU will halt at the beginning of the next CPU loop. +/// If set to true, the CPU will halt at the beginning of the next CPU loop.  bool GetCpuHaltFlag(); -// If set to true and the CPU is halted, the CPU will step one instruction. +/// If set to true and the CPU is halted, the CPU will step one instruction.  bool GetCpuStepFlag();  /** diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index eb7feb617..5fdb1d289 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -9,6 +9,7 @@  #include "common/logging/log.h"  #include "common/string_util.h"  #include "core/file_sys/romfs_factory.h" +#include "core/gdbstub/gdbstub.h"  #include "core/hle/kernel/process.h"  #include "core/hle/kernel/resource_limit.h"  #include "core/hle/service/filesystem/filesystem.h" @@ -133,6 +134,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(          next_load_addr = AppLoader_NSO::LoadModule(path, load_addr);          if (next_load_addr) {              LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); +            // Register module with GDBStub +            GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);          } else {              next_load_addr = load_addr;          } diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index da064f8e3..0fd930ae2 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -7,10 +7,12 @@  #include "common/common_funcs.h"  #include "common/file_util.h"  #include "common/logging/log.h" +#include "common/string_util.h"  #include "common/swap.h"  #include "core/core.h"  #include "core/file_sys/program_metadata.h"  #include "core/file_sys/romfs_factory.h" +#include "core/gdbstub/gdbstub.h"  #include "core/hle/kernel/process.h"  #include "core/hle/kernel/resource_limit.h"  #include "core/hle/service/filesystem/filesystem.h" @@ -259,6 +261,8 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {          next_load_addr = AppLoader_NSO::LoadModule(module, nca->GetExeFsFile(module), load_addr);          if (next_load_addr) {              LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); +            // Register module with GDBStub +            GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);          } else {              next_load_addr = load_addr;          } diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 3853cfa1a..4d7c69a22 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -9,6 +9,7 @@  #include "common/logging/log.h"  #include "common/swap.h"  #include "core/core.h" +#include "core/gdbstub/gdbstub.h"  #include "core/hle/kernel/process.h"  #include "core/hle/kernel/resource_limit.h"  #include "core/loader/nro.h" @@ -115,6 +116,9 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {      codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));      Core::CurrentProcess()->LoadModule(codeset, load_base); +    // Register module with GDBStub +    GDBStub::RegisterModule(codeset->name, load_base, load_base); +      return true;  } diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 7f84e4b1b..1c629e21f 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -10,6 +10,7 @@  #include "common/logging/log.h"  #include "common/swap.h"  #include "core/core.h" +#include "core/gdbstub/gdbstub.h"  #include "core/hle/kernel/process.h"  #include "core/hle/kernel/resource_limit.h"  #include "core/loader/nso.h" @@ -147,6 +148,9 @@ VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>&      codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));      Core::CurrentProcess()->LoadModule(codeset, load_base); +    // Register module with GDBStub +    GDBStub::RegisterModule(codeset->name, load_base, load_base); +      return load_base + image_size;  }  | 
