* Disabled by default.
*/
static int gdb_report_data_abort;
+/* If set, errors when accessing registers are reported to gdb. Disabled by
+ * default. */
+static int gdb_report_register_access_error;
/* set if we are sending target descriptions to gdb
* via qXfer:features:read packet */
} else {
if (gdb_connection->ctrl_c) {
signal_var = 0x2;
- gdb_connection->ctrl_c = 0;
} else
signal_var = gdb_last_signal(target);
target->rtos->current_thread);
target->rtos->current_threadid = target->rtos->current_thread;
target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
- signal_var = gdb_last_signal(ct);
+ if (!gdb_connection->ctrl_c)
+ signal_var = gdb_last_signal(ct);
}
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s",
signal_var, stop_reason, current_thread);
+
+ gdb_connection->ctrl_c = 0;
}
gdb_put_packet(connection, sig_reply, sig_reply_len);
reg_packet_p = reg_packet;
for (i = 0; i < reg_list_size; i++) {
- if (!reg_list[i]->valid)
- reg_list[i]->type->get(reg_list[i]);
+ 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);
+ }
+ }
gdb_str_to_target(target, reg_packet_p, reg_list[i]);
reg_packet_p += DIV_ROUND_UP(reg_list[i]->size, 8) * 2;
}
bin_buf = malloc(DIV_ROUND_UP(reg_list[i]->size, 8));
gdb_target_to_reg(target, packet_p, chars, bin_buf);
- reg_list[i]->type->set(reg_list[i], bin_buf);
+ retval = reg_list[i]->type->set(reg_list[i], bin_buf);
+ if (retval != ERROR_OK && gdb_report_register_access_error) {
+ LOG_DEBUG("Couldn't set register %s.", reg_list[i]->name);
+ free(reg_list);
+ free(bin_buf);
+ return gdb_error(connection, retval);
+ }
/* advance packet pointer */
packet_p += chars;
return ERROR_SERVER_REMOTE_CLOSED;
}
- if (!reg_list[reg_num]->valid)
- reg_list[reg_num]->type->get(reg_list[reg_num]);
+ 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 */
gdb_target_to_reg(target, separator + 1, chars, bin_buf);
- reg_list[reg_num]->type->set(reg_list[reg_num], bin_buf);
+ retval = reg_list[reg_num]->type->set(reg_list[reg_num], bin_buf);
+ if (retval != ERROR_OK && gdb_report_register_access_error) {
+ LOG_DEBUG("Couldn't set register %s.", reg_list[reg_num]->name);
+ free(bin_buf);
+ free(reg_list);
+ return gdb_error(connection, retval);
+ }
gdb_put_packet(connection, "OK", 2);
static const char *gdb_get_reg_type_name(enum reg_type type)
{
switch (type) {
+ case REG_TYPE_BOOL:
+ return "bool";
case REG_TYPE_INT:
return "int";
case REG_TYPE_INT8:
return "int64";
case REG_TYPE_INT128:
return "int128";
+ case REG_TYPE_UINT:
+ return "uint";
case REG_TYPE_UINT8:
return "uint8";
case REG_TYPE_UINT16:
type->id, type->reg_type_struct->size);
while (field != NULL) {
xml_printf(&retval, tdesc, pos, size,
- "<field name=\"%s\" start=\"%d\" end=\"%d\"/>\n",
- field->name, field->bitfield->start,
- field->bitfield->end);
+ "<field name=\"%s\" start=\"%d\" end=\"%d\" type=\"%s\" />\n",
+ field->name, field->bitfield->start, field->bitfield->end,
+ gdb_get_reg_type_name(field->bitfield->type));
field = field->next;
}
field = type->reg_type_flags->fields;
while (field != NULL) {
xml_printf(&retval, tdesc, pos, size,
- "<field name=\"%s\" start=\"%d\" end=\"%d\"/>\n",
- field->name, field->bitfield->start, field->bitfield->end);
+ "<field name=\"%s\" start=\"%d\" end=\"%d\" type=\"%s\" />\n",
+ field->name, field->bitfield->start, field->bitfield->end,
+ gdb_get_reg_type_name(field->bitfield->type));
field = field->next;
}
else
transfer_type = 'l';
- *chunk = malloc(length + 2);
+ *chunk = malloc(length + 2 + 3);
+ /* 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) {
LOG_ERROR("Unable to allocate memory");
return ERROR_FAIL;
/* single-step or step-over-breakpoint */
if (parse[0] == 's') {
+ bool fake_step = false;
+
if (strncmp(parse, "s:", 2) == 0) {
struct target *ct = target;
int current_pc = 1;
parse = endp;
}
- if (target->rtos != NULL)
+ if (target->rtos != NULL) {
+ /* 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);
+ /*
+ * 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;
}
}
- LOG_DEBUG("target %s single-step thread %"PRId64, target_name(ct), thread_id);
+ 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);
+
+ sig_reply_len = snprintf(sig_reply, sizeof(sig_reply),
+ "T05thread:%016"PRIx64";", thread_id);
+
+ gdb_put_packet(connection, sig_reply, sig_reply_len);
+ log_remove_callback(gdb_log_callback, connection);
+
+ return true;
+ }
+
/* support for gdb_sync command */
if (gdb_connection->sync) {
gdb_connection->sync = false;
return ERROR_OK;
}
+COMMAND_HANDLER(handle_gdb_report_register_access_error)
+{
+ if (CMD_ARGC != 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ COMMAND_PARSE_ENABLE(CMD_ARGV[0], gdb_report_register_access_error);
+ return ERROR_OK;
+}
+
/* gdb_breakpoint_override */
COMMAND_HANDLER(handle_gdb_breakpoint_override_command)
{
.help = "enable or disable reporting data aborts",
.usage = "('enable'|'disable')"
},
+ {
+ .name = "gdb_report_register_access_error",
+ .handler = handle_gdb_report_register_access_error,
+ .mode = COMMAND_CONFIG,
+ .help = "enable or disable reporting register access errors",
+ .usage = "('enable'|'disable')"
+ },
{
.name = "gdb_breakpoint_override",
.handler = handle_gdb_breakpoint_override_command,