X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fserver%2Fgdb_server.c;h=c293f5b057235f829bd6228c94b0459c23dff4b9;hp=beeeedcce1470fc804cdd96274e650370c4c3528;hb=47b8cf84202bf792cf66fbfa01169e9592236b8a;hpb=d979d78e97786667d168ba183c9fc60c622d29c1 diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index beeeedcce1..c293f5b057 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -31,9 +31,7 @@ * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * + * along with this program. If not, see . * ***************************************************************************/ #ifdef HAVE_CONFIG_H @@ -60,6 +58,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 +88,10 @@ 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; + /* temporarily used for thread list support */ + char *thread_list; }; #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) { @@ -607,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 if (gdb_con->noack_mode) { + 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"); @@ -677,7 +697,8 @@ static int gdb_output_con(struct connection *connection, const char *line) return ERROR_GDB_BUFFER_TOO_SMALL; hex_buffer[0] = 'O'; - int pkt_len = hexify(hex_buffer + 1, line, bin_size, bin_size * 2 + 1); + size_t pkt_len = hexify(hex_buffer + 1, (const uint8_t *)line, bin_size, + bin_size * 2 + 1); int retval = gdb_put_packet(connection, hex_buffer, pkt_len + 1); free(hex_buffer); @@ -691,6 +712,154 @@ static int gdb_output(struct command_context *context, const char *line) return ERROR_OK; } +static void gdb_signal_reply(struct target *target, struct connection *connection) +{ + struct gdb_connection *gdb_connection = connection->priv; + char sig_reply[45]; + char stop_reason[20]; + char current_thread[25]; + int sig_reply_len; + int signal_var; + + rtos_update_threads(target); + + 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; + } else + signal_var = gdb_last_signal(target); + + stop_reason[0] = '\0'; + if (target->debug_reason == DBG_REASON_WATCHPOINT) { + enum watchpoint_rw hit_wp_type; + target_addr_t hit_wp_address; + + if (watchpoint_hit(target, &hit_wp_type, &hit_wp_address) == ERROR_OK) { + + switch (hit_wp_type) { + case WPT_WRITE: + snprintf(stop_reason, sizeof(stop_reason), + "watch:%08" TARGET_PRIxADDR ";", hit_wp_address); + break; + case WPT_READ: + snprintf(stop_reason, sizeof(stop_reason), + "rwatch:%08" TARGET_PRIxADDR ";", hit_wp_address); + break; + case WPT_ACCESS: + snprintf(stop_reason, sizeof(stop_reason), + "awatch:%08" TARGET_PRIxADDR ";", hit_wp_address); + break; + default: + break; + } + } + } + + current_thread[0] = '\0'; + if (target->rtos != NULL) { + snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";", target->rtos->current_thread); + target->rtos->current_threadid = target->rtos->current_thread; + } + + sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s", + signal_var, stop_reason, current_thread); + } + + gdb_put_packet(connection, sig_reply, sig_reply_len); + gdb_connection->frontend_state = TARGET_HALTED; +} + +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,%" 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,%" PRIx32, target->fileio_info->identifier, + target->fileio_info->param_1); + else if (strcmp(target->fileio_info->identifier, "read") == 0) + 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,%" 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,%" 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,%" 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,%" 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,%" 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,%" 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,%" 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,%" PRIx32, target->fileio_info->identifier, + target->fileio_info->param_1); + else if (strcmp(target->fileio_info->identifier, "system") == 0) + 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%02" PRIx32, 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; @@ -705,22 +874,14 @@ static void gdb_frontend_halted(struct target *target, struct connection *connec * that are to be ignored. */ if (gdb_connection->frontend_state == TARGET_RUNNING) { - char sig_reply[4]; - int signal_var; - /* stop forwarding log packets! */ log_remove_callback(gdb_log_callback, connection); - if (gdb_connection->ctrl_c) { - signal_var = 0x2; - gdb_connection->ctrl_c = 0; - } else - signal_var = gdb_last_signal(target); - - snprintf(sig_reply, 4, "T%2.2x", signal_var); - gdb_put_packet(connection, sig_reply, 3); - gdb_connection->frontend_state = TARGET_HALTED; - rtos_update_threads(target); + /* 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); } } @@ -729,6 +890,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: @@ -767,9 +932,12 @@ 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; + gdb_connection->thread_list = NULL; /* send ACK to GDB for debug request */ gdb_write(connection, "+", 1); @@ -809,6 +977,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 " \ @@ -883,7 +1054,7 @@ static void gdb_send_error(struct connection *connection, uint8_t the_error) } static int gdb_last_signal_packet(struct connection *connection, - char *packet, int packet_size) + char const *packet, int packet_size) { struct target *target = get_target_from_connection(connection); struct gdb_connection *gdb_con = connection->priv; @@ -940,7 +1111,7 @@ static void gdb_str_to_target(struct target *target, /* copy over in register buffer */ static void gdb_target_to_reg(struct target *target, - char *tstr, int str_len, uint8_t *bin) + char const *tstr, int str_len, uint8_t *bin) { if (str_len % 2) { LOG_ERROR("BUG: gdb value with uneven number of characters encountered"); @@ -961,7 +1132,7 @@ static void gdb_target_to_reg(struct target *target, } static int gdb_get_registers_packet(struct connection *connection, - char *packet, int packet_size) + char const *packet, int packet_size) { struct target *target = get_target_from_connection(connection); struct reg **reg_list; @@ -990,6 +1161,9 @@ static int gdb_get_registers_packet(struct connection *connection, assert(reg_packet_size > 0); reg_packet = malloc(reg_packet_size + 1); /* plus one for string termination null */ + if (reg_packet == NULL) + return ERROR_FAIL; + reg_packet_p = reg_packet; for (i = 0; i < reg_list_size; i++) { @@ -1017,14 +1191,14 @@ static int gdb_get_registers_packet(struct connection *connection, } static int gdb_set_registers_packet(struct connection *connection, - char *packet, int packet_size) + char const *packet, int packet_size) { struct target *target = get_target_from_connection(connection); int i; struct reg **reg_list; int reg_list_size; int retval; - char *packet_p; + char const *packet_p; #ifdef _DEBUG_GDB_IO_ LOG_DEBUG("-"); @@ -1072,7 +1246,7 @@ static int gdb_set_registers_packet(struct connection *connection, } static int gdb_get_register_packet(struct connection *connection, - char *packet, int packet_size) + char const *packet, int packet_size) { struct target *target = get_target_from_connection(connection); char *reg_packet; @@ -1111,7 +1285,7 @@ static int gdb_get_register_packet(struct connection *connection, } static int gdb_set_register_packet(struct connection *connection, - char *packet, int packet_size) + char const *packet, int packet_size) { struct target *target = get_target_from_connection(connection); char *separator; @@ -1177,7 +1351,7 @@ static int gdb_error(struct connection *connection, int retval) * 8191 bytes by the looks of it. Why 8191 bytes instead of 8192????? */ static int gdb_read_memory_packet(struct connection *connection, - char *packet, int packet_size) + char const *packet, int packet_size) { struct target *target = get_target_from_connection(connection); char *separator; @@ -1201,6 +1375,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); @@ -1228,7 +1408,7 @@ static int gdb_read_memory_packet(struct connection *connection, if (retval == ERROR_OK) { hex_buffer = malloc(len * 2 + 1); - int pkt_len = hexify(hex_buffer, (char *)buffer, len, len * 2 + 1); + size_t pkt_len = hexify(hex_buffer, buffer, len, len * 2 + 1); gdb_put_packet(connection, hex_buffer, pkt_len); @@ -1242,7 +1422,7 @@ static int gdb_read_memory_packet(struct connection *connection, } static int gdb_write_memory_packet(struct connection *connection, - char *packet, int packet_size) + char const *packet, int packet_size) { struct target *target = get_target_from_connection(connection); char *separator; @@ -1273,7 +1453,7 @@ static int gdb_write_memory_packet(struct connection *connection, LOG_DEBUG("addr: 0x%8.8" PRIx32 ", len: 0x%8.8" PRIx32 "", addr, len); - if (unhexify((char *)buffer, separator, len) != (int)len) + if (unhexify(buffer, separator, len) != len) LOG_ERROR("unable to decode memory packet"); retval = target_write_buffer(target, addr, len, buffer); @@ -1289,7 +1469,7 @@ static int gdb_write_memory_packet(struct connection *connection, } static int gdb_write_memory_binary_packet(struct connection *connection, - char *packet, int packet_size) + char const *packet, int packet_size) { struct target *target = get_target_from_connection(connection); char *separator; @@ -1297,6 +1477,10 @@ static int gdb_write_memory_binary_packet(struct connection *connection, uint32_t len = 0; int retval = ERROR_OK; + /* Packets larger than fast_limit bytes will be acknowledged instantly on + * the assumption that we're in a download and it's important to go as fast + * as possible. */ + uint32_t fast_limit = 8; /* skip command character */ packet++; @@ -1317,19 +1501,23 @@ static int gdb_write_memory_binary_packet(struct connection *connection, struct gdb_connection *gdb_connection = connection->priv; - if (gdb_connection->mem_write_error) { + if (gdb_connection->mem_write_error) retval = ERROR_FAIL; - /* now that we have reported the memory write error, we can clear the condition */ - gdb_connection->mem_write_error = false; - } - /* By replying the packet *immediately* GDB will send us a new packet - * while we write the last one to the target. - */ - if (retval == ERROR_OK) - gdb_put_packet(connection, "OK", 2); - else { + if (retval == ERROR_OK) { + if (len >= fast_limit) { + /* By replying the packet *immediately* GDB will send us a new packet + * while we write the last one to the target. + * We only do this for larger writes, so that users who do something like: + * p *((int*)0xdeadbeef)=8675309 + * will get immediate feedback that that write failed. + */ + gdb_put_packet(connection, "OK", 2); + } + } else { retval = gdb_error(connection, retval); + /* now that we have reported the memory write error, we can clear the condition */ + gdb_connection->mem_write_error = false; if (retval != ERROR_OK) return retval; } @@ -1342,11 +1530,20 @@ static int gdb_write_memory_binary_packet(struct connection *connection, gdb_connection->mem_write_error = true; } + if (len < fast_limit) { + if (retval != ERROR_OK) { + gdb_error(connection, retval); + gdb_connection->mem_write_error = false; + } else { + gdb_put_packet(connection, "OK", 2); + } + } + return ERROR_OK; } static int gdb_step_continue_packet(struct connection *connection, - char *packet, int packet_size) + char const *packet, int packet_size) { struct target *target = get_target_from_connection(connection); int current = 0; @@ -1355,12 +1552,12 @@ 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]; if (packet[0] == 'c') { LOG_DEBUG("continue"); /* resume at current address, don't handle breakpoints, not debugging */ @@ -1374,7 +1571,7 @@ static int gdb_step_continue_packet(struct connection *connection, } static int gdb_breakpoint_watchpoint_packet(struct connection *connection, - char *packet, int packet_size) + char const *packet, int packet_size) { struct target *target = get_target_from_connection(connection); int type; @@ -1502,29 +1699,31 @@ 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 const *buf, char **annex, int *ofs, unsigned int *len) { - char *separator; - - /* Extract and NUL-terminate the annex. */ - *annex = buf; - while (*buf && *buf != ':') - buf++; - if (*buf == '\0') - return -1; - *buf++ = 0; + /* Locate the annex. */ + const char *annex_end = strchr(buf, ':'); + if (annex_end == NULL) + return ERROR_FAIL; /* After the read marker and annex, qXfer looks like a * traditional 'm' packet. */ - - *ofs = strtoul(buf, &separator, 16); + char *separator; + *ofs = strtoul(annex_end + 1, &separator, 16); if (*separator != ',') - return -1; + return ERROR_FAIL; *len = strtoul(separator + 1, NULL, 16); - return 0; + /* Extract the annex if needed */ + if (annex != NULL) { + *annex = strndup(buf, annex_end - buf); + if (*annex == NULL) + return ERROR_FAIL; + } + + return ERROR_OK; } static int compare_bank(const void *a, const void *b) @@ -1542,7 +1741,7 @@ static int compare_bank(const void *a, const void *b) } static int gdb_memory_map(struct connection *connection, - char *packet, int packet_size) + char const *packet, int packet_size) { /* We get away with only specifying flash here. Regions that are not * specified are treated as if we provided no memory map(if not we @@ -1583,14 +1782,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 *), @@ -1688,6 +1889,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: @@ -1712,6 +1915,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: @@ -1823,7 +2028,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 const **feature_list[], int *feature_list_size, struct reg **reg_list, int reg_list_size) { int tbl_sz = 0; @@ -1835,7 +2040,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 @@ -1843,7 +2049,7 @@ int get_reg_features_list(struct target *target, char **feature_list[], int *fea */ for (int j = 0; j < (tbl_sz + 1); j++) { if (!((*feature_list)[j])) { - (*feature_list)[tbl_sz++] = strdup(reg_list[i]->feature->name); + (*feature_list)[tbl_sz++] = reg_list[i]->feature->name; *feature_list = realloc(*feature_list, sizeof(char *) * (tbl_sz + 1)); (*feature_list)[tbl_sz] = NULL; break; @@ -1861,46 +2067,53 @@ 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; + struct reg **reg_list = NULL; int reg_list_size; + char const **features = NULL; + int feature_list_size = 0; + 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); if (retval != ERROR_OK) { LOG_ERROR("get register list failed"); - return ERROR_FAIL; + retval = ERROR_FAIL; + goto error; } - if (reg_list_size <= 0) - return ERROR_FAIL; + if (reg_list_size <= 0) { + LOG_ERROR("get register list failed"); + retval = ERROR_FAIL; + goto error; + } - char **features = NULL; /* Get a list of available target registers features */ - retval = get_reg_features_list(target, &features, NULL, reg_list, reg_list_size); + 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"); - return ERROR_FAIL; + retval = ERROR_FAIL; + goto error; } /* 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]); @@ -1917,7 +2130,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; @@ -1931,57 +2144,70 @@ 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); +error: + free(features); + free(reg_list); - 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); } @@ -1993,6 +2219,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); @@ -2007,11 +2238,146 @@ 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; + char const **features = NULL; + int feature_list_size = 0; + + 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) { + LOG_ERROR("get register list failed"); + 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: + free(features); + + free(reg_list); + + return retval; +} + +static int gdb_generate_thread_list(struct target *target, char **thread_list_out) +{ + struct rtos *rtos = target->rtos; + int retval = ERROR_OK; + char *thread_list = NULL; + int pos = 0; + int size = 0; + + xml_printf(&retval, &thread_list, &pos, &size, + "\n" + "\n"); + + if (rtos != NULL) { + for (int i = 0; i < rtos->thread_count; i++) { + struct thread_detail *thread_detail = &rtos->thread_details[i]; + + if (!thread_detail->exists) + continue; + + xml_printf(&retval, &thread_list, &pos, &size, + "", thread_detail->threadid); + + if (thread_detail->thread_name_str != NULL) + xml_printf(&retval, &thread_list, &pos, &size, + "Name: %s", thread_detail->thread_name_str); + + if (thread_detail->extra_info_str != NULL) { + if (thread_detail->thread_name_str != NULL) + xml_printf(&retval, &thread_list, &pos, &size, + ", "); + xml_printf(&retval, &thread_list, &pos, &size, + thread_detail->extra_info_str); + } + + xml_printf(&retval, &thread_list, &pos, &size, + "\n"); + } + } + + xml_printf(&retval, &thread_list, &pos, &size, + "\n"); + + if (retval == ERROR_OK) + *thread_list_out = thread_list; + else + free(thread_list); + + return retval; +} + +static int gdb_get_thread_list_chunk(struct target *target, char **thread_list, + char **chunk, int32_t offset, uint32_t length) +{ + if (*thread_list == NULL) { + int retval = gdb_generate_thread_list(target, thread_list); + if (retval != ERROR_OK) { + LOG_ERROR("Unable to Generate Thread List"); + return ERROR_FAIL; + } + } + + size_t thread_list_length = strlen(*thread_list); + char transfer_type; + + length = MIN(length, thread_list_length - offset); + if (length < (thread_list_length - offset)) + transfer_type = 'm'; + else + transfer_type = 'l'; + + *chunk = malloc(length + 2); + if (*chunk == NULL) { + LOG_ERROR("Unable to allocate memory"); + return ERROR_FAIL; + } + + (*chunk)[0] = transfer_type; + strncpy((*chunk) + 1, (*thread_list) + offset, length); + (*chunk)[1 + length] = '\0'; + + /* After gdb-server sends out last chunk, invalidate thread list. */ + if (transfer_type == 'l') { + free(*thread_list); + *thread_list = NULL; + } + return ERROR_OK; } static int gdb_query_packet(struct connection *connection, - char *packet, int packet_size) + char const *packet, int packet_size) { struct command_context *cmd_ctx = connection->cmd_ctx; struct gdb_connection *gdb_connection = connection->priv; @@ -2021,7 +2387,7 @@ static int gdb_query_packet(struct connection *connection, if (packet_size > 6) { char *cmd; cmd = malloc((packet_size - 6) / 2 + 1); - int len = unhexify(cmd, packet + 6, (packet_size - 6) / 2); + size_t len = unhexify((uint8_t *)cmd, packet + 6, (packet_size - 6) / 2); cmd[len] = 0; /* We want to print all debug output to GDB connection */ @@ -2074,20 +2440,35 @@ 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, &pos, &size, - "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;QStartNoAckMode+", + "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;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); @@ -2107,12 +2488,11 @@ static int gdb_query_packet(struct connection *connection, int offset; unsigned int length; - char *annex; /* skip command character */ packet += 20; - if (decode_xfer_read(packet, &annex, &offset, &length) < 0) { + if (decode_xfer_read(packet, NULL, &offset, &length) < 0) { gdb_send_error(connection, 01); return ERROR_OK; } @@ -2122,7 +2502,39 @@ 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; + } + + gdb_put_packet(connection, xml, strlen(xml)); + + free(xml); + return ERROR_OK; + } else if (strncmp(packet, "qXfer:threads:read:", 19) == 0) { + char *xml = NULL; + int retval = ERROR_OK; + + int offset; + unsigned int length; + + /* skip command character */ + packet += 19; + + if (decode_xfer_read(packet, NULL, &offset, &length) < 0) { + gdb_send_error(connection, 01); + return ERROR_OK; + } + + /* Target should prepare correct thread list 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_thread_list_chunk(target, &gdb_connection->thread_list, + &xml, offset, length); if (retval != ERROR_OK) { gdb_error(connection, retval); return retval; @@ -2143,7 +2555,7 @@ static int gdb_query_packet(struct connection *connection, } static int gdb_v_packet(struct connection *connection, - char *packet, int packet_size) + char const *packet, int packet_size) { struct gdb_connection *gdb_connection = connection->priv; struct gdb_service *gdb_service = connection->service->priv; @@ -2160,20 +2572,20 @@ static int gdb_v_packet(struct connection *connection, unsigned long addr; unsigned long length; - char *parse = packet + 12; + char const *parse = packet + 12; if (*parse == '\0') { LOG_ERROR("incomplete vFlashErase packet received, dropping connection"); return ERROR_SERVER_REMOTE_CLOSED; } - addr = strtoul(parse, &parse, 16); + addr = strtoul(parse, (char **)&parse, 16); if (*(parse++) != ',' || *parse == '\0') { LOG_ERROR("incomplete vFlashErase packet received, dropping connection"); return ERROR_SERVER_REMOTE_CLOSED; } - length = strtoul(parse, &parse, 16); + length = strtoul(parse, (char **)&parse, 16); if (*parse != '\0') { LOG_ERROR("incomplete vFlashErase packet received, dropping connection"); @@ -2216,13 +2628,13 @@ static int gdb_v_packet(struct connection *connection, int retval; unsigned long addr; unsigned long length; - char *parse = packet + 12; + char const *parse = packet + 12; if (*parse == '\0') { LOG_ERROR("incomplete vFlashErase packet received, dropping connection"); return ERROR_SERVER_REMOTE_CLOSED; } - addr = strtoul(parse, &parse, 16); + addr = strtoul(parse, (char **)&parse, 16); if (*(parse++) != ':') { LOG_ERROR("incomplete vFlashErase packet received, dropping connection"); return ERROR_SERVER_REMOTE_CLOSED; @@ -2237,7 +2649,7 @@ static int gdb_v_packet(struct connection *connection, /* create new section with content from packet buffer */ retval = image_add_section(gdb_connection->vflash_image, - addr, length, 0x0, (uint8_t *)parse); + addr, length, 0x0, (uint8_t const *)parse); if (retval != ERROR_OK) return retval; @@ -2285,6 +2697,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 const *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) { @@ -2313,7 +2773,7 @@ static int gdb_input_inner(struct connection *connection) struct gdb_service *gdb_service = connection->service->priv; struct target *target = gdb_service->target; - char *packet = gdb_packet_buffer; + char const *packet = gdb_packet_buffer; int packet_size; int retval; struct gdb_connection *gdb_con = connection->priv; @@ -2332,12 +2792,12 @@ static int gdb_input_inner(struct connection *connection) */ do { packet_size = GDB_BUFFER_SIZE-1; - retval = gdb_get_packet(connection, packet, &packet_size); + retval = gdb_get_packet(connection, gdb_packet_buffer, &packet_size); if (retval != ERROR_OK) return retval; /* terminate with zero */ - packet[packet_size] = 0; + gdb_packet_buffer[packet_size] = '\0'; if (LOG_LEVEL_IS(LOG_LVL_DEBUG)) { if (packet[0] == 'X') { @@ -2511,6 +2971,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]); @@ -2589,6 +3062,11 @@ static int gdb_target_start(struct target *target, const char *port) static int gdb_target_add_one(struct target *target) { + if (strcmp(gdb_port, "disabled") == 0) { + LOG_INFO("gdb port disabled"); + return ERROR_OK; + } + /* one gdb instance per smp list */ if ((target->smp) && (target->gdb_service)) return ERROR_OK; @@ -2602,7 +3080,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); } } @@ -2612,6 +3090,11 @@ static int gdb_target_add_one(struct target *target) int gdb_target_add_all(struct target *target) { + if (strcmp(gdb_port, "disabled") == 0) { + LOG_INFO("gdb server disabled"); + return ERROR_OK; + } + if (NULL == target) { LOG_WARNING("gdb services need one or more targets defined"); return ERROR_OK; @@ -2649,7 +3132,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; @@ -2717,41 +3200,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; } - struct fileio fileio; - size_t size_written; + tdesc_length = strlen(tdesc); - tdesc_filename = malloc(strlen(target_type_name(target)) + 5); - sprintf(tdesc_filename, "%s.xml", target_type_name(target)); + struct fileio *fileio; + size_t size_written; - 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); + retval = fileio_write(fileio, tdesc_length, tdesc, &size_written); - fileio_close(&fileio); + 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[] = { @@ -2772,7 +3260,7 @@ static const struct command_registration gdb_command_handlers[] = { "server listens for the next port number after the " "base port number specified. " "No arguments reports GDB port. \"pipe\" means listen to stdin " - "output to stdout, an integer is base port number, \"disable\" disables " + "output to stdout, an integer is base port number, \"disabled\" disables " "port. Any other string is are interpreted as named pipe to listen to. " "Output pipe is the same name as input pipe, but with 'o' appended.", .usage = "[port_num]",