X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=blobdiff_plain;f=src%2Fserver%2Fgdb_server.c;h=61d7686c44b7bd16a01bc2d58d21ae28d88e2608;hp=c369665547f14c545a832c824fea5b87a23759d0;hb=HEAD;hpb=d3249fd45a7832ed922ed4227c867c3c674b4812 diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index c369665547..dfd7cd5201 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /*************************************************************************** * Copyright (C) 2005 by Dominic Rath * * Dominic.Rath@gmx.de * @@ -19,19 +21,6 @@ * * * Copyright (C) 2013 Franck Jullien * * elec4fun@gmail.com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * 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, see . * ***************************************************************************/ #ifdef HAVE_CONFIG_H @@ -61,6 +50,15 @@ * found in most modern embedded processors. */ +enum gdb_output_flag { + /* GDB doesn't accept 'O' packets */ + GDB_OUTPUT_NO, + /* GDB doesn't accept 'O' packets but accepts notifications */ + GDB_OUTPUT_NOTIF, + /* GDB accepts 'O' packets */ + GDB_OUTPUT_ALL, +}; + struct target_desc_format { char *tdesc; uint32_t tdesc_length; @@ -75,6 +73,8 @@ struct gdb_connection { enum target_state frontend_state; struct image *vflash_image; bool closed; + /* set to prevent re-entrance from log messages during gdb_get_packet() + * and gdb_put_packet(). */ bool busy; int noack_mode; /* set flag to true if you want the next stepi to return immediately. @@ -97,6 +97,10 @@ struct gdb_connection { struct target_desc_format target_desc; /* temporarily used for thread list support */ char *thread_list; + /* flag to mask the output from gdb_log_callback() */ + enum gdb_output_flag output_flag; + /* Unique index for this GDB connection. */ + unsigned int unique_index; }; #if 0 @@ -119,7 +123,7 @@ static void gdb_sig_halted(struct connection *connection); /* number of gdb connections, mainly to suppress gdb related debugging spam * in helper/log.c when no gdb connections are actually active */ -int gdb_actual_connections; +static int gdb_actual_connections; /* set if we are sending a memory map to gdb * via qXfer:memory-map:read packet */ @@ -147,6 +151,9 @@ static char gdb_running_type; static int gdb_last_signal(struct target *target) { + LOG_TARGET_DEBUG(target, "Debug reason is: %s", + target_debug_reason_str(target->debug_reason)); + switch (target->debug_reason) { case DBG_REASON_DBGRQ: return 0x2; /* SIGINT */ @@ -161,8 +168,9 @@ static int gdb_last_signal(struct target *target) case DBG_REASON_NOTHALTED: return 0x0; /* no signal... shouldn't happen */ default: - LOG_USER("undefined debug reason %d - target needs reset", - target->debug_reason); + LOG_USER("undefined debug reason %d (%s) - target needs reset", + target->debug_reason, + target_debug_reason_str(target->debug_reason)); return 0x0; } } @@ -177,7 +185,7 @@ static int check_pending(struct connection *connection, fd_set read_fds; struct gdb_connection *gdb_con = connection->priv; int t; - if (got_data == NULL) + if (!got_data) got_data = &t; *got_data = 0; @@ -227,44 +235,26 @@ static int gdb_get_char_inner(struct connection *connection, int *next_char) if (gdb_con->buf_cnt > 0) break; if (gdb_con->buf_cnt == 0) { + LOG_DEBUG("GDB connection closed by the remote client"); gdb_con->closed = true; return ERROR_SERVER_REMOTE_CLOSED; } #ifdef _WIN32 - errno = WSAGetLastError(); - - switch (errno) { - case WSAEWOULDBLOCK: - usleep(1000); - break; - case WSAECONNABORTED: - gdb_con->closed = true; - return ERROR_SERVER_REMOTE_CLOSED; - case WSAECONNRESET: - gdb_con->closed = true; - return ERROR_SERVER_REMOTE_CLOSED; - default: - LOG_ERROR("read: %d", errno); - exit(-1); - } + bool retry = (WSAGetLastError() == WSAEWOULDBLOCK); #else - switch (errno) { - case EAGAIN: - usleep(1000); - break; - case ECONNABORTED: - gdb_con->closed = true; - return ERROR_SERVER_REMOTE_CLOSED; - case ECONNRESET: - gdb_con->closed = true; - return ERROR_SERVER_REMOTE_CLOSED; - default: - LOG_ERROR("read: %s", strerror(errno)); - gdb_con->closed = true; - return ERROR_SERVER_REMOTE_CLOSED; - } + bool retry = (errno == EAGAIN); #endif + + if (retry) { + // Try again after a delay + usleep(1000); + } else { + // Print error and close the socket + log_socket_error("GDB"); + gdb_con->closed = true; + return ERROR_SERVER_REMOTE_CLOSED; + } } #ifdef _DEBUG_GDB_IO_ @@ -348,23 +338,74 @@ static int gdb_putback_char(struct connection *connection, int last_char) static int gdb_write(struct connection *connection, void *data, int len) { struct gdb_connection *gdb_con = connection->priv; - if (gdb_con->closed) + if (gdb_con->closed) { + LOG_DEBUG("GDB socket marked as closed, cannot write to it."); return ERROR_SERVER_REMOTE_CLOSED; + } if (connection_write(connection, data, len) == len) return ERROR_OK; + + LOG_WARNING("Error writing to GDB socket. Dropping the connection."); gdb_con->closed = true; return ERROR_SERVER_REMOTE_CLOSED; } +static void gdb_log_incoming_packet(struct connection *connection, char *packet) +{ + if (!LOG_LEVEL_IS(LOG_LVL_DEBUG)) + return; + + struct target *target = get_target_from_connection(connection); + struct gdb_connection *gdb_connection = connection->priv; + + /* Avoid dumping non-printable characters to the terminal */ + const unsigned packet_len = strlen(packet); + const char *nonprint = find_nonprint_char(packet, packet_len); + if (nonprint) { + /* Does packet at least have a prefix that is printable? + * Look within the first 50 chars of the packet. */ + const char *colon = memchr(packet, ':', MIN(50, packet_len)); + const bool packet_has_prefix = (colon); + const bool packet_prefix_printable = (packet_has_prefix && nonprint > colon); + + if (packet_prefix_printable) { + const unsigned int prefix_len = colon - packet + 1; /* + 1 to include the ':' */ + const unsigned int payload_len = packet_len - prefix_len; + LOG_TARGET_DEBUG(target, "{%d} received packet: %.*s", + gdb_connection->unique_index, prefix_len, packet, payload_len); + } else { + LOG_TARGET_DEBUG(target, "{%d} received packet: ", + gdb_connection->unique_index, packet_len); + } + } else { + /* All chars printable, dump the packet as is */ + LOG_TARGET_DEBUG(target, "{%d} received packet: %s", gdb_connection->unique_index, packet); + } +} + +static void gdb_log_outgoing_packet(struct connection *connection, char *packet_buf, + unsigned int packet_len, unsigned char checksum) +{ + if (!LOG_LEVEL_IS(LOG_LVL_DEBUG)) + return; + + struct target *target = get_target_from_connection(connection); + struct gdb_connection *gdb_connection = connection->priv; + + if (find_nonprint_char(packet_buf, packet_len)) + LOG_TARGET_DEBUG(target, "{%d} sending packet: $#%2.2x", + gdb_connection->unique_index, packet_len, checksum); + else + LOG_TARGET_DEBUG(target, "{%d} sending packet: $%.*s#%2.2x", + gdb_connection->unique_index, packet_len, packet_buf, checksum); +} + static int gdb_put_packet_inner(struct connection *connection, char *buffer, int len) { int i; unsigned char my_checksum = 0; -#ifdef _DEBUG_GDB_IO_ - char *debug_buffer; -#endif int reply; int retval; struct gdb_connection *gdb_con = connection->priv; @@ -400,11 +441,7 @@ static int gdb_put_packet_inner(struct connection *connection, #endif while (1) { -#ifdef _DEBUG_GDB_IO_ - debug_buffer = strndup(buffer, len); - LOG_DEBUG("sending packet '$%s#%2.2x'", debug_buffer, my_checksum); - free(debug_buffer); -#endif + gdb_log_outgoing_packet(connection, buffer, len, my_checksum); char local_buffer[1024]; local_buffer[0] = '$'; @@ -437,22 +474,27 @@ static int gdb_put_packet_inner(struct connection *connection, if (retval != ERROR_OK) return retval; - if (reply == '+') + if (reply == '+') { + gdb_log_incoming_packet(connection, "+"); break; - else if (reply == '-') { + } else if (reply == '-') { /* Stop sending output packets for now */ - log_remove_callback(gdb_log_callback, connection); + gdb_con->output_flag = GDB_OUTPUT_NO; + gdb_log_incoming_packet(connection, "-"); LOG_WARNING("negative reply, retrying"); } else if (reply == 0x3) { gdb_con->ctrl_c = true; + gdb_log_incoming_packet(connection, ""); retval = gdb_get_char(connection, &reply); if (retval != ERROR_OK) return retval; - if (reply == '+') + if (reply == '+') { + gdb_log_incoming_packet(connection, "+"); break; - else if (reply == '-') { + } else if (reply == '-') { /* Stop sending output packets for now */ - log_remove_callback(gdb_log_callback, connection); + gdb_con->output_flag = GDB_OUTPUT_NO; + gdb_log_incoming_packet(connection, "-"); LOG_WARNING("negative reply, retrying"); } else if (reply == '$') { LOG_ERROR("GDB missing ack(1) - assumed good"); @@ -629,6 +671,7 @@ static int gdb_get_packet_inner(struct connection *connection, case '$': break; case '+': + gdb_log_incoming_packet(connection, "+"); /* According to the GDB documentation * (https://sourceware.org/gdb/onlinedocs/gdb/Packet-Acknowledgment.html): * "gdb sends a final `+` acknowledgment of the stub's `OK` @@ -646,9 +689,11 @@ static int gdb_get_packet_inner(struct connection *connection, } break; case '-': + gdb_log_incoming_packet(connection, "-"); LOG_WARNING("negative acknowledgment, but no packet pending"); break; case 0x3: + gdb_log_incoming_packet(connection, ""); gdb_con->ctrl_c = true; *len = 0; return ERROR_OK; @@ -705,7 +750,7 @@ static int gdb_output_con(struct connection *connection, const char *line) bin_size = strlen(line); hex_buffer = malloc(bin_size * 2 + 2); - if (hex_buffer == NULL) + if (!hex_buffer) return ERROR_GDB_BUFFER_TOO_SMALL; hex_buffer[0] = 'O'; @@ -728,7 +773,7 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio { struct gdb_connection *gdb_connection = connection->priv; char sig_reply[65]; - char stop_reason[20]; + char stop_reason[32]; char current_thread[25]; int sig_reply_len; int signal_var; @@ -739,7 +784,7 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "W00"); } else { struct target *ct; - if (target->rtos != NULL) { + if (target->rtos) { target->rtos->current_threadid = target->rtos->current_thread; target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct); } else { @@ -747,6 +792,7 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio } if (gdb_connection->ctrl_c) { + LOG_TARGET_DEBUG(target, "Responding with signal 2 (SIGINT) to debugger due to Ctrl-C"); signal_var = 0x2; } else signal_var = gdb_last_signal(ct); @@ -778,7 +824,7 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio } current_thread[0] = '\0'; - if (target->rtos != NULL) + if (target->rtos) snprintf(current_thread, sizeof(current_thread), "thread:%" PRIx64 ";", target->rtos->current_thread); @@ -895,7 +941,7 @@ static void gdb_frontend_halted(struct target *target, struct connection *connec */ if (gdb_connection->frontend_state == TARGET_RUNNING) { /* stop forwarding log packets! */ - log_remove_callback(gdb_log_callback, connection); + gdb_connection->output_flag = GDB_OUTPUT_NO; /* check fileio first */ if (target_get_gdb_fileio_info(target, target->fileio_info) == ERROR_OK) @@ -934,6 +980,7 @@ static int gdb_new_connection(struct connection *connection) struct target *target; int retval; int initial_ack; + static unsigned int next_unique_id = 1; target = get_target_from_connection(connection); connection->priv = gdb_connection; @@ -955,9 +1002,8 @@ static int gdb_new_connection(struct connection *connection) 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); + gdb_connection->output_flag = GDB_OUTPUT_NO; + gdb_connection->unique_index = next_unique_id++; /* output goes through gdb connection */ command_set_output_handler(connection->cmd_ctx, gdb_output, connection); @@ -969,6 +1015,21 @@ static int gdb_new_connection(struct connection *connection) breakpoint_clear_target(target); watchpoint_clear_target(target); + /* Since version 3.95 (gdb-19990504), with the exclusion of 6.5~6.8, GDB + * sends an ACK at connection with the following comment in its source code: + * "Ack any packet which the remote side has already sent." + * LLDB does the same since the first gdb-remote implementation. + * Remove the initial ACK from the incoming buffer. + */ + retval = gdb_get_char(connection, &initial_ack); + if (retval != ERROR_OK) + return retval; + + if (initial_ack != '+') + gdb_putback_char(connection, initial_ack); + + target_call_event_callbacks(target, TARGET_EVENT_GDB_ATTACH); + if (target->rtos) { /* clean previous rtos session if supported*/ if (target->rtos->type->clean) @@ -978,18 +1039,6 @@ static int gdb_new_connection(struct connection *connection) rtos_update_threads(target); } - /* remove the initial ACK from the incoming buffer */ - retval = gdb_get_char(connection, &initial_ack); - if (retval != ERROR_OK) - return retval; - - /* FIX!!!??? would we actually ever receive a + here??? - * Not observed. - */ - if (initial_ack != '+') - gdb_putback_char(connection, initial_ack); - target_call_event_callbacks(target, TARGET_EVENT_GDB_ATTACH); - if (gdb_use_memory_map) { /* Connect must fail if the memory map can't be set up correctly. * @@ -1010,20 +1059,19 @@ static int gdb_new_connection(struct connection *connection) } } - gdb_actual_connections++; - log_printf_lf(all_targets->next != NULL ? LOG_LVL_INFO : LOG_LVL_DEBUG, + log_printf_lf(all_targets->next ? LOG_LVL_INFO : LOG_LVL_DEBUG, __FILE__, __LINE__, __func__, "New GDB Connection: %d, Target %s, state: %s", - gdb_actual_connections, + gdb_connection->unique_index, target_name(target), target_state_name(target)); if (!target_was_examined(target)) { LOG_ERROR("Target %s not examined yet, refuse gdb connection %d!", - target_name(target), gdb_actual_connections); - gdb_actual_connections--; + target_name(target), gdb_connection->unique_index); return ERROR_TARGET_NOT_EXAMINED; } + gdb_actual_connections++; if (target->state != TARGET_HALTED) LOG_WARNING("GDB connection %d on target %s not halted", @@ -1036,6 +1084,8 @@ static int gdb_new_connection(struct connection *connection) * register callback to be informed about target events */ target_register_event_callback(gdb_target_callback_event_handler, connection); + log_add_callback(gdb_log_callback, connection); + return ERROR_OK; } @@ -1052,7 +1102,8 @@ static int gdb_connection_closed(struct connection *connection) log_remove_callback(gdb_log_callback, connection); gdb_actual_connections--; - LOG_DEBUG("GDB Close, Target: %s, state: %s, gdb_actual_connections=%d", + LOG_DEBUG("{%d} GDB Close, Target: %s, state: %s, gdb_actual_connections=%d", + gdb_connection->unique_index, target_name(target), target_state_name(target), gdb_actual_connections); @@ -1164,6 +1215,27 @@ static void gdb_target_to_reg(struct target *target, } } +/* get register value if needed and fill the buffer accordingly */ +static int gdb_get_reg_value_as_str(struct target *target, char *tstr, struct reg *reg) +{ + int retval = ERROR_OK; + + if (!reg->valid) + retval = reg->type->get(reg); + + const unsigned int len = DIV_ROUND_UP(reg->size, 8) * 2; + switch (retval) { + case ERROR_OK: + gdb_str_to_target(target, tstr, reg); + return ERROR_OK; + case ERROR_TARGET_RESOURCE_NOT_AVAILABLE: + memset(tstr, 'x', len); + tstr[len] = '\0'; + return ERROR_OK; + } + return ERROR_FAIL; +} + static int gdb_get_registers_packet(struct connection *connection, char const *packet, int packet_size) { @@ -1180,7 +1252,7 @@ static int gdb_get_registers_packet(struct connection *connection, LOG_DEBUG("-"); #endif - if ((target->rtos != NULL) && (ERROR_OK == rtos_get_gdb_reg_list(connection))) + if ((target->rtos) && (rtos_get_gdb_reg_list(connection) == ERROR_OK)) return ERROR_OK; retval = target_get_gdb_reg_list(target, ®_list, ®_list_size, @@ -1189,7 +1261,7 @@ static int gdb_get_registers_packet(struct connection *connection, return gdb_error(connection, retval); for (i = 0; i < reg_list_size; i++) { - if (reg_list[i] == NULL || reg_list[i]->exist == false) + if (!reg_list[i] || reg_list[i]->exist == false || reg_list[i]->hidden) continue; reg_packet_size += DIV_ROUND_UP(reg_list[i]->size, 8) * 2; } @@ -1197,24 +1269,19 @@ 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) + if (!reg_packet) return ERROR_FAIL; reg_packet_p = reg_packet; for (i = 0; i < reg_list_size; i++) { - if (reg_list[i] == NULL || reg_list[i]->exist == false) + if (!reg_list[i] || reg_list[i]->exist == false || reg_list[i]->hidden) continue; - if (!reg_list[i]->valid) { - retval = reg_list[i]->type->get(reg_list[i]); - if (retval != ERROR_OK && gdb_report_register_access_error) { - LOG_DEBUG("Couldn't get register %s.", reg_list[i]->name); - free(reg_packet); - free(reg_list); - return gdb_error(connection, retval); - } + if (gdb_get_reg_value_as_str(target, reg_packet_p, reg_list[i]) != ERROR_OK) { + free(reg_packet); + free(reg_list); + return gdb_error(connection, retval); } - gdb_str_to_target(target, reg_packet_p, reg_list[i]); reg_packet_p += DIV_ROUND_UP(reg_list[i]->size, 8) * 2; } @@ -1266,6 +1333,8 @@ static int gdb_set_registers_packet(struct connection *connection, packet_p = packet; for (i = 0; i < reg_list_size; i++) { uint8_t *bin_buf; + if (!reg_list[i] || !reg_list[i]->exist || reg_list[i]->hidden) + continue; int chars = (DIV_ROUND_UP(reg_list[i]->size, 8) * 2); if (packet_p + chars > packet + packet_size) @@ -1310,7 +1379,7 @@ static int gdb_get_register_packet(struct connection *connection, LOG_DEBUG("-"); #endif - if ((target->rtos != NULL) && (ERROR_OK == rtos_get_gdb_reg(connection, reg_num))) + if ((target->rtos) && (rtos_get_gdb_reg(connection, reg_num) == ERROR_OK)) return ERROR_OK; retval = target_get_gdb_reg_list_noread(target, ®_list, ®_list_size, @@ -1318,23 +1387,19 @@ static int gdb_get_register_packet(struct connection *connection, if (retval != ERROR_OK) return gdb_error(connection, retval); - if (reg_list_size <= reg_num) { - LOG_ERROR("gdb requested a non-existing register"); + if ((reg_list_size <= reg_num) || !reg_list[reg_num] || + !reg_list[reg_num]->exist || reg_list[reg_num]->hidden) { + LOG_ERROR("gdb requested a non-existing register (reg_num=%d)", reg_num); return ERROR_SERVER_REMOTE_CLOSED; } - if (!reg_list[reg_num]->valid) { - retval = reg_list[reg_num]->type->get(reg_list[reg_num]); - if (retval != ERROR_OK && gdb_report_register_access_error) { - LOG_DEBUG("Couldn't get register %s.", reg_list[reg_num]->name); - free(reg_list); - return gdb_error(connection, retval); - } - } - - reg_packet = malloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2 + 1); /* plus one for string termination null */ + reg_packet = calloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2 + 1, 1); /* plus one for string termination null */ - gdb_str_to_target(target, reg_packet, reg_list[reg_num]); + if (gdb_get_reg_value_as_str(target, reg_packet, reg_list[reg_num]) != ERROR_OK) { + free(reg_packet); + free(reg_list); + return gdb_error(connection, retval); + } gdb_put_packet(connection, reg_packet, DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2); @@ -1366,8 +1431,8 @@ static int gdb_set_register_packet(struct connection *connection, uint8_t *bin_buf = malloc(chars / 2); gdb_target_to_reg(target, separator + 1, chars, bin_buf); - if ((target->rtos != NULL) && - (ERROR_OK == rtos_set_reg(connection, reg_num, bin_buf))) { + if ((target->rtos) && + (rtos_set_reg(connection, reg_num, bin_buf) == ERROR_OK)) { free(bin_buf); gdb_put_packet(connection, "OK", 2); return ERROR_OK; @@ -1380,8 +1445,9 @@ static int gdb_set_register_packet(struct connection *connection, return gdb_error(connection, retval); } - if (reg_list_size <= reg_num) { - LOG_ERROR("gdb requested a non-existing register"); + if ((reg_list_size <= reg_num) || !reg_list[reg_num] || + !reg_list[reg_num]->exist || reg_list[reg_num]->hidden) { + LOG_ERROR("gdb requested a non-existing register (reg_num=%d)", reg_num); free(bin_buf); free(reg_list); return ERROR_SERVER_REMOTE_CLOSED; @@ -1424,9 +1490,6 @@ static int gdb_error(struct connection *connection, int retval) return ERROR_OK; } -/* We don't have to worry about the default 2 second timeout for GDB packets, - * because GDB breaks up large memory reads into smaller reads. - */ static int gdb_read_memory_packet(struct connection *connection, char const *packet, int packet_size) { @@ -1462,7 +1525,11 @@ static int gdb_read_memory_packet(struct connection *connection, LOG_DEBUG("addr: 0x%16.16" PRIx64 ", len: 0x%8.8" PRIx32 "", addr, len); - retval = target_read_buffer(target, addr, len, buffer); + retval = ERROR_NOT_IMPLEMENTED; + if (target->rtos) + retval = rtos_read_buffer(target, addr, len, buffer); + if (retval == ERROR_NOT_IMPLEMENTED) + retval = target_read_buffer(target, addr, len, buffer); if ((retval != ERROR_OK) && !gdb_report_data_abort) { /* TODO : Here we have to lie and send back all zero's lest stack traces won't work. @@ -1533,7 +1600,11 @@ static int gdb_write_memory_packet(struct connection *connection, if (unhexify(buffer, separator, len) != len) LOG_ERROR("unable to decode memory packet"); - retval = target_write_buffer(target, addr, len, buffer); + retval = ERROR_NOT_IMPLEMENTED; + if (target->rtos) + retval = rtos_write_buffer(target, addr, len, buffer); + if (retval == ERROR_NOT_IMPLEMENTED) + retval = target_write_buffer(target, addr, len, buffer); if (retval == ERROR_OK) gdb_put_packet(connection, "OK", 2); @@ -1602,7 +1673,12 @@ static int gdb_write_memory_binary_packet(struct connection *connection, if (len) { LOG_DEBUG("addr: 0x%" PRIx64 ", len: 0x%8.8" PRIx32 "", addr, len); - retval = target_write_buffer(target, addr, len, (uint8_t *)separator); + retval = ERROR_NOT_IMPLEMENTED; + if (target->rtos) + retval = rtos_write_buffer(target, addr, len, (uint8_t *)separator); + if (retval == ERROR_NOT_IMPLEMENTED) + retval = target_write_buffer(target, addr, len, (uint8_t *)separator); + if (retval != ERROR_OK) gdb_connection->mem_write_error = true; } @@ -1700,7 +1776,10 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection, case 1: if (packet[0] == 'Z') { retval = breakpoint_add(target, address, size, bp_type); - if (retval != ERROR_OK) { + if (retval == ERROR_NOT_IMPLEMENTED) { + /* Send empty reply to report that breakpoints of this type are not supported */ + gdb_put_packet(connection, "", 0); + } else if (retval != ERROR_OK) { retval = gdb_error(connection, retval); if (retval != ERROR_OK) return retval; @@ -1716,8 +1795,11 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection, case 4: { if (packet[0] == 'Z') { - retval = watchpoint_add(target, address, size, wp_type, 0, 0xffffffffu); - if (retval != ERROR_OK) { + retval = watchpoint_add(target, address, size, wp_type, 0, WATCHPOINT_IGNORE_DATA_VALUE_MASK); + if (retval == ERROR_NOT_IMPLEMENTED) { + /* Send empty reply to report that watchpoints of this type are not supported */ + gdb_put_packet(connection, "", 0); + } else if (retval != ERROR_OK) { retval = gdb_error(connection, retval); if (retval != ERROR_OK) return retval; @@ -1747,14 +1829,14 @@ static __attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 5, 6))) void xml_printf( int first = 1; for (;; ) { - if ((*xml == NULL) || (!first)) { + if ((!*xml) || (!first)) { /* start by 0 to exercise all the code paths. * Need minimum 2 bytes to fit 1 char and 0 terminator. */ *size = *size * 2 + 2; char *t = *xml; *xml = realloc(*xml, *size); - if (*xml == NULL) { + if (!*xml) { free(t); *retval = ERROR_SERVER_REMOTE_CLOSED; return; @@ -1779,7 +1861,7 @@ static int decode_xfer_read(char const *buf, char **annex, int *ofs, unsigned in { /* Locate the annex. */ const char *annex_end = strchr(buf, ':'); - if (annex_end == NULL) + if (!annex_end) return ERROR_FAIL; /* After the read marker and annex, qXfer looks like a @@ -1793,9 +1875,9 @@ static int decode_xfer_read(char const *buf, char **annex, int *ofs, unsigned in *len = strtoul(separator + 1, NULL, 16); /* Extract the annex if needed */ - if (annex != NULL) { + if (annex) { *annex = strndup(buf, annex_end - buf); - if (*annex == NULL) + if (!*annex) return ERROR_FAIL; } @@ -2014,7 +2096,7 @@ static int lookup_add_arch_defined_types(char const **arch_defined_types_list[], { int tbl_sz = *num_arch_defined_types; - if (type_id != NULL && (strcmp(type_id, ""))) { + if (type_id && (strcmp(type_id, ""))) { for (int j = 0; j < (tbl_sz + 1); j++) { if (!((*arch_defined_types_list)[j])) { (*arch_defined_types_list)[tbl_sz++] = type_id; @@ -2057,7 +2139,7 @@ static int gdb_generate_reg_type_description(struct target *target, } else if (type->type_class == REG_TYPE_CLASS_UNION) { struct reg_data_type_union_field *field; field = type->reg_type_union->fields; - while (field != NULL) { + while (field) { struct reg_data_type *data_type = field->type; if (data_type->type == REG_TYPE_ARCH_DEFINED) { if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id, @@ -2077,7 +2159,7 @@ static int gdb_generate_reg_type_description(struct target *target, type->id); field = type->reg_type_union->fields; - while (field != NULL) { + while (field) { xml_printf(&retval, tdesc, pos, size, "\n", field->name, field->type->id); @@ -2099,7 +2181,7 @@ static int gdb_generate_reg_type_description(struct target *target, xml_printf(&retval, tdesc, pos, size, "\n", type->id, type->reg_type_struct->size); - while (field != NULL) { + while (field) { xml_printf(&retval, tdesc, pos, size, "\n", field->name, field->bitfield->start, field->bitfield->end, @@ -2108,7 +2190,7 @@ static int gdb_generate_reg_type_description(struct target *target, field = field->next; } } else { - while (field != NULL) { + while (field) { struct reg_data_type *data_type = field->type; if (data_type->type == REG_TYPE_ARCH_DEFINED) { if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id, @@ -2125,7 +2207,7 @@ static int gdb_generate_reg_type_description(struct target *target, xml_printf(&retval, tdesc, pos, size, "\n", type->id); - while (field != NULL) { + while (field) { xml_printf(&retval, tdesc, pos, size, "\n", field->name, field->type->id); @@ -2147,7 +2229,7 @@ static int gdb_generate_reg_type_description(struct target *target, struct reg_data_type_flags_field *field; field = type->reg_type_flags->fields; - while (field != NULL) { + while (field) { xml_printf(&retval, tdesc, pos, size, "\n", field->name, field->bitfield->start, field->bitfield->end, @@ -2176,11 +2258,11 @@ static int get_reg_features_list(struct target *target, char const **feature_lis *feature_list = calloc(1, sizeof(char *)); for (int i = 0; i < reg_list_size; i++) { - if (reg_list[i]->exist == false) + if (reg_list[i]->exist == false || reg_list[i]->hidden) continue; - if (reg_list[i]->feature != NULL - && reg_list[i]->feature->name != NULL + if (reg_list[i]->feature + && reg_list[i]->feature->name && (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 @@ -2206,6 +2288,123 @@ static int get_reg_features_list(struct target *target, char const **feature_lis return ERROR_OK; } +/* Create a register list that's the union of all the registers of the SMP + * group this target is in. If the target is not part of an SMP group, this + * returns the same as target_get_gdb_reg_list_noread(). + */ +static int smp_reg_list_noread(struct target *target, + struct reg **combined_list[], int *combined_list_size, + enum target_register_class reg_class) +{ + if (!target->smp) + return target_get_gdb_reg_list_noread(target, combined_list, + combined_list_size, REG_CLASS_ALL); + + unsigned int combined_allocated = 256; + struct reg **local_list = malloc(combined_allocated * sizeof(struct reg *)); + if (!local_list) { + LOG_ERROR("malloc(%zu) failed", combined_allocated * sizeof(struct reg *)); + return ERROR_FAIL; + } + unsigned int local_list_size = 0; + + struct target_list *head; + foreach_smp_target(head, target->smp_targets) { + if (!target_was_examined(head->target)) + continue; + + struct reg **reg_list = NULL; + int reg_list_size; + int result = target_get_gdb_reg_list_noread(head->target, ®_list, + ®_list_size, reg_class); + if (result != ERROR_OK) { + free(local_list); + return result; + } + for (int i = 0; i < reg_list_size; i++) { + bool found = false; + struct reg *a = reg_list[i]; + if (a->exist) { + /* Nested loop makes this O(n^2), but this entire function with + * 5 RISC-V targets takes just 2ms on my computer. Fast enough + * for me. */ + for (unsigned int j = 0; j < local_list_size; j++) { + struct reg *b = local_list[j]; + if (!strcmp(a->name, b->name)) { + found = true; + if (a->size != b->size) { + LOG_ERROR("SMP register %s is %d bits on one " + "target, but %d bits on another target.", + a->name, a->size, b->size); + free(reg_list); + free(local_list); + return ERROR_FAIL; + } + break; + } + } + if (!found) { + LOG_DEBUG("[%s] %s not found in combined list", target_name(target), a->name); + if (local_list_size >= combined_allocated) { + combined_allocated *= 2; + local_list = realloc(local_list, combined_allocated * sizeof(struct reg *)); + if (!local_list) { + LOG_ERROR("realloc(%zu) failed", combined_allocated * sizeof(struct reg *)); + free(reg_list); + return ERROR_FAIL; + } + } + local_list[local_list_size] = a; + local_list_size++; + } + } + } + free(reg_list); + } + + if (local_list_size == 0) { + LOG_ERROR("Unable to get register list"); + free(local_list); + return ERROR_FAIL; + } + + /* Now warn the user about any registers that weren't found in every target. */ + foreach_smp_target(head, target->smp_targets) { + if (!target_was_examined(head->target)) + continue; + + struct reg **reg_list = NULL; + int reg_list_size; + int result = target_get_gdb_reg_list_noread(head->target, ®_list, + ®_list_size, reg_class); + if (result != ERROR_OK) { + free(local_list); + return result; + } + for (unsigned int i = 0; i < local_list_size; i++) { + bool found = false; + struct reg *a = local_list[i]; + for (int j = 0; j < reg_list_size; j++) { + struct reg *b = reg_list[j]; + if (b->exist && !strcmp(a->name, b->name)) { + found = true; + break; + } + } + if (!found) { + LOG_WARNING("Register %s does not exist in %s, which is part of an SMP group where " + "this register does exist.", + a->name, target_name(head->target)); + } + } + free(reg_list); + } + + *combined_list = local_list; + *combined_list_size = local_list_size; + return ERROR_OK; +} + static int gdb_generate_target_description(struct target *target, char **tdesc_out) { int retval = ERROR_OK; @@ -2219,8 +2418,8 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o int size = 0; - retval = target_get_gdb_reg_list_noread(target, ®_list, - ®_list_size, REG_CLASS_ALL); + retval = smp_reg_list_noread(target, ®_list, ®_list_size, + REG_CLASS_ALL); if (retval != ERROR_OK) { LOG_ERROR("get register list failed"); @@ -2252,12 +2451,12 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o /* generate architecture element if supported by target */ architecture = target_get_gdb_arch(target); - if (architecture != NULL) + if (architecture) xml_printf(&retval, &tdesc, &pos, &size, "%s\n", architecture); /* generate target description according to register list */ - if (features != NULL) { + if (features) { while (features[current_feature]) { char const **arch_defined_types = NULL; int num_arch_defined_types = 0; @@ -2270,14 +2469,14 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o int i; for (i = 0; i < reg_list_size; i++) { - if (reg_list[i]->exist == false) + if (reg_list[i]->exist == false || reg_list[i]->hidden) continue; if (strcmp(reg_list[i]->feature->name, features[current_feature])) continue; const char *type_str; - if (reg_list[i]->reg_data_type != NULL) { + if (reg_list[i]->reg_data_type) { if (reg_list[i]->reg_data_type->type == REG_TYPE_ARCH_DEFINED) { /* generate group != NULL) + if (reg_list[i]->group) xml_printf(&retval, &tdesc, &pos, &size, " group=\"%s\"", reg_list[i]->group); @@ -2349,7 +2548,7 @@ error: static int gdb_get_target_description_chunk(struct target *target, struct target_desc_format *target_desc, char **chunk, int32_t offset, uint32_t length) { - if (target_desc == NULL) { + if (!target_desc) { LOG_ERROR("Unable to Generate Target Description"); return ERROR_FAIL; } @@ -2357,7 +2556,7 @@ static int gdb_get_target_description_chunk(struct target *target, struct target char *tdesc = target_desc->tdesc; uint32_t tdesc_length = target_desc->tdesc_length; - if (tdesc == NULL) { + if (!tdesc) { int retval = gdb_generate_target_description(target, &tdesc); if (retval != ERROR_OK) { LOG_ERROR("Unable to Generate Target Description"); @@ -2375,7 +2574,7 @@ static int gdb_get_target_description_chunk(struct target *target, struct target transfer_type = 'l'; *chunk = malloc(length + 2); - if (*chunk == NULL) { + if (!*chunk) { LOG_ERROR("Unable to allocate memory"); return ERROR_FAIL; } @@ -2457,22 +2656,28 @@ static int gdb_generate_thread_list(struct target *target, char **thread_list_ou "\n" "\n"); - if (rtos != NULL) { + if (rtos) { 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) + xml_printf(&retval, &thread_list, &pos, &size, + "", + thread_detail->threadid, + thread_detail->thread_name_str); + else + xml_printf(&retval, &thread_list, &pos, &size, + "", thread_detail->threadid); - if (thread_detail->thread_name_str != NULL) + if (thread_detail->thread_name_str) 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) + if (thread_detail->extra_info_str) { + if (thread_detail->thread_name_str) xml_printf(&retval, &thread_list, &pos, &size, ", "); xml_printf(&retval, &thread_list, &pos, &size, @@ -2498,7 +2703,7 @@ static int gdb_generate_thread_list(struct target *target, char **thread_list_ou 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) { + if (!*thread_list) { int retval = gdb_generate_thread_list(target, thread_list); if (retval != ERROR_OK) { LOG_ERROR("Unable to Generate Thread List"); @@ -2516,11 +2721,11 @@ static int gdb_get_thread_list_chunk(struct target *target, char **thread_list, transfer_type = 'l'; *chunk = malloc(length + 2 + 3); - /* Allocating extra 3 bytes prevents false positive valgrind report + /* Allocating extra 3 bytes prevents false positive valgrind report * of strlen(chunk) word access: * Invalid read of size 4 * Address 0x4479934 is 44 bytes inside a block of size 45 alloc'd */ - if (*chunk == NULL) { + if (!*chunk) { LOG_ERROR("Unable to allocate memory"); return ERROR_FAIL; } @@ -2547,22 +2752,75 @@ static int gdb_query_packet(struct connection *connection, if (strncmp(packet, "qRcmd,", 6) == 0) { if (packet_size > 6) { + Jim_Interp *interp = cmd_ctx->interp; char *cmd; cmd = malloc((packet_size - 6) / 2 + 1); 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 */ - log_add_callback(gdb_log_callback, connection); + gdb_connection->output_flag = GDB_OUTPUT_ALL; target_call_timer_callbacks_now(); /* some commands need to know the GDB connection, make note of current * GDB connection. */ current_gdb_connection = gdb_connection; - command_run_line(cmd_ctx, cmd); + + struct target *saved_target_override = cmd_ctx->current_target_override; + cmd_ctx->current_target_override = NULL; + + struct command_context *old_context = Jim_GetAssocData(interp, "context"); + Jim_DeleteAssocData(interp, "context"); + int retval = Jim_SetAssocData(interp, "context", NULL, cmd_ctx); + if (retval == JIM_OK) { + retval = Jim_EvalObj(interp, Jim_NewStringObj(interp, cmd, -1)); + Jim_DeleteAssocData(interp, "context"); + } + int inner_retval = Jim_SetAssocData(interp, "context", NULL, old_context); + if (retval == JIM_OK) + retval = inner_retval; + + cmd_ctx->current_target_override = saved_target_override; + current_gdb_connection = NULL; target_call_timer_callbacks_now(); - log_remove_callback(gdb_log_callback, connection); + gdb_connection->output_flag = GDB_OUTPUT_NO; free(cmd); + if (retval == JIM_RETURN) + retval = interp->returnCode; + int lenmsg; + const char *cretmsg = Jim_GetString(Jim_GetResult(interp), &lenmsg); + char *retmsg; + if (lenmsg && cretmsg[lenmsg - 1] != '\n') { + retmsg = alloc_printf("%s\n", cretmsg); + lenmsg++; + } else { + retmsg = strdup(cretmsg); + } + if (!retmsg) + return ERROR_GDB_BUFFER_TOO_SMALL; + + if (retval == JIM_OK) { + if (lenmsg) { + char *hex_buffer = malloc(lenmsg * 2 + 1); + if (!hex_buffer) { + free(retmsg); + return ERROR_GDB_BUFFER_TOO_SMALL; + } + + size_t pkt_len = hexify(hex_buffer, (const uint8_t *)retmsg, lenmsg, + lenmsg * 2 + 1); + gdb_put_packet(connection, hex_buffer, pkt_len); + free(hex_buffer); + } else { + gdb_put_packet(connection, "OK", 2); + } + } else { + if (lenmsg) + gdb_output_con(connection, retmsg); + gdb_send_error(connection, retval); + } + free(retmsg); + return ERROR_OK; } gdb_put_packet(connection, "OK", 2); return ERROR_OK; @@ -2587,7 +2845,9 @@ static int gdb_query_packet(struct connection *connection, len = strtoul(separator + 1, NULL, 16); + gdb_connection->output_flag = GDB_OUTPUT_NOTIF; retval = target_checksum_memory(target, addr, len, &checksum); + gdb_connection->output_flag = GDB_OUTPUT_NO; if (retval == ERROR_OK) { snprintf(gdb_reply, 10, "C%8.8" PRIx32 "", checksum); @@ -2710,13 +2970,19 @@ static int gdb_query_packet(struct connection *connection, gdb_connection->noack_mode = 1; gdb_put_packet(connection, "OK", 2); return ERROR_OK; + } else if (target->type->gdb_query_custom) { + char *buffer = NULL; + int ret = target->type->gdb_query_custom(target, packet, &buffer); + gdb_put_packet(connection, buffer, strlen(buffer)); + return ret; } gdb_put_packet(connection, "", 0); return ERROR_OK; } -static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet, int packet_size) +static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet, + __attribute__((unused)) int packet_size) { struct gdb_connection *gdb_connection = connection->priv; struct target *target = get_target_from_connection(connection); @@ -2725,7 +2991,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p /* query for vCont supported */ if (parse[0] == '?') { - if (target->type->step != NULL) { + if (target->type->step) { /* gdb doesn't accept c without C and s without S */ gdb_put_packet(connection, "vCont;c;C;s;S", 13); return true; @@ -2735,14 +3001,13 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p if (parse[0] == ';') { ++parse; - --packet_size; } /* simple case, a continue packet */ if (parse[0] == 'c') { gdb_running_type = 'c'; LOG_DEBUG("target %s continue", target_name(target)); - log_add_callback(gdb_log_callback, connection); + gdb_connection->output_flag = GDB_OUTPUT_ALL; retval = target_resume(target, 1, 0, 0, 0); if (retval == ERROR_TARGET_NOT_HALTED) LOG_INFO("target %s was not halted when resume was requested", target_name(target)); @@ -2770,128 +3035,123 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p gdb_running_type = 's'; bool fake_step = false; - if (strncmp(parse, "s:", 2) == 0) { - struct target *ct = target; - int current_pc = 1; - int64_t thread_id; + struct target *ct = target; + int current_pc = 1; + int64_t thread_id; + parse++; + if (parse[0] == ':') { char *endp; - - parse += 2; - packet_size -= 2; - + parse++; thread_id = strtoll(parse, &endp, 16); - if (endp != NULL) { - packet_size -= endp - parse; + if (endp) { parse = endp; } + } else { + thread_id = 0; + } - if (target->rtos != NULL) { - /* FIXME: why is this necessary? rtos state should be up-to-date here already! */ - rtos_update_threads(target); + if (target->rtos) { + /* FIXME: why is this necessary? rtos state should be up-to-date here already! */ + rtos_update_threads(target); - target->rtos->gdb_target_for_threadid(connection, thread_id, &ct); + target->rtos->gdb_target_for_threadid(connection, thread_id, &ct); - /* - * check if the thread to be stepped is the current rtos thread - * if not, we must fake the step - */ - if (target->rtos->current_thread != thread_id) - fake_step = true; - } + /* + * check if the thread to be stepped is the current rtos thread + * if not, we must fake the step + */ + if (target->rtos->current_thread != thread_id) + fake_step = true; + } - if (parse[0] == ';') { - ++parse; - --packet_size; + if (parse[0] == ';') { + ++parse; - if (parse[0] == 'c') { + if (parse[0] == 'c') { + parse += 1; + + /* check if thread-id follows */ + if (parse[0] == ':') { + int64_t tid; parse += 1; - /* check if thread-id follows */ - if (parse[0] == ':') { - int64_t tid; - parse += 1; - - tid = strtoll(parse, &endp, 16); - if (tid == thread_id) { - /* - * Special case: only step a single thread (core), - * keep the other threads halted. Currently, only - * aarch64 target understands it. Other target types don't - * care (nobody checks the actual value of 'current') - * and it doesn't really matter. This deserves - * a symbolic constant and a formal interface documentation - * at a later time. - */ - LOG_DEBUG("request to step current core only"); - /* uncomment after checking that indeed other targets are safe */ - /*current_pc = 2;*/ - } + tid = strtoll(parse, NULL, 16); + if (tid == thread_id) { + /* + * Special case: only step a single thread (core), + * keep the other threads halted. Currently, only + * aarch64 target understands it. Other target types don't + * care (nobody checks the actual value of 'current') + * and it doesn't really matter. This deserves + * a symbolic constant and a formal interface documentation + * at a later time. + */ + LOG_DEBUG("request to step current core only"); + /* uncomment after checking that indeed other targets are safe */ + /*current_pc = 2;*/ } } } + } - LOG_DEBUG("target %s single-step thread %"PRIx64, target_name(ct), thread_id); - log_add_callback(gdb_log_callback, connection); - target_call_event_callbacks(ct, TARGET_EVENT_GDB_START); - - /* - * work around an annoying gdb behaviour: when the current thread - * is changed in gdb, it assumes that the target can follow and also - * make the thread current. This is an assumption that cannot hold - * for a real target running a multi-threading OS. We just fake - * the step to not trigger an internal error in gdb. See - * https://sourceware.org/bugzilla/show_bug.cgi?id=22925 for details - */ - if (fake_step) { - int sig_reply_len; - char sig_reply[128]; - - LOG_DEBUG("fake step thread %"PRIx64, thread_id); + LOG_DEBUG("target %s single-step thread %"PRIx64, target_name(ct), thread_id); + gdb_connection->output_flag = GDB_OUTPUT_ALL; + target_call_event_callbacks(ct, TARGET_EVENT_GDB_START); - sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), - "T05thread:%016"PRIx64";", thread_id); + /* + * work around an annoying gdb behaviour: when the current thread + * is changed in gdb, it assumes that the target can follow and also + * make the thread current. This is an assumption that cannot hold + * for a real target running a multi-threading OS. We just fake + * the step to not trigger an internal error in gdb. See + * https://sourceware.org/bugzilla/show_bug.cgi?id=22925 for details + */ + if (fake_step) { + int sig_reply_len; + char sig_reply[128]; - gdb_put_packet(connection, sig_reply, sig_reply_len); - log_remove_callback(gdb_log_callback, connection); + LOG_DEBUG("fake step thread %"PRIx64, thread_id); - return true; - } + sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), + "T05thread:%016"PRIx64";", thread_id); - /* support for gdb_sync command */ - if (gdb_connection->sync) { - gdb_connection->sync = false; - if (ct->state == TARGET_HALTED) { - LOG_DEBUG("stepi ignored. GDB will now fetch the register state " - "from the target."); - gdb_sig_halted(connection); - log_remove_callback(gdb_log_callback, connection); - } else - gdb_connection->frontend_state = TARGET_RUNNING; - return true; - } + gdb_put_packet(connection, sig_reply, sig_reply_len); + gdb_connection->output_flag = GDB_OUTPUT_NO; - retval = target_step(ct, current_pc, 0, 0); - if (retval == ERROR_TARGET_NOT_HALTED) - LOG_INFO("target %s was not halted when step was requested", target_name(ct)); + return true; + } - /* if step was successful send a reply back to gdb */ - if (retval == ERROR_OK) { - retval = target_poll(ct); - if (retval != ERROR_OK) - LOG_DEBUG("error polling target %s after successful step", target_name(ct)); - /* send back signal information */ - gdb_signal_reply(ct, connection); - /* stop forwarding log packets! */ - log_remove_callback(gdb_log_callback, connection); + /* support for gdb_sync command */ + if (gdb_connection->sync) { + gdb_connection->sync = false; + if (ct->state == TARGET_HALTED) { + LOG_DEBUG("stepi ignored. GDB will now fetch the register state " + "from the target."); + gdb_sig_halted(connection); + gdb_connection->output_flag = GDB_OUTPUT_NO; } else gdb_connection->frontend_state = TARGET_RUNNING; - } else { - LOG_ERROR("Unknown vCont packet"); - return false; + return true; } + + retval = target_step(ct, current_pc, 0, 0); + if (retval == ERROR_TARGET_NOT_HALTED) + LOG_INFO("target %s was not halted when step was requested", target_name(ct)); + + /* if step was successful send a reply back to gdb */ + if (retval == ERROR_OK) { + retval = target_poll(ct); + if (retval != ERROR_OK) + LOG_DEBUG("error polling target %s after successful step", target_name(ct)); + /* send back signal information */ + gdb_signal_reply(ct, connection); + /* stop forwarding log packets! */ + gdb_connection->output_flag = GDB_OUTPUT_NO; + } else + gdb_connection->frontend_state = TARGET_RUNNING; return true; } - + LOG_ERROR("Unknown vCont packet"); return false; } @@ -2903,7 +3163,7 @@ static char *next_hex_encoded_field(const char **str, char sep) return NULL; const char *end = strchr(hex, sep); - if (end == NULL) + if (!end) hexlen = strlen(hex); else hexlen = end - hex; @@ -2916,7 +3176,7 @@ static char *next_hex_encoded_field(const char **str, char sep) size_t count = hexlen / 2; char *decoded = malloc(count + 1); - if (decoded == NULL) + if (!decoded) return NULL; size_t converted = unhexify((void *)decoded, hex, count); @@ -2961,16 +3221,18 @@ static bool gdb_handle_vrun_packet(struct connection *connection, const char *pa free(next_hex_encoded_field(&parse, ';')); char *cmdline = next_hex_encoded_field(&parse, ';'); - char *arg; - while (cmdline != NULL && (arg = next_hex_encoded_field(&parse, ';')) != NULL) { + while (cmdline) { + char *arg = next_hex_encoded_field(&parse, ';'); + if (!arg) + break; char *new_cmdline = alloc_printf("%s %s", cmdline, arg); free(cmdline); free(arg); cmdline = new_cmdline; } - if (cmdline != NULL) { - if (target->semihosting != NULL) { + if (cmdline) { + if (target->semihosting) { LOG_INFO("GDB set inferior command line to '%s'", cmdline); free(target->semihosting->cmdline); target->semihosting->cmdline = cmdline; @@ -3024,7 +3286,7 @@ static int gdb_v_packet(struct connection *connection, } if (strncmp(packet, "vFlashErase:", 12) == 0) { - unsigned long addr; + target_addr_t addr; unsigned long length; char const *parse = packet + 12; @@ -3033,7 +3295,7 @@ static int gdb_v_packet(struct connection *connection, return ERROR_SERVER_REMOTE_CLOSED; } - addr = strtoul(parse, (char **)&parse, 16); + addr = strtoull(parse, (char **)&parse, 16); if (*(parse++) != ',' || *parse == '\0') { LOG_ERROR("incomplete vFlashErase packet received, dropping connection"); @@ -3081,7 +3343,7 @@ static int gdb_v_packet(struct connection *connection, if (strncmp(packet, "vFlashWrite:", 12) == 0) { int retval; - unsigned long addr; + target_addr_t addr; unsigned long length; char const *parse = packet + 12; @@ -3089,7 +3351,8 @@ static int gdb_v_packet(struct connection *connection, LOG_ERROR("incomplete vFlashErase packet received, dropping connection"); return ERROR_SERVER_REMOTE_CLOSED; } - addr = strtoul(parse, (char **)&parse, 16); + + addr = strtoull(parse, (char **)&parse, 16); if (*(parse++) != ':') { LOG_ERROR("incomplete vFlashErase packet received, dropping connection"); return ERROR_SERVER_REMOTE_CLOSED; @@ -3097,7 +3360,7 @@ static int gdb_v_packet(struct connection *connection, length = packet_size - (parse - packet); /* create a new image if there isn't already one */ - if (gdb_connection->vflash_image == NULL) { + if (!gdb_connection->vflash_image) { gdb_connection->vflash_image = malloc(sizeof(struct image)); image_open(gdb_connection->vflash_image, "", "build"); } @@ -3116,6 +3379,13 @@ static int gdb_v_packet(struct connection *connection, if (strncmp(packet, "vFlashDone", 10) == 0) { uint32_t written; + /* GDB command 'flash-erase' does not send a vFlashWrite, + * so nothing to write here. */ + if (!gdb_connection->vflash_image) { + gdb_put_packet(connection, "OK", 2); + return ERROR_OK; + } + /* process the flashing buffer. No need to erase as GDB * always issues a vFlashErase first. */ target_call_event_callbacks(target, @@ -3210,6 +3480,10 @@ static void gdb_log_callback(void *priv, const char *file, unsigned line, struct connection *connection = priv; struct gdb_connection *gdb_con = connection->priv; + if (gdb_con->output_flag != GDB_OUTPUT_ALL) + /* No out allowed */ + return; + if (gdb_con->busy) { /* do not reply this using the O packet */ return; @@ -3259,20 +3533,10 @@ static int gdb_input_inner(struct connection *connection) /* terminate with zero */ gdb_packet_buffer[packet_size] = '\0'; - if (LOG_LEVEL_IS(LOG_LVL_DEBUG)) { - if (packet[0] == 'X') { - /* binary packets spew junk into the debug log stream */ - char buf[50]; - int x; - for (x = 0; (x < 49) && (packet[x] != ':'); x++) - buf[x] = packet[x]; - buf[x] = 0; - LOG_DEBUG("received packet: '%s:'", buf); - } else - LOG_DEBUG("received packet: '%s'", packet); - } - if (packet_size > 0) { + + gdb_log_incoming_packet(connection, gdb_packet_buffer); + retval = ERROR_OK; switch (packet[0]) { case 'T': /* Is thread alive? */ @@ -3301,10 +3565,14 @@ static int gdb_input_inner(struct connection *connection) retval = gdb_set_register_packet(connection, packet, packet_size); break; case 'm': + gdb_con->output_flag = GDB_OUTPUT_NOTIF; retval = gdb_read_memory_packet(connection, packet, packet_size); + gdb_con->output_flag = GDB_OUTPUT_NO; break; case 'M': + gdb_con->output_flag = GDB_OUTPUT_NOTIF; retval = gdb_write_memory_packet(connection, packet, packet_size); + gdb_con->output_flag = GDB_OUTPUT_NO; break; case 'z': case 'Z': @@ -3315,7 +3583,7 @@ static int gdb_input_inner(struct connection *connection) /* '?' is sent after the eventual '!' */ if (!warn_use_ext && !gdb_con->extended_protocol) { warn_use_ext = true; - LOG_WARNING("Prefer GDB command \"target extended-remote %s\" instead of \"target remote %s\"", + LOG_WARNING("Prefer GDB command \"target extended-remote :%s\" instead of \"target remote :%s\"", connection->service->port, connection->service->port); } break; @@ -3323,7 +3591,7 @@ static int gdb_input_inner(struct connection *connection) case 's': { gdb_thread_packet(connection, packet, packet_size); - log_add_callback(gdb_log_callback, connection); + gdb_con->output_flag = GDB_OUTPUT_ALL; if (gdb_con->mem_write_error) { LOG_ERROR("Memory write failure!"); @@ -3366,7 +3634,7 @@ static int gdb_input_inner(struct connection *connection) gdb_sig_halted(connection); /* stop forwarding log packets! */ - log_remove_callback(gdb_log_callback, connection); + gdb_con->output_flag = GDB_OUTPUT_NO; } else { /* We're running/stepping, in which case we can * forward log output until the target is halted @@ -3395,9 +3663,9 @@ static int gdb_input_inner(struct connection *connection) retval = gdb_detach(connection); break; case 'X': + gdb_con->output_flag = GDB_OUTPUT_NOTIF; retval = gdb_write_memory_binary_packet(connection, packet, packet_size); - if (retval != ERROR_OK) - return retval; + gdb_con->output_flag = GDB_OUTPUT_NO; break; case 'k': if (gdb_con->extended_protocol) { @@ -3417,12 +3685,14 @@ static int gdb_input_inner(struct connection *connection) break; case 'j': + /* DEPRECATED */ /* packet supported only by smp target i.e cortex_a.c*/ /* handle smp packet replying coreid played to gbd */ gdb_read_smp_packet(connection, packet, packet_size); break; case 'J': + /* DEPRECATED */ /* packet supported only by smp target i.e cortex_a.c */ /* handle smp packet setting coreid to be played at next * resume to gdb */ @@ -3438,7 +3708,7 @@ static int gdb_input_inner(struct connection *connection) * Fretcode,errno,Ctrl-C flag;call-specific attachment */ gdb_con->frontend_state = TARGET_RUNNING; - log_add_callback(gdb_log_callback, connection); + gdb_con->output_flag = GDB_OUTPUT_ALL; gdb_fileio_response_packet(connection, packet, packet_size); break; @@ -3491,13 +3761,76 @@ static int gdb_input(struct connection *connection) return ERROR_OK; } +/* + * Send custom notification packet as keep-alive during memory read/write. + * + * From gdb 7.0 (released 2009-10-06) an unknown notification received during + * memory read/write would be silently dropped. + * Before gdb 7.0 any character, with exclusion of "+-$", would be considered + * as junk and ignored. + * In both cases the reception will reset the timeout counter in gdb, thus + * working as a keep-alive. + * Check putpkt_binary() and getpkt_sane() in gdb commit + * 74531fed1f2d662debc2c209b8b3faddceb55960 + * + * Enable remote debug in gdb with 'set debug remote 1' to either dump the junk + * characters in gdb pre-7.0 and the notification from gdb 7.0. + */ +static void gdb_async_notif(struct connection *connection) +{ + static unsigned char count; + unsigned char checksum = 0; + char buf[22]; + + int len = sprintf(buf, "%%oocd_keepalive:%2.2x", count++); + for (int i = 1; i < len; i++) + checksum += buf[i]; + len += sprintf(buf + len, "#%2.2x", checksum); + +#ifdef _DEBUG_GDB_IO_ + LOG_DEBUG("sending packet '%s'", buf); +#endif + + gdb_write(connection, buf, len); +} + +static void gdb_keep_client_alive(struct connection *connection) +{ + struct gdb_connection *gdb_con = connection->priv; + + switch (gdb_con->output_flag) { + case GDB_OUTPUT_NO: + /* no need for keep-alive */ + break; + case GDB_OUTPUT_NOTIF: + /* send asynchronous notification */ + gdb_async_notif(connection); + break; + case GDB_OUTPUT_ALL: + /* send an empty O packet */ + gdb_output_con(connection, ""); + break; + default: + break; + } +} + +static const struct service_driver gdb_service_driver = { + .name = "gdb", + .new_connection_during_keep_alive_handler = NULL, + .new_connection_handler = gdb_new_connection, + .input_handler = gdb_input, + .connection_closed_handler = gdb_connection_closed, + .keep_client_alive_handler = gdb_keep_client_alive, +}; + static int gdb_target_start(struct target *target, const char *port) { struct gdb_service *gdb_service; int ret; gdb_service = malloc(sizeof(struct gdb_service)); - if (NULL == gdb_service) + if (!gdb_service) return -ENOMEM; LOG_INFO("starting gdb server for %s on %s", target_name(target), port); @@ -3507,19 +3840,14 @@ static int gdb_target_start(struct target *target, const char *port) gdb_service->core[1] = -1; target->gdb_service = gdb_service; - ret = add_service("gdb", - port, 1, &gdb_new_connection, &gdb_input, - &gdb_connection_closed, gdb_service); + ret = add_service(&gdb_service_driver, port, target->gdb_max_connections, gdb_service); /* initialize all targets gdb service with the same pointer */ { struct target_list *head; - struct target *curr; - head = target->head; - while (head != (struct target_list *)NULL) { - curr = head->target; + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; if (curr != target) curr->gdb_service = gdb_service; - head = head->next; } } return ret; @@ -3581,14 +3909,14 @@ static int gdb_target_add_one(struct target *target) int gdb_target_add_all(struct target *target) { - if (NULL == target) { + if (!target) { LOG_WARNING("gdb services need one or more targets defined"); return ERROR_OK; } - while (NULL != target) { + while (target) { int retval = gdb_target_add_one(target); - if (ERROR_OK != retval) + if (retval != ERROR_OK) return retval; target = target->next; @@ -3602,7 +3930,7 @@ COMMAND_HANDLER(handle_gdb_sync_command) if (CMD_ARGC != 0) return ERROR_COMMAND_SYNTAX_ERROR; - if (current_gdb_connection == NULL) { + if (!current_gdb_connection) { command_print(CMD, "gdb_sync command can only be run from within gdb using \"monitor gdb_sync\""); return ERROR_FAIL; @@ -3617,7 +3945,7 @@ COMMAND_HANDLER(handle_gdb_sync_command) COMMAND_HANDLER(handle_gdb_port_command) { int retval = CALL_COMMAND_HANDLER(server_pipe_command, &gdb_port); - if (ERROR_OK == retval) { + if (retval == ERROR_OK) { free(gdb_port_next); gdb_port_next = strdup(gdb_port); } @@ -3711,7 +4039,7 @@ COMMAND_HANDLER(handle_gdb_save_tdesc_command) size_t size_written; char *tdesc_filename = alloc_printf("%s.xml", target_type_name(target)); - if (tdesc_filename == NULL) { + if (!tdesc_filename) { retval = ERROR_FAIL; goto out; } @@ -3825,3 +4153,8 @@ void gdb_service_free(void) free(gdb_port); free(gdb_port_next); } + +int gdb_get_actual_connections(void) +{ + return gdb_actual_connections; +}