NULL check before dereferencing
[fw/openocd] / src / server / gdb_server.c
index 8eacf8c388c4b549af590eada61db30aa7abc7db..c578780c8d374cc865c3d2de90a7d2dc95894682 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,11 @@ 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;
 
 static int gdb_last_signal(struct target *target)
 {
@@ -691,28 +701,17 @@ static int gdb_output(struct command_context *context, const char *line)
        return ERROR_OK;
 }
 
-static void gdb_frontend_halted(struct target *target, struct connection *connection)
+static void gdb_signal_reply(struct target *target, struct connection *connection)
 {
        struct gdb_connection *gdb_connection = connection->priv;
+       char sig_reply[20];
+       char stop_reason[20];
+       int sig_reply_len;
+       int signal_var;
 
-       /* In the GDB protocol when we are stepping or continuing execution,
-        * we have a lingering reply. Upon receiving a halted event
-        * when we have that lingering packet, we reply to the original
-        * step or continue packet.
-        *
-        * Executing monitor commands can bring the target in and
-        * out of the running state so we'll see lots of TARGET_EVENT_XXX
-        * that are to be ignored.
-        */
-       if (gdb_connection->frontend_state == TARGET_RUNNING) {
-               char sig_reply[20];
-               char stop_reason[20];
-               int sig_reply_len;
-               int signal_var;
-
-               /* stop forwarding log packets! */
-               log_remove_callback(gdb_log_callback, connection);
-
+       if (target->debug_reason == DBG_REASON_EXIT) {
+               sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "W00");
+       } else {
                if (gdb_connection->ctrl_c) {
                        signal_var = 0x2;
                        gdb_connection->ctrl_c = 0;
@@ -747,18 +746,135 @@ static void gdb_frontend_halted(struct target *target, struct connection *connec
 
                sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s",
                                signal_var, stop_reason);
+       }
 
-               gdb_put_packet(connection, sig_reply, sig_reply_len);
+       gdb_put_packet(connection, sig_reply, sig_reply_len);
+       gdb_connection->frontend_state = TARGET_HALTED;
+       rtos_update_threads(target);
+}
+
+static void gdb_fileio_reply(struct target *target, struct connection *connection)
+{
+       struct gdb_connection *gdb_connection = connection->priv;
+       char fileio_command[256];
+       int command_len;
+       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,
+                               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,
+                               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,
+                               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,
+                               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,
+                               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,
+                               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,
+                               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,
+                               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,
+                               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,
+                               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,
+                               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,
+                               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);
+       } else {
+               LOG_DEBUG("Unknown syscall: %s", target->fileio_info->identifier);
+
+               /* encounter unknown syscall, continue */
+               gdb_connection->frontend_state = TARGET_RUNNING;
+               target_resume(target, 1, 0x0, 0, 0);
+               return;
+       }
+
+       command_len = strlen(fileio_command);
+       gdb_put_packet(connection, fileio_command, command_len);
+
+       if (program_exited) {
+               /* Use target_resume() to let target run its own exit syscall handler. */
+               gdb_connection->frontend_state = TARGET_RUNNING;
+               target_resume(target, 1, 0x0, 0, 0);
+       } else {
                gdb_connection->frontend_state = TARGET_HALTED;
                rtos_update_threads(target);
        }
 }
 
+static void gdb_frontend_halted(struct target *target, struct connection *connection)
+{
+       struct gdb_connection *gdb_connection = connection->priv;
+
+       /* In the GDB protocol when we are stepping or continuing execution,
+        * we have a lingering reply. Upon receiving a halted event
+        * when we have that lingering packet, we reply to the original
+        * step or continue packet.
+        *
+        * Executing monitor commands can bring the target in and
+        * out of the running state so we'll see lots of TARGET_EVENT_XXX
+        * that are to be ignored.
+        */
+       if (gdb_connection->frontend_state == TARGET_RUNNING) {
+               /* stop forwarding log packets! */
+               log_remove_callback(gdb_log_callback, connection);
+
+               /* check fileio first */
+               if (target_get_gdb_fileio_info(target, target->fileio_info) == ERROR_OK)
+                       gdb_fileio_reply(target, connection);
+               else
+                       gdb_signal_reply(target, connection);
+       }
+}
+
 static int gdb_target_callback_event_handler(struct target *target,
                enum target_event event, void *priv)
 {
        int retval;
        struct connection *connection = priv;
+       struct gdb_service *gdb_service = connection->service->priv;
+
+       if (gdb_service->target != target)
+               return ERROR_OK;
 
        switch (event) {
                case TARGET_EVENT_GDB_HALT:
@@ -797,9 +913,11 @@ static int gdb_new_connection(struct connection *connection)
        gdb_connection->closed = 0;
        gdb_connection->busy = 0;
        gdb_connection->noack_mode = 0;
-       gdb_connection->sync = true;
+       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);
@@ -1391,6 +1509,7 @@ static int gdb_step_continue_packet(struct connection *connection,
        } else
                current = 1;
 
+       gdb_running_type = packet[0];
        if (packet[0] == 'c') {
                LOG_DEBUG("continue");
                /* resume at current address, don't handle breakpoints, not debugging */
@@ -1718,6 +1837,8 @@ static int gdb_memory_map(struct connection *connection,
 static const char *gdb_get_reg_type_name(enum reg_type type)
 {
        switch (type) {
+               case REG_TYPE_INT:
+                       return "int";
                case REG_TYPE_INT8:
                        return "int8";
                case REG_TYPE_INT16:
@@ -1742,6 +1863,8 @@ static const char *gdb_get_reg_type_name(enum reg_type type)
                        return "code_ptr";
                case REG_TYPE_DATA_PTR:
                        return "data_ptr";
+               case REG_TYPE_FLOAT:
+                       return "float";
                case REG_TYPE_IEEE_SINGLE:
                        return "ieee_single";
                case REG_TYPE_IEEE_DOUBLE:
@@ -1853,7 +1976,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;
@@ -1865,7 +1988,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
@@ -1891,19 +2015,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);
 
@@ -1912,25 +2032,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]);
 
@@ -1947,7 +2075,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;
@@ -1961,57 +2089,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);
        }
 
@@ -2023,6 +2163,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);
@@ -2037,9 +2182,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;
+
+       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;
+       }
+
+       char **features = NULL;
+       /* 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)
 {
@@ -2104,11 +2296,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,
@@ -2117,7 +2324,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);
@@ -2152,7 +2359,8 @@ static int gdb_query_packet(struct connection *connection,
                 * 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;
@@ -2315,6 +2523,54 @@ static int gdb_detach(struct connection *connection)
        return gdb_put_packet(connection, "OK", 2);
 }
 
+/* The format of 'F' response packet is
+ * Fretcode,errno,Ctrl-C flag;call-specific attachment
+ */
+static int gdb_fileio_response_packet(struct connection *connection,
+               char *packet, int packet_size)
+{
+       struct target *target = get_target_from_connection(connection);
+       char *separator;
+       char *parsing_point;
+       int fileio_retcode = strtoul(packet + 1, &separator, 16);
+       int fileio_errno = 0;
+       bool fileio_ctrl_c = false;
+       int retval;
+
+       LOG_DEBUG("-");
+
+       if (*separator == ',') {
+               parsing_point = separator + 1;
+               fileio_errno = strtoul(parsing_point, &separator, 16);
+               if (*separator == ',') {
+                       if (*(separator + 1) == 'C') {
+                               /* TODO: process ctrl-c */
+                               fileio_ctrl_c = true;
+                       }
+               }
+       }
+
+       LOG_DEBUG("File-I/O response, retcode: 0x%x, errno: 0x%x, ctrl-c: %s",
+                       fileio_retcode, fileio_errno, fileio_ctrl_c ? "true" : "false");
+
+       retval = target_gdb_fileio_end(target, fileio_retcode, fileio_errno, fileio_ctrl_c);
+       if (retval != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* After File-I/O ends, keep continue or step */
+       if (gdb_running_type == 'c')
+               retval = target_resume(target, 1, 0x0, 0, 0);
+       else if (gdb_running_type == 's')
+               retval = target_step(target, 1, 0x0, 0);
+       else
+               retval = ERROR_FAIL;
+
+       if (retval != ERROR_OK)
+               return ERROR_FAIL;
+
+       return ERROR_OK;
+}
+
 static void gdb_log_callback(void *priv, const char *file, unsigned line,
                const char *function, const char *string)
 {
@@ -2541,6 +2797,19 @@ static int gdb_input_inner(struct connection *connection)
                                        gdb_write_smp_packet(connection, packet, packet_size);
                                        break;
 
+                               case 'F':
+                                       /* File-I/O extension */
+                                       /* After gdb uses host-side syscall to complete target file
+                                        * I/O, gdb sends host-side syscall return value to target
+                                        * by 'F' packet.
+                                        * The format of 'F' response packet is
+                                        * Fretcode,errno,Ctrl-C flag;call-specific attachment
+                                        */
+                                       gdb_con->frontend_state = TARGET_RUNNING;
+                                       log_add_callback(gdb_log_callback, connection);
+                                       gdb_fileio_response_packet(connection, packet, packet_size);
+                                       break;
+
                                default:
                                        /* ignore unknown packets */
                                        LOG_DEBUG("ignoring 0x%2.2x packet", packet[0]);
@@ -2632,7 +2901,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);
                        }
                }
@@ -2679,7 +2948,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;
@@ -2747,41 +3016,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);
-
-       free(tdesc_filename);
+       retval = fileio_open(&fileio, tdesc_filename, FILEIO_WRITE, FILEIO_TEXT);
 
        if (retval != ERROR_OK) {
-               LOG_WARNING("Can't open %s for writing", 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);
 
-       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[] = {