diff options
| -rw-r--r-- | src/citra/citra.cpp | 27 | ||||
| -rw-r--r-- | src/core/gdbstub/gdbstub.cpp | 108 | 
2 files changed, 107 insertions, 28 deletions
| diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index b12369136..3a1fbe3f7 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -36,25 +36,43 @@  static void PrintHelp()  { -    std::cout << "Usage: citra <filename>" << std::endl; +    std::cout << "Usage: citra [options] <filename>" << std::endl; +    std::cout << "--help, -h            Display this information" << std::endl; +    std::cout << "--gdbport, -g number  Enable gdb stub on port number" << std::endl;  }  /// Application entry point  int main(int argc, char **argv) { +    Config config;      int option_index = 0; +    bool use_gdbstub = Settings::values.use_gdbstub; +    u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); +    char *endarg;      std::string boot_filename; +      static struct option long_options[] = {          { "help", no_argument, 0, 'h' }, +        { "gdbport", required_argument, 0, 'g' },          { 0, 0, 0, 0 }      };      while (optind < argc) { -        char arg = getopt_long(argc, argv, ":h", long_options, &option_index); +        char arg = getopt_long(argc, argv, ":hg:", long_options, &option_index);          if (arg != -1) {              switch (arg) {              case 'h':                  PrintHelp();                  return 0; +            case 'g': +                errno = 0; +                gdb_port = strtoul(optarg, &endarg, 0); +                use_gdbstub = true; +                if (endarg == optarg) errno = EINVAL; +                if (errno != 0) { +                    perror("--gdbport"); +                    exit(1); +                } +                break;              }          } else {              boot_filename = argv[optind]; @@ -73,11 +91,10 @@ int main(int argc, char **argv) {          return -1;      } -    Config config;      log_filter.ParseFilterString(Settings::values.log_filter); -    GDBStub::ToggleServer(Settings::values.use_gdbstub); -    GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port)); +    GDBStub::ToggleServer(use_gdbstub); +    GDBStub::SetServerPort(gdb_port);      std::unique_ptr<EmuWindow_SDL2> emu_window = std::make_unique<EmuWindow_SDL2>(); diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 3a2445241..c1a7ec5bf 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -60,6 +60,59 @@ const u32 R15_REGISTER = 15;  const u32 CPSR_REGISTER = 25;  const u32 FPSCR_REGISTER = 58; +// For sample XML files see the GDB source /gdb/features +// GDB also wants the l character at the start +// This XML defines what the registers are for this specific ARM device +static const char* target_xml = +R"(l<?xml version="1.0"?> +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target version="1.0"> +  <feature name="org.gnu.gdb.arm.core"> +    <reg name="r0" bitsize="32"/> +    <reg name="r1" bitsize="32"/> +    <reg name="r2" bitsize="32"/> +    <reg name="r3" bitsize="32"/> +    <reg name="r4" bitsize="32"/> +    <reg name="r5" bitsize="32"/> +    <reg name="r6" bitsize="32"/> +    <reg name="r7" bitsize="32"/> +    <reg name="r8" bitsize="32"/> +    <reg name="r9" bitsize="32"/> +    <reg name="r10" bitsize="32"/> +    <reg name="r11" bitsize="32"/> +    <reg name="r12" bitsize="32"/> +    <reg name="sp" bitsize="32" type="data_ptr"/> +    <reg name="lr" bitsize="32"/> +    <reg name="pc" bitsize="32" type="code_ptr"/> + +    <!-- The CPSR is register 25, rather than register 16, because +         the FPA registers historically were placed between the PC +         and the CPSR in the "g" packet.  --> + +    <reg name="cpsr" bitsize="32" regnum="25"/> +  </feature> +  <feature name="org.gnu.gdb.arm.vfp"> +    <reg name="d0" bitsize="64" type="float"/> +    <reg name="d1" bitsize="64" type="float"/> +    <reg name="d2" bitsize="64" type="float"/> +    <reg name="d3" bitsize="64" type="float"/> +    <reg name="d4" bitsize="64" type="float"/> +    <reg name="d5" bitsize="64" type="float"/> +    <reg name="d6" bitsize="64" type="float"/> +    <reg name="d7" bitsize="64" type="float"/> +    <reg name="d8" bitsize="64" type="float"/> +    <reg name="d9" bitsize="64" type="float"/> +    <reg name="d10" bitsize="64" type="float"/> +    <reg name="d11" bitsize="64" type="float"/> +    <reg name="d12" bitsize="64" type="float"/> +    <reg name="d13" bitsize="64" type="float"/> +    <reg name="d14" bitsize="64" type="float"/> +    <reg name="d15" bitsize="64" type="float"/> +    <reg name="fpscr" bitsize="32" type="int" group="float"/> +  </feature> +</target> +)"; +  namespace GDBStub {  static int gdbserver_socket = -1; @@ -211,7 +264,7 @@ static u8 ReadByte() {  }  /// Calculate the checksum of the current command buffer. -static u8 CalculateChecksum(u8 *buffer, u32 length) { +static u8 CalculateChecksum(u8* buffer, u32 length) {      return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>()));  } @@ -353,8 +406,15 @@ static void SendReply(const char* reply) {  static void HandleQuery() {      LOG_DEBUG(Debug_GDBStub, "gdb: query '%s'\n", command_buffer + 1); -    if (!strcmp(reinterpret_cast<const char*>(command_buffer + 1), "TStatus")) { +    const char* query = reinterpret_cast<const char*>(command_buffer + 1); + +    if (strcmp(query, "TStatus") == 0 ) {          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+"); +    } else if (strncmp(query, "Xfer:features:read:target.xml:", strlen("Xfer:features:read:target.xml:")) == 0) { +        SendReply(target_xml);      } else {          SendReply("");      } @@ -491,29 +551,25 @@ static void ReadRegisters() {      memset(buffer, 0, sizeof(buffer));      u8* bufptr = buffer; -    for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) { -        if (reg <= R15_REGISTER) { -            IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetReg(reg)); -        } else if (reg == CPSR_REGISTER) { -            IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetCPSR()); -        } else if (reg == CPSR_REGISTER - 1) { -            // Dummy FPA register, ignore -            IntToGdbHex(bufptr + i * CHAR_BIT, 0); -        } else if (reg < CPSR_REGISTER) { -            // Dummy FPA registers, ignore -            IntToGdbHex(bufptr + i * CHAR_BIT, 0); -            IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0); -            IntToGdbHex(bufptr + (i + 2) * CHAR_BIT, 0); -            i += 2; -        } else if (reg > CPSR_REGISTER && reg < FPSCR_REGISTER) { -            IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPReg(reg - CPSR_REGISTER - 1)); -            IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0); -            i++; -        } else if (reg == FPSCR_REGISTER) { -            IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR)); -        } + +    for (int reg = 0; reg <= R15_REGISTER; reg++) { +        IntToGdbHex(bufptr + reg * CHAR_BIT, Core::g_app_core->GetReg(reg));      } +    bufptr += (16 * CHAR_BIT); + +    IntToGdbHex(bufptr, Core::g_app_core->GetCPSR()); + +    bufptr += CHAR_BIT; + +    for (int reg = 0; reg <= 31; reg++) { +        IntToGdbHex(bufptr + reg * CHAR_BIT, Core::g_app_core->GetVFPReg(reg)); +    } + +    bufptr += (32 * CHAR_BIT); + +    IntToGdbHex(bufptr, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR)); +      SendReply(reinterpret_cast<char*>(buffer));  } @@ -885,6 +941,12 @@ void Init(u16 port) {          LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket");      } +    // Set socket to SO_REUSEADDR so it can always bind on the same port +    int reuse_enabled = 1; +    if (setsockopt(tmpsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_enabled, sizeof(reuse_enabled)) < 0) { +        LOG_ERROR(Debug_GDBStub, "Failed to set gdb socket option"); +    } +      const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server);      socklen_t server_addrlen = sizeof(saddr_server);      if (bind(tmpsock, server_addr, server_addrlen) < 0) { | 
