X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fserver%2Fgdb_server.c;h=0b28287d80a7c8ee22f69a7f18d72ad3e69f0162;hp=c4ad91cfab5743f9e30f9fe90256d6031da2ad69;hb=537b06a810778995d7f79d9120f4063f33dee635;hpb=399561eafa747e71202d00985206b65868142afb diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index c4ad91cfab..0b28287d80 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -60,6 +60,11 @@ * 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); @@ -864,6 +881,10 @@ static int gdb_target_callback_event_handler(struct target *target, { 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: @@ -902,9 +923,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); @@ -944,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 " \ @@ -1336,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); @@ -1490,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]; @@ -1638,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) @@ -1719,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 *), @@ -1824,6 +1869,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: @@ -1848,6 +1895,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: @@ -1959,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; @@ -1971,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 @@ -1997,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, - "\n" - "\n" - "\n"); - retval = target_get_gdb_reg_list(target, ®_list, ®_list_size, REG_CLASS_ALL); @@ -2018,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, + "\n" + "\n" + "\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, "\n", features[current_feature]); @@ -2053,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 reg_data_type); type_str = reg_list[i]->reg_data_type->id; @@ -2067,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, "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, "\n"); current_feature++; } } - xml_printf(&retval, tdesc, &pos, &size, + xml_printf(&retval, &tdesc, &pos, &size, "\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); } @@ -2129,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); @@ -2143,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, ®_list, + ®_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) { @@ -2210,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, @@ -2223,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); @@ -2243,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; @@ -2252,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; @@ -2799,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); } } @@ -2846,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; @@ -2914,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)); - - int retval = fileio_open(&fileio, tdesc_filename, FILEIO_WRITE, FILEIO_TEXT); + char *tdesc_filename = alloc_printf("%s.xml", target_type_name(target)); + if (tdesc_filename == NULL) { + retval = ERROR_FAIL; + goto out; + } - 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[] = {