gdb_server: Don't modify "buf" argument in decode_xfer_read()
[openocd.git] / src / server / gdb_server.c
index a9681c79716ea36f6c91d80b8ce0841e6f4c1ab7..0b28287d80a7c8ee22f69a7f18d72ad3e69f0162 100644 (file)
  * found in most modern embedded processors.
  */
 
+struct target_desc_format {
+       char *tdesc;
+       uint32_t tdesc_length;
+};
+
 /* private connection data for GDB */
 struct gdb_connection {
        char buffer[GDB_BUFFER_SIZE];
@@ -85,6 +90,8 @@ struct gdb_connection {
         * normally we reply with a S reply via gdb_last_signal_packet.
         * as a side note this behaviour only effects gdb > 6.8 */
        bool attached;
+       /* temporarily used for target description support */
+       struct target_desc_format target_desc;
 };
 
 #if 0
@@ -97,8 +104,8 @@ static int gdb_breakpoint_override;
 static enum breakpoint_type gdb_breakpoint_override_type;
 
 static int gdb_error(struct connection *connection, int retval);
-static const char *gdb_port;
-static const char *gdb_port_next;
+static char *gdb_port;
+static char *gdb_port_next;
 
 static void gdb_log_callback(void *priv, const char *file, unsigned line,
                const char *function, const char *string);
@@ -122,8 +129,8 @@ static int gdb_report_data_abort;
 
 /* set if we are sending target descriptions to gdb
  * via qXfer:features:read packet */
-/* disabled by default */
-static int gdb_use_target_description;
+/* enabled by default */
+static int gdb_use_target_description = 1;
 
 /* current processing free-run type, used by file-I/O */
 static char gdb_running_type;
@@ -610,11 +617,21 @@ static int gdb_get_packet_inner(struct connection *connection,
                                case '$':
                                        break;
                                case '+':
-                                       /* gdb sends a dummy ack '+' at every remote connect - see
-                                        * remote_start_remote (remote.c)
-                                        * in case anyone tries to debug why they receive this
-                                        * warning every time */
-                                       LOG_WARNING("acknowledgment received, but no packet pending");
+                                       /* According to the GDB documentation
+                                        * (https://sourceware.org/gdb/onlinedocs/gdb/Packet-Acknowledgment.html):
+                                        * "gdb sends a final `+` acknowledgment of the stub's `OK`
+                                        * response, which can be safely ignored by the stub."
+                                        * However OpenOCD server already is in noack mode at this
+                                        * point and instead of ignoring this it was emitting a
+                                        * warning. This code makes server ignore the first ACK
+                                        * that will be received after going into noack mode,
+                                        * warning only about subsequent ACK's. */
+                                       if (gdb_con->noack_mode > 1) {
+                                               LOG_WARNING("acknowledgment received, but no packet pending");
+                                       } else {
+                                               LOG_DEBUG("Received first acknowledgment after entering noack mode. Ignoring it.");
+                                               gdb_con->noack_mode = 2;
+                                       }
                                        break;
                                case '-':
                                        LOG_WARNING("negative acknowledgment, but no packet pending");
@@ -721,15 +738,15 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
                                switch (hit_wp_type) {
                                        case WPT_WRITE:
                                                snprintf(stop_reason, sizeof(stop_reason),
-                                                               "watch:%08x;", hit_wp_address);
+                                                               "watch:%08" PRIx32 ";", hit_wp_address);
                                                break;
                                        case WPT_READ:
                                                snprintf(stop_reason, sizeof(stop_reason),
-                                                               "rwatch:%08x;", hit_wp_address);
+                                                               "rwatch:%08" PRIx32 ";", hit_wp_address);
                                                break;
                                        case WPT_ACCESS:
                                                snprintf(stop_reason, sizeof(stop_reason),
-                                                               "awatch:%08x;", hit_wp_address);
+                                                               "awatch:%08" PRIx32 ";", hit_wp_address);
                                                break;
                                        default:
                                                break;
@@ -754,64 +771,64 @@ static void gdb_fileio_reply(struct target *target, struct connection *connectio
        bool program_exited = false;
 
        if (strcmp(target->fileio_info->identifier, "open") == 0)
-               sprintf(fileio_command, "F%s,%x/%x,%x,%x", target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2,
                                target->fileio_info->param_3,
                                target->fileio_info->param_4);
        else if (strcmp(target->fileio_info->identifier, "close") == 0)
-               sprintf(fileio_command, "F%s,%x", target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx32, target->fileio_info->identifier,
                                target->fileio_info->param_1);
        else if (strcmp(target->fileio_info->identifier, "read") == 0)
-               sprintf(fileio_command, "F%s,%x,%x,%x", target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2,
                                target->fileio_info->param_3);
        else if (strcmp(target->fileio_info->identifier, "write") == 0)
-               sprintf(fileio_command, "F%s,%x,%x,%x", target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2,
                                target->fileio_info->param_3);
        else if (strcmp(target->fileio_info->identifier, "lseek") == 0)
-               sprintf(fileio_command, "F%s,%x,%x,%x", target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2,
                                target->fileio_info->param_3);
        else if (strcmp(target->fileio_info->identifier, "rename") == 0)
-               sprintf(fileio_command, "F%s,%x/%x,%x/%x", target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2,
                                target->fileio_info->param_3,
                                target->fileio_info->param_4);
        else if (strcmp(target->fileio_info->identifier, "unlink") == 0)
-               sprintf(fileio_command, "F%s,%x/%x", target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2);
        else if (strcmp(target->fileio_info->identifier, "stat") == 0)
-               sprintf(fileio_command, "F%s,%x/%x,%x", target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2,
                                target->fileio_info->param_3);
        else if (strcmp(target->fileio_info->identifier, "fstat") == 0)
-               sprintf(fileio_command, "F%s,%x,%x", target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2);
        else if (strcmp(target->fileio_info->identifier, "gettimeofday") == 0)
-               sprintf(fileio_command, "F%s,%x,%x", target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx32 ",%" PRIx32, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2);
        else if (strcmp(target->fileio_info->identifier, "isatty") == 0)
-               sprintf(fileio_command, "F%s,%x", target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx32, target->fileio_info->identifier,
                                target->fileio_info->param_1);
        else if (strcmp(target->fileio_info->identifier, "system") == 0)
-               sprintf(fileio_command, "F%s,%x/%x", target->fileio_info->identifier,
+               sprintf(fileio_command, "F%s,%" PRIx32 "/%" PRIx32, target->fileio_info->identifier,
                                target->fileio_info->param_1,
                                target->fileio_info->param_2);
        else if (strcmp(target->fileio_info->identifier, "exit") == 0) {
                /* If target hits exit syscall, report to GDB the program is terminated.
                 * In addition, let target run its own exit syscall handler. */
                program_exited = true;
-               sprintf(fileio_command, "W%02x", target->fileio_info->param_1);
+               sprintf(fileio_command, "W%02" PRIx32, target->fileio_info->param_1);
        } else {
                LOG_DEBUG("Unknown syscall: %s", target->fileio_info->identifier);
 
@@ -909,6 +926,8 @@ static int gdb_new_connection(struct connection *connection)
        gdb_connection->sync = false;
        gdb_connection->mem_write_error = false;
        gdb_connection->attached = true;
+       gdb_connection->target_desc.tdesc = NULL;
+       gdb_connection->target_desc.tdesc_length = 0;
 
        /* send ACK to GDB for debug request */
        gdb_write(connection, "+", 1);
@@ -948,6 +967,9 @@ static int gdb_new_connection(struct connection *connection)
                int i;
                for (i = 0; i < flash_get_bank_count(); i++) {
                        struct flash_bank *p;
+                       p = get_flash_bank_by_num_noprobe(i);
+                       if (p->target != gdb_service->target)
+                               continue;
                        retval = get_flash_bank_by_num(i, &p);
                        if (retval != ERROR_OK) {
                                LOG_ERROR("Connect failed. Consider setting up a gdb-attach event for the target " \
@@ -1340,6 +1362,12 @@ static int gdb_read_memory_packet(struct connection *connection,
 
        len = strtoul(separator + 1, NULL, 16);
 
+       if (!len) {
+               LOG_WARNING("invalid read memory packet received (len == 0)");
+               gdb_put_packet(connection, NULL, 0);
+               return ERROR_OK;
+       }
+
        buffer = malloc(len);
 
        LOG_DEBUG("addr: 0x%8.8" PRIx32 ", len: 0x%8.8" PRIx32 "", addr, len);
@@ -1494,10 +1522,9 @@ static int gdb_step_continue_packet(struct connection *connection,
 
        LOG_DEBUG("-");
 
-       if (packet_size > 1) {
-               packet[packet_size] = 0;
+       if (packet_size > 1)
                address = strtoul(packet + 1, NULL, 16);
-       else
+       else
                current = 1;
 
        gdb_running_type = packet[0];
@@ -1642,29 +1669,41 @@ static void xml_printf(int *retval, char **xml, int *pos, int *size,
        }
 }
 
-static int decode_xfer_read(char *buf, char **annex, int *ofs, unsigned int *len)
+static int decode_xfer_read(char *_buf, char **annex, int *ofs, unsigned int *len)
 {
+       int ret = 0;
+       char *buf = strdup(_buf);
+       char *_annex;
        char *separator;
 
        /* Extract and NUL-terminate the annex. */
-       *annex = buf;
+       _annex = buf;
        while (*buf && *buf != ':')
                buf++;
-       if (*buf == '\0')
-               return -1;
+       if (*buf == '\0') {
+               ret = -1;
+               goto out;
+       }
        *buf++ = 0;
 
+       /* Return annex as copy because "buf" will be freed in this function */
+       *annex = strdup(_annex);
+
        /* After the read marker and annex, qXfer looks like a
         * traditional 'm' packet. */
 
        *ofs = strtoul(buf, &separator, 16);
 
-       if (*separator != ',')
-               return -1;
+       if (*separator != ',') {
+               ret = -1;
+               goto out;
+       }
 
        *len = strtoul(separator + 1, NULL, 16);
 
-       return 0;
+out:
+       free(buf);
+       return ret;
 }
 
 static int compare_bank(const void *a, const void *b)
@@ -1723,14 +1762,16 @@ static int gdb_memory_map(struct connection *connection,
        banks = malloc(sizeof(struct flash_bank *)*flash_get_bank_count());
 
        for (i = 0; i < flash_get_bank_count(); i++) {
+               p = get_flash_bank_by_num_noprobe(i);
+               if (p->target != target)
+                       continue;
                retval = get_flash_bank_by_num(i, &p);
                if (retval != ERROR_OK) {
                        free(banks);
                        gdb_error(connection, retval);
                        return retval;
                }
-               if (p->target == target)
-                       banks[target_flash_banks++] = p;
+               banks[target_flash_banks++] = p;
        }
 
        qsort(banks, target_flash_banks, sizeof(struct flash_bank *),
@@ -1967,7 +2008,7 @@ static int gdb_generate_reg_type_description(struct target *target,
 /* Get a list of available target registers features. feature_list must
  * be freed by caller.
  */
-int get_reg_features_list(struct target *target, char **feature_list[], int *feature_list_size,
+static int get_reg_features_list(struct target *target, char **feature_list[], int *feature_list_size,
                struct reg **reg_list, int reg_list_size)
 {
        int tbl_sz = 0;
@@ -1979,7 +2020,8 @@ int get_reg_features_list(struct target *target, char **feature_list[], int *fea
                if (reg_list[i]->exist == false)
                        continue;
 
-               if ((reg_list[i]->feature->name != NULL)
+               if (reg_list[i]->feature != NULL
+                       && reg_list[i]->feature->name != NULL
                        && (strcmp(reg_list[i]->feature->name, ""))) {
                        /* We found a feature, check if the feature is already in the
                         * table. If not, allocate a new entry for the table and
@@ -2005,19 +2047,15 @@ int get_reg_features_list(struct target *target, char **feature_list[], int *fea
        return ERROR_OK;
 }
 
-static int gdb_generate_target_description(struct target *target, char **tdesc)
+static int gdb_generate_target_description(struct target *target, char **tdesc_out)
 {
        int retval = ERROR_OK;
        struct reg **reg_list;
        int reg_list_size;
+       char *tdesc = NULL;
        int pos = 0;
        int size = 0;
 
-       xml_printf(&retval, tdesc, &pos, &size,
-                       "<?xml version=\"1.0\"?>\n"
-                       "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">\n"
-                       "<target version=\"1.0\">\n");
-
        retval = target_get_gdb_reg_list(target, &reg_list,
                        &reg_list_size, REG_CLASS_ALL);
 
@@ -2026,25 +2064,33 @@ static int gdb_generate_target_description(struct target *target, char **tdesc)
                return ERROR_FAIL;
        }
 
-       if (reg_list_size <= 0)
+       if (reg_list_size <= 0) {
+               free(reg_list);
                return ERROR_FAIL;
+       }
 
        char **features = NULL;
        /* Get a list of available target registers features */
        retval = get_reg_features_list(target, &features, NULL, reg_list, reg_list_size);
        if (retval != ERROR_OK) {
                LOG_ERROR("Can't get the registers feature list");
+               free(reg_list);
                return ERROR_FAIL;
        }
 
        /* If we found some features associated with registers, create sections */
        int current_feature = 0;
 
+       xml_printf(&retval, &tdesc, &pos, &size,
+                       "<?xml version=\"1.0\"?>\n"
+                       "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">\n"
+                       "<target version=\"1.0\">\n");
+
        /* generate target description according to register list */
        if (features != NULL) {
                while (features[current_feature]) {
 
-                       xml_printf(&retval, tdesc, &pos, &size,
+                       xml_printf(&retval, &tdesc, &pos, &size,
                                        "<feature name=\"%s\">\n",
                                        features[current_feature]);
 
@@ -2061,7 +2107,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc)
                                if (reg_list[i]->reg_data_type != NULL) {
                                        if (reg_list[i]->reg_data_type->type == REG_TYPE_ARCH_DEFINED) {
                                                /* generate <type... first, if there are architecture-defined types. */
-                                               gdb_generate_reg_type_description(target, tdesc, &pos, &size,
+                                               gdb_generate_reg_type_description(target, &tdesc, &pos, &size,
                                                                reg_list[i]->reg_data_type);
 
                                                type_str = reg_list[i]->reg_data_type->id;
@@ -2075,57 +2121,69 @@ static int gdb_generate_target_description(struct target *target, char **tdesc)
                                        type_str = "int";
                                }
 
-                               xml_printf(&retval, tdesc, &pos, &size,
+                               xml_printf(&retval, &tdesc, &pos, &size,
                                                "<reg name=\"%s\"", reg_list[i]->name);
-                               xml_printf(&retval, tdesc, &pos, &size,
+                               xml_printf(&retval, &tdesc, &pos, &size,
                                                " bitsize=\"%d\"", reg_list[i]->size);
-                               xml_printf(&retval, tdesc, &pos, &size,
+                               xml_printf(&retval, &tdesc, &pos, &size,
                                                " regnum=\"%d\"", reg_list[i]->number);
                                if (reg_list[i]->caller_save)
-                                       xml_printf(&retval, tdesc, &pos, &size,
+                                       xml_printf(&retval, &tdesc, &pos, &size,
                                                        " save-restore=\"yes\"");
                                else
-                                       xml_printf(&retval, tdesc, &pos, &size,
+                                       xml_printf(&retval, &tdesc, &pos, &size,
                                                        " save-restore=\"no\"");
 
-                               xml_printf(&retval, tdesc, &pos, &size,
+                               xml_printf(&retval, &tdesc, &pos, &size,
                                                " type=\"%s\"", type_str);
 
                                if (reg_list[i]->group != NULL)
-                                       xml_printf(&retval, tdesc, &pos, &size,
+                                       xml_printf(&retval, &tdesc, &pos, &size,
                                                        " group=\"%s\"", reg_list[i]->group);
 
-                               xml_printf(&retval, tdesc, &pos, &size,
+                               xml_printf(&retval, &tdesc, &pos, &size,
                                                "/>\n");
                        }
 
-                       xml_printf(&retval, tdesc, &pos, &size,
+                       xml_printf(&retval, &tdesc, &pos, &size,
                                        "</feature>\n");
 
                        current_feature++;
                }
        }
 
-       xml_printf(&retval, tdesc, &pos, &size,
+       xml_printf(&retval, &tdesc, &pos, &size,
                        "</target>\n");
 
-       if (reg_list != NULL)
-               free(reg_list);
+       free(reg_list);
+       free(features);
 
-       if (features != NULL)
-               free(features);
+       if (retval == ERROR_OK)
+               *tdesc_out = tdesc;
+       else
+               free(tdesc);
 
-       return ERROR_OK;
+       return retval;
 }
 
-static int gdb_get_target_description_chunk(struct target *target, char **chunk,
-               int32_t offset, uint32_t length)
+static int gdb_get_target_description_chunk(struct target *target, struct target_desc_format *target_desc,
+               char **chunk, int32_t offset, uint32_t length)
 {
-       static char *tdesc;
-       static uint32_t tdesc_length;
+       if (target_desc == NULL) {
+               LOG_ERROR("Unable to Generate Target Description");
+               return ERROR_FAIL;
+       }
+
+       char *tdesc = target_desc->tdesc;
+       uint32_t tdesc_length = target_desc->tdesc_length;
 
        if (tdesc == NULL) {
-               gdb_generate_target_description(target, &tdesc);
+               int retval = gdb_generate_target_description(target, &tdesc);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Unable to Generate Target Description");
+                       return ERROR_FAIL;
+               }
+
                tdesc_length = strlen(tdesc);
        }
 
@@ -2137,6 +2195,11 @@ static int gdb_get_target_description_chunk(struct target *target, char **chunk,
                transfer_type = 'l';
 
        *chunk = malloc(length + 2);
+       if (*chunk == NULL) {
+               LOG_ERROR("Unable to allocate memory");
+               return ERROR_FAIL;
+       }
+
        (*chunk)[0] = transfer_type;
        if (transfer_type == 'm') {
                strncpy((*chunk) + 1, tdesc + offset, length);
@@ -2151,9 +2214,56 @@ static int gdb_get_target_description_chunk(struct target *target, char **chunk,
                tdesc_length = 0;
        }
 
+       target_desc->tdesc = tdesc;
+       target_desc->tdesc_length = tdesc_length;
+
        return ERROR_OK;
 }
 
+static int gdb_target_description_supported(struct target *target, int *supported)
+{
+       int retval = ERROR_OK;
+       struct reg **reg_list = NULL;
+       int reg_list_size = 0;
+       int feature_list_size = 0;
+       char **features = NULL;
+
+       retval = target_get_gdb_reg_list(target, &reg_list,
+                       &reg_list_size, REG_CLASS_ALL);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("get register list failed");
+               goto error;
+       }
+
+       if (reg_list_size <= 0) {
+               retval = ERROR_FAIL;
+               goto error;
+       }
+
+       /* Get a list of available target registers features */
+       retval = get_reg_features_list(target, &features, &feature_list_size, reg_list, reg_list_size);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Can't get the registers feature list");
+               goto error;
+       }
+
+       if (supported) {
+               if (feature_list_size)
+                       *supported = 1;
+               else
+                       *supported = 0;
+       }
+
+error:
+       if (reg_list != NULL)
+               free(reg_list);
+
+       if (features != NULL)
+               free(features);
+
+       return retval;
+}
+
 static int gdb_query_packet(struct connection *connection,
                char *packet, int packet_size)
 {
@@ -2218,11 +2328,26 @@ static int gdb_query_packet(struct connection *connection,
                }
        } else if (strncmp(packet, "qSupported", 10) == 0) {
                /* we currently support packet size and qXfer:memory-map:read (if enabled)
-                * disable qXfer:features:read for the moment */
+                * qXfer:features:read is supported for some targets */
                int retval = ERROR_OK;
                char *buffer = NULL;
                int pos = 0;
                int size = 0;
+               int gdb_target_desc_supported = 0;
+
+               /* we need to test that the target supports target descriptions */
+               retval = gdb_target_description_supported(target, &gdb_target_desc_supported);
+               if (retval != ERROR_OK) {
+                       LOG_INFO("Failed detecting Target Description Support, disabling");
+                       gdb_target_desc_supported = 0;
+               }
+
+               /* support may be disabled globally */
+               if (gdb_use_target_description == 0) {
+                       if (gdb_target_desc_supported)
+                               LOG_WARNING("Target Descriptions Supported, but disabled");
+                       gdb_target_desc_supported = 0;
+               }
 
                xml_printf(&retval,
                        &buffer,
@@ -2231,7 +2356,7 @@ static int gdb_query_packet(struct connection *connection,
                        "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;QStartNoAckMode+",
                        (GDB_BUFFER_SIZE - 1),
                        ((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-',
-                       (gdb_use_target_description == 1) ? '+' : '-');
+                       (gdb_target_desc_supported == 1) ? '+' : '-');
 
                if (retval != ERROR_OK) {
                        gdb_send_error(connection, 01);
@@ -2251,7 +2376,7 @@ static int gdb_query_packet(struct connection *connection,
 
                int offset;
                unsigned int length;
-               char *annex;
+               char *annex = NULL;
 
                /* skip command character */
                packet += 20;
@@ -2260,13 +2385,15 @@ static int gdb_query_packet(struct connection *connection,
                        gdb_send_error(connection, 01);
                        return ERROR_OK;
                }
+               free(annex);
 
                /* Target should prepare correct target description for annex.
                 * The first character of returned xml is 'm' or 'l'. 'm' for
                 * there are *more* chunks to transfer. 'l' for it is the *last*
                 * chunk of target description.
                 */
-               retval = gdb_get_target_description_chunk(target, &xml, offset, length);
+               retval = gdb_get_target_description_chunk(target, &gdb_connection->target_desc,
+                               &xml, offset, length);
                if (retval != ERROR_OK) {
                        gdb_error(connection, retval);
                        return retval;
@@ -2807,7 +2934,7 @@ static int gdb_target_add_one(struct target *target)
                portnumber = strtol(gdb_port_next, &end, 0);
                if (!*end) {
                        if (parse_long(gdb_port_next, &portnumber) == ERROR_OK) {
-                               free((void *)gdb_port_next);
+                               free(gdb_port_next);
                                gdb_port_next = alloc_printf("%d", portnumber+1);
                        }
                }
@@ -2854,7 +2981,7 @@ COMMAND_HANDLER(handle_gdb_port_command)
 {
        int retval = CALL_COMMAND_HANDLER(server_pipe_command, &gdb_port);
        if (ERROR_OK == retval) {
-               free((void *)gdb_port_next);
+               free(gdb_port_next);
                gdb_port_next = strdup(gdb_port);
        }
        return retval;
@@ -2922,41 +3049,46 @@ COMMAND_HANDLER(handle_gdb_target_description_command)
 
 COMMAND_HANDLER(handle_gdb_save_tdesc_command)
 {
-       static char *tdesc;
-       static uint32_t tdesc_length;
+       char *tdesc;
+       uint32_t tdesc_length;
        struct target *target = get_current_target(CMD_CTX);
-       char *tdesc_filename;
 
-       if (tdesc == NULL) {
-               gdb_generate_target_description(target, &tdesc);
-               tdesc_length = strlen(tdesc);
+       int retval = gdb_generate_target_description(target, &tdesc);
+       if (retval != ERROR_OK) {
+               LOG_ERROR("Unable to Generate Target Description");
+               return ERROR_FAIL;
        }
 
+       tdesc_length = strlen(tdesc);
+
        struct fileio fileio;
        size_t size_written;
 
-       tdesc_filename = malloc(strlen(target_type_name(target)) + 5);
-       sprintf(tdesc_filename, "%s.xml", target_type_name(target));
+       char *tdesc_filename = alloc_printf("%s.xml", target_type_name(target));
+       if (tdesc_filename == NULL) {
+               retval = ERROR_FAIL;
+               goto out;
+       }
 
-       int retval = fileio_open(&fileio, tdesc_filename, FILEIO_WRITE, FILEIO_TEXT);
+       retval = fileio_open(&fileio, tdesc_filename, FILEIO_WRITE, FILEIO_TEXT);
 
        if (retval != ERROR_OK) {
-               LOG_WARNING("Can't open %s for writing", tdesc_filename);
-               free(tdesc_filename);
-               return ERROR_FAIL;
+               LOG_ERROR("Can't open %s for writing", tdesc_filename);
+               goto out;
        }
 
        retval = fileio_write(&fileio, tdesc_length, tdesc, &size_written);
 
        fileio_close(&fileio);
-       free(tdesc_filename);
 
-       if (retval != ERROR_OK) {
-               LOG_WARNING("Error while writing the tdesc file");
-               return ERROR_FAIL;
-       }
+       if (retval != ERROR_OK)
+               LOG_ERROR("Error while writing the tdesc file");
 
-       return ERROR_OK;
+out:
+       free(tdesc_filename);
+       free(tdesc);
+
+       return retval;
 }
 
 static const struct command_registration gdb_command_handlers[] = {

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)