int closed;
int busy;
int noack_mode;
- bool sync; /* set flag to true if you want the next stepi to return immediately.
- allowing GDB to pick up a fresh set of register values from the target
- without modifying the target state. */
+ /* set flag to true if you want the next stepi to return immediately.
+ * allowing GDB to pick up a fresh set of register values from the target
+ * without modifying the target state. */
+ bool sync;
/* We delay reporting memory write errors until next step/continue or memory
* write. This improves performance of gdb load significantly as the GDB packet
* can be replied immediately and a new GDB packet will be ready without delay
- * (ca. 10% or so...).
- */
+ * (ca. 10% or so...). */
bool mem_write_error;
+ /* with extended-remote it seems we need to better emulate attach/detach.
+ * what this means is we reply with a W stop reply after a kill packet,
+ * 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;
};
#if 0
static int gdb_error(struct connection *connection, int retval);
static const char *gdb_port;
static const char *gdb_port_next;
-static const char DIGITS[16] = "0123456789abcdef";
static void gdb_log_callback(void *priv, const char *file, unsigned line,
const char *function, const char *string);
return 0x0; /* no signal... shouldn't happen */
default:
LOG_USER("undefined debug reason %d - target needs reset",
- target->debug_reason);
+ target->debug_reason);
return 0x0;
}
}
char local_buffer[1024];
local_buffer[0] = '$';
if ((size_t)len + 4 <= sizeof(local_buffer)) {
- /* performance gain on smaller packets by only a single call to gdb_write()
- **/
+ /* performance gain on smaller packets by only a single call to gdb_write() */
memcpy(local_buffer + 1, buffer, len++);
- local_buffer[len++] = '#';
- local_buffer[len++] = DIGITS[(my_checksum >> 4) & 0xf];
- local_buffer[len++] = DIGITS[my_checksum & 0xf];
+ len += snprintf(local_buffer + len, sizeof(local_buffer) - len, "#%02x", my_checksum);
retval = gdb_write(connection, local_buffer, len);
if (retval != ERROR_OK)
return retval;
} else {
/* larger packets are transmitted directly from caller supplied buffer
- by several calls to gdb_write() to avoid dynamic allocation */
- local_buffer[1] = '#';
- local_buffer[2] = DIGITS[(my_checksum >> 4) & 0xf];
- local_buffer[3] = DIGITS[my_checksum & 0xf];
+ * by several calls to gdb_write() to avoid dynamic allocation */
+ snprintf(local_buffer + 1, sizeof(local_buffer) - 1, "#%02x", my_checksum);
retval = gdb_write(connection, local_buffer, 1);
if (retval != ERROR_OK)
return retval;
gdb_putback_char(connection, reply);
return ERROR_OK;
} else {
- LOG_ERROR(
- "unknown character(1) 0x%2.2x in reply, dropping connection", reply);
+ LOG_ERROR("unknown character(1) 0x%2.2x in reply, dropping connection", reply);
gdb_con->closed = 1;
return ERROR_SERVER_REMOTE_CLOSED;
}
i++;
if (character == '#') {
/* Danger! character can be '#' when esc is
- * used so we need an explicit boolean for done here.
- */
+ * used so we need an explicit boolean for done here. */
done = 1;
break;
}
int checksum_ok = 0;
/* explicit code expansion here to get faster inlined code in -O3 by not
- * calculating checksum
- */
+ * calculating checksum */
if (gdb_con->noack_mode) {
retval = fetch_packet(connection, &checksum_ok, 1, len, buffer);
if (retval != ERROR_OK)
static int gdb_output_con(struct connection *connection, const char *line)
{
char *hex_buffer;
- int i, bin_size;
+ int bin_size;
bin_size = strlen(line);
- hex_buffer = malloc(bin_size*2 + 2);
+ hex_buffer = malloc(bin_size * 2 + 2);
if (hex_buffer == NULL)
return ERROR_GDB_BUFFER_TOO_SMALL;
hex_buffer[0] = 'O';
- for (i = 0; i < bin_size; i++)
- snprintf(hex_buffer + 1 + i*2, 3, "%2.2x", line[i]);
- hex_buffer[bin_size*2 + 1] = 0;
-
- int retval = gdb_put_packet(connection, hex_buffer, bin_size*2 + 1);
+ int pkt_len = hexify(hex_buffer + 1, line, bin_size, bin_size * 2 + 1);
+ int retval = gdb_put_packet(connection, hex_buffer, pkt_len + 1);
free(hex_buffer);
return retval;
target_call_event_callbacks(target, TARGET_EVENT_GDB_END);
break;
case TARGET_EVENT_GDB_FLASH_ERASE_START:
- target_handle_event(target, TARGET_EVENT_OLD_gdb_program_config);
retval = jtag_execute_queue();
if (retval != ERROR_OK)
return retval;
gdb_connection->noack_mode = 0;
gdb_connection->sync = true;
gdb_connection->mem_write_error = false;
+ gdb_connection->attached = true;
/* send ACK to GDB for debug request */
gdb_write(connection, "+", 1);
struct flash_bank *p;
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 to prepare target for GDB connect, or use 'gdb_memory_map disable'.");
+ LOG_ERROR("Connect failed. Consider setting up a gdb-attach event for the target " \
+ "to prepare target for GDB connect, or use 'gdb_memory_map disable'.");
return retval;
}
}
char *packet, int packet_size)
{
struct target *target = get_target_from_connection(connection);
+ struct gdb_connection *gdb_con = connection->priv;
char sig_reply[4];
int signal_var;
+ if (!gdb_con->attached) {
+ /* if we are here we have received a kill packet
+ * reply W stop reply otherwise gdb gets very unhappy */
+ gdb_put_packet(connection, "W00", 3);
+ return ERROR_OK;
+ }
+
signal_var = gdb_last_signal(target);
snprintf(sig_reply, 4, "S%2.2x", signal_var);
return ERROR_OK;
}
-static int gdb_reg_pos(struct target *target, int pos, int len)
+static inline int gdb_reg_pos(struct target *target, int pos, int len)
{
if (target->endianness == TARGET_LITTLE_ENDIAN)
return pos;
for (i = 0; i < buf_len; i++) {
int j = gdb_reg_pos(target, i, buf_len);
- tstr[i*2] = DIGITS[(buf[j]>>4) & 0xf];
- tstr[i*2 + 1] = DIGITS[buf[j]&0xf];
+ tstr += sprintf(tstr, "%02x", buf[j]);
}
}
-static int hextoint(int c)
-{
- if (c >= '0' && c <= '9')
- return c - '0';
- c = toupper(c);
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
- LOG_ERROR("BUG: invalid register value %08x", c);
- return 0;
-}
-
/* copy over in register buffer */
static void gdb_target_to_reg(struct target *target,
char *tstr, int str_len, uint8_t *bin)
int i;
for (i = 0; i < str_len; i += 2) {
- uint8_t t = hextoint(tstr[i]) << 4;
- t |= hextoint(tstr[i + 1]);
+ unsigned t;
+ if (sscanf(tstr + i, "%02x", &t) != 1) {
+ LOG_ERROR("BUG: unable to convert register value");
+ exit(-1);
+ }
int j = gdb_reg_pos(target, i/2, str_len/2);
bin[j] = t;
packet_size--;
if (packet_size % 2) {
- LOG_WARNING(
- "GDB set_registers packet with uneven characters received, dropping connection");
+ LOG_WARNING("GDB set_registers packet with uneven characters received, dropping connection");
return ERROR_SERVER_REMOTE_CLOSED;
}
if (reg_list_size <= reg_num) {
LOG_ERROR("gdb requested a non-existing register");
- exit(-1);
+ return ERROR_SERVER_REMOTE_CLOSED;
}
if (!reg_list[reg_num]->valid)
if (retval != ERROR_OK)
return gdb_error(connection, retval);
- if (reg_list_size < reg_num) {
+ if (reg_list_size <= reg_num) {
LOG_ERROR("gdb requested a non-existing register");
return ERROR_SERVER_REMOTE_CLOSED;
}
bin_buf = malloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8));
int chars = (DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2);
- /* fix!!! add some sanity checks on packet size here */
+ if ((unsigned int)chars != strlen(separator + 1)) {
+ LOG_ERROR("gdb sent a packet with wrong register size");
+ free(bin_buf);
+ return ERROR_SERVER_REMOTE_CLOSED;
+ }
gdb_target_to_reg(target, separator + 1, chars, bin_buf);
if (retval == ERROR_OK) {
hex_buffer = malloc(len * 2 + 1);
- uint32_t i;
- for (i = 0; i < len; i++) {
- uint8_t t = buffer[i];
- hex_buffer[2 * i] = DIGITS[(t >> 4) & 0xf];
- hex_buffer[2 * i + 1] = DIGITS[t & 0xf];
- }
+ int pkt_len = hexify(hex_buffer, (char *)buffer, len, len * 2 + 1);
- gdb_put_packet(connection, hex_buffer, len * 2);
+ gdb_put_packet(connection, hex_buffer, pkt_len);
free(hex_buffer);
} else
uint32_t len = 0;
uint8_t *buffer;
-
- uint32_t i;
int retval;
/* skip command character */
LOG_DEBUG("addr: 0x%8.8" PRIx32 ", len: 0x%8.8" PRIx32 "", addr, len);
- for (i = 0; i < len; i++) {
- uint32_t tmp;
- sscanf(separator + 2*i, "%2" SCNx32, &tmp);
- buffer[i] = tmp;
- }
+ if (unhexify((char *)buffer, separator + 2, len) != (int)len)
+ LOG_ERROR("unable to decode memory packet");
retval = target_write_buffer(target, addr, len, buffer);
if (packet[0] == 'c') {
LOG_DEBUG("continue");
- target_handle_event(target, TARGET_EVENT_OLD_pre_resume);
- retval = target_resume(target, current, address, 0, 0); /* resume at current
- *address, don't handle
- *breakpoints, not debugging
- **/
+ /* resume at current address, don't handle breakpoints, not debugging */
+ retval = target_resume(target, current, address, 0, 0);
} else if (packet[0] == 's') {
LOG_DEBUG("step");
/* step at current or address, don't handle breakpoints */
struct gdb_connection *gdb_connection = connection->priv;
struct target *target = get_target_from_connection(connection);
- if (strstr(packet, "qRcmd,")) {
+ if (strncmp(packet, "qRcmd,", 6) == 0) {
if (packet_size > 6) {
char *cmd;
- int i;
- cmd = malloc((packet_size - 6)/2 + 1);
- for (i = 0; i < (packet_size - 6)/2; i++) {
- uint32_t tmp;
- sscanf(packet + 6 + 2*i, "%2" SCNx32, &tmp);
- cmd[i] = tmp;
- }
- cmd[(packet_size - 6)/2] = 0x0;
+ cmd = malloc((packet_size - 6) / 2 + 1);
+ int len = unhexify(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_put_packet(connection, "OK", 2);
return ERROR_OK;
- } else if (strstr(packet, "qCRC:")) {
+ } else if (strncmp(packet, "qCRC:", 5) == 0) {
if (packet_size > 5) {
int retval;
char gdb_reply[10];
addr = strtoul(packet, &separator, 16);
if (*separator != ',') {
- LOG_ERROR(
- "incomplete read memory packet received, dropping connection");
+ LOG_ERROR("incomplete read memory packet received, dropping connection");
return ERROR_SERVER_REMOTE_CLOSED;
}
return ERROR_OK;
}
- } else if (strstr(packet, "qSupported")) {
+ } 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 */
int retval = ERROR_OK;
free(buffer);
return ERROR_OK;
- } else if (strstr(packet, "qXfer:memory-map:read::")
+ } else if ((strncmp(packet, "qXfer:memory-map:read::", 23) == 0)
&& (flash_get_bank_count() > 0))
return gdb_memory_map(connection, packet, packet_size);
- else if (strstr(packet, "qXfer:features:read:")) {
+ else if (strncmp(packet, "qXfer:features:read:", 20) == 0) {
char *xml = NULL;
int size = 0;
int pos = 0;
free(xml);
return ERROR_OK;
- } else if (strstr(packet, "QStartNoAckMode")) {
+ } else if (strncmp(packet, "QStartNoAckMode", 15) == 0) {
gdb_connection->noack_mode = 1;
gdb_put_packet(connection, "OK", 2);
return ERROR_OK;
return ERROR_OK;
}
- if (strstr(packet, "vFlashErase:")) {
+ if (strncmp(packet, "vFlashErase:", 12) == 0) {
unsigned long addr;
unsigned long length;
return ERROR_OK;
}
- if (strstr(packet, "vFlashWrite:")) {
+ if (strncmp(packet, "vFlashWrite:", 12) == 0) {
int retval;
unsigned long addr;
unsigned long length;
return ERROR_OK;
}
- if (!strcmp(packet, "vFlashDone")) {
+ if (strncmp(packet, "vFlashDone", 10) == 0) {
uint32_t written;
/* process the flashing buffer. No need to erase as GDB
* always issues a vFlashErase first. */
target_call_event_callbacks(gdb_service->target,
- TARGET_EVENT_GDB_FLASH_WRITE_START);
- result =
- flash_write(gdb_service->target, gdb_connection->vflash_image, &written, 0);
+ TARGET_EVENT_GDB_FLASH_WRITE_START);
+ result = flash_write(gdb_service->target, gdb_connection->vflash_image, &written, 0);
target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_FLASH_WRITE_END);
if (result != ERROR_OK) {
if (result == ERROR_FLASH_DST_OUT_OF_BANK)
{
struct gdb_service *gdb_service = connection->service->priv;
- target_call_event_callbacks(gdb_service->target,
- TARGET_EVENT_GDB_DETACH);
+ target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_DETACH);
return gdb_put_packet(connection, "OK", 2);
}
char sig_reply[4];
snprintf(sig_reply, 4, "T%2.2x", 2);
gdb_put_packet(connection, sig_reply, 3);
-
}
static int gdb_input_inner(struct connection *connection)
case 'T': /* Is thread alive? */
gdb_thread_packet(connection, packet, packet_size);
break;
- case 'H': /* Set current thread ( 'c' for step and continue, 'g' for
- * all other operations ) */
+ case 'H': /* Set current thread ( 'c' for step and continue,
+ * 'g' for all other operations ) */
gdb_thread_packet(connection, packet, packet_size);
break;
case 'q':
LOG_ERROR("Memory write failure!");
/* now that we have reported the memory write error,
- *we can clear the condition */
+ * we can clear the condition */
gdb_con->mem_write_error = false;
}
retval = gdb_step_continue_packet(connection, packet, packet_size);
if (retval != ERROR_OK) {
/* we'll never receive a halted
- *condition... issue a false one..
- **/
+ * condition... issue a false one..
+ */
gdb_frontend_halted(target, connection);
}
}
return retval;
break;
case 'k':
- if (extended_protocol != 0)
+ if (extended_protocol != 0) {
+ gdb_con->attached = false;
break;
+ }
gdb_put_packet(connection, "OK", 2);
return ERROR_SERVER_REMOTE_CLOSED;
case '!':
/* handle extended restart packet */
breakpoint_clear_target(gdb_service->target);
watchpoint_clear_target(gdb_service->target);
- command_run_linef(connection->cmd_ctx,
- "ocd_gdb_restart %s",
- target_name(target));
+ command_run_linef(connection->cmd_ctx, "ocd_gdb_restart %s",
+ target_name(target));
+ /* set connection as attached after reset */
+ gdb_con->attached = true;
/* info rtos parts */
gdb_thread_packet(connection, packet, packet_size);
- gdb_put_packet(connection, "OK", 2);
break;
case 'j':
- /* packet supported only by smp target i.e cortex_a.c*/
+ /* 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':
- /* packet supported only by smp target i.e cortex_a.c */
- /* handle smp packet setting coreid to be played at next
- * resume to gdb */
+ /* packet supported only by smp target i.e cortex_a.c */
+ /* handle smp packet setting coreid to be played at next
+ * resume to gdb */
gdb_write_smp_packet(connection, packet, packet_size);
break;
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
gdb_con->ctrl_c = 0;
} else {
- LOG_INFO(
- "The target is not running when halt was requested, stopping GDB.");
+ LOG_INFO("The target is not running when halt was requested, stopping GDB.");
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
}
}