]> git.sur5r.net Git - openocd/blobdiff - src/server/gdb_server.c
gdb_server: prevent false positive valgrind report
[openocd] / src / server / gdb_server.c
index 42ac8a5e2e7a2bfea4798a4109049651fb8796ad..2acebe83968eb0d224b7e6273a30509d72724967 100644 (file)
@@ -961,9 +961,14 @@ static int gdb_new_connection(struct connection *connection)
        breakpoint_clear_target(target);
        watchpoint_clear_target(target);
 
-       /* clean previous rtos session if supported*/
-       if ((target->rtos) && (target->rtos->type->clean))
-               target->rtos->type->clean(target);
+       if (target->rtos) {
+               /* clean previous rtos session if supported*/
+               if (target->rtos->type->clean)
+                       target->rtos->type->clean(target);
+
+               /* update threads */
+               rtos_update_threads(target);
+       }
 
        /* remove the initial ACK from the incoming buffer */
        retval = gdb_get_char(connection, &initial_ack);
@@ -1941,12 +1946,45 @@ static const char *gdb_get_reg_type_name(enum reg_type type)
        return "int"; /* "int" as default value */
 }
 
+static int lookup_add_arch_defined_types(char const **arch_defined_types_list[], const char *type_id,
+                                       int *num_arch_defined_types)
+{
+       int tbl_sz = *num_arch_defined_types;
+
+       if (type_id != NULL && (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;
+                               *arch_defined_types_list = realloc(*arch_defined_types_list,
+                                                               sizeof(char *) * (tbl_sz + 1));
+                               (*arch_defined_types_list)[tbl_sz] = NULL;
+                               *num_arch_defined_types = tbl_sz;
+                               return 1;
+                       } else {
+                               if (!strcmp((*arch_defined_types_list)[j], type_id))
+                                       return 0;
+                       }
+               }
+       }
+
+       return -1;
+}
+
 static int gdb_generate_reg_type_description(struct target *target,
-               char **tdesc, int *pos, int *size, struct reg_data_type *type)
+               char **tdesc, int *pos, int *size, struct reg_data_type *type,
+               char const **arch_defined_types_list[], int * num_arch_defined_types)
 {
        int retval = ERROR_OK;
 
        if (type->type_class == REG_TYPE_CLASS_VECTOR) {
+               struct reg_data_type *data_type = type->reg_type_vector->type;
+               if (data_type->type == REG_TYPE_ARCH_DEFINED) {
+                       if (lookup_add_arch_defined_types(arch_defined_types_list, data_type->id,
+                                                       num_arch_defined_types))
+                               gdb_generate_reg_type_description(target, tdesc, pos, size, data_type,
+                                                               arch_defined_types_list,
+                                                               num_arch_defined_types);
+               }
                /* <vector id="id" type="type" count="count"/> */
                xml_printf(&retval, tdesc, pos, size,
                                "<vector id=\"%s\" type=\"%s\" count=\"%d\"/>\n",
@@ -1954,6 +1992,20 @@ static int gdb_generate_reg_type_description(struct target *target,
                                type->reg_type_vector->count);
 
        } 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) {
+                       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,
+                                                               num_arch_defined_types))
+                                       gdb_generate_reg_type_description(target, tdesc, pos, size, data_type,
+                                                                       arch_defined_types_list,
+                                                                       num_arch_defined_types);
+                       }
+
+                       field = field->next;
+               }
                /* <union id="id">
                 *  <field name="name" type="type"/> ...
                 * </union> */
@@ -1961,7 +2013,6 @@ static int gdb_generate_reg_type_description(struct target *target,
                                "<union id=\"%s\">\n",
                                type->id);
 
-               struct reg_data_type_union_field *field;
                field = type->reg_type_union->fields;
                while (field != NULL) {
                        xml_printf(&retval, tdesc, pos, size,
@@ -1994,6 +2045,17 @@ static int gdb_generate_reg_type_description(struct target *target,
                                field = field->next;
                        }
                } else {
+                       while (field != NULL) {
+                               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,
+                                                                       num_arch_defined_types))
+                                               gdb_generate_reg_type_description(target, tdesc, pos, size, data_type,
+                                                                               arch_defined_types_list,
+                                                                               num_arch_defined_types);
+                               }
+                       }
+
                        /* <struct id="id">
                         *  <field name="name" type="type"/> ...
                         * </struct> */
@@ -2086,11 +2148,15 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
        struct reg **reg_list = NULL;
        int reg_list_size;
        char const **features = NULL;
+       char const **arch_defined_types = NULL;
        int feature_list_size = 0;
+       int num_arch_defined_types = 0;
        char *tdesc = NULL;
        int pos = 0;
        int size = 0;
 
+       arch_defined_types = calloc(1, sizeof(char *));
+
        retval = target_get_gdb_reg_list(target, &reg_list,
                        &reg_list_size, REG_CLASS_ALL);
 
@@ -2143,8 +2209,13 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
                                if (reg_list[i]->reg_data_type != NULL) {
                                        if (reg_list[i]->reg_data_type->type == REG_TYPE_ARCH_DEFINED) {
                                                /* generate <type... first, if there are architecture-defined types. */
-                                               gdb_generate_reg_type_description(target, &tdesc, &pos, &size,
-                                                               reg_list[i]->reg_data_type);
+                                               if (lookup_add_arch_defined_types(&arch_defined_types,
+                                                                               reg_list[i]->reg_data_type->id,
+                                                                               &num_arch_defined_types))
+                                                       gdb_generate_reg_type_description(target, &tdesc, &pos, &size,
+                                                                                       reg_list[i]->reg_data_type,
+                                                                                       &arch_defined_types,
+                                                                                       &num_arch_defined_types);
 
                                                type_str = reg_list[i]->reg_data_type->id;
                                        } else {
@@ -2194,6 +2265,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o
 error:
        free(features);
        free(reg_list);
+       free(arch_defined_types);
 
        if (retval == ERROR_OK)
                *tdesc_out = tdesc;
@@ -2370,7 +2442,11 @@ static int gdb_get_thread_list_chunk(struct target *target, char **thread_list,
        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;
@@ -2594,18 +2670,32 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
                LOG_DEBUG("target %s continue", target_name(target));
                log_add_callback(gdb_log_callback, connection);
                retval = target_resume(target, 1, 0, 0, 0);
-               if (retval == ERROR_OK) {
-                       gdb_connection->frontend_state = TARGET_RUNNING;
-                       target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
+               if (retval == ERROR_TARGET_NOT_HALTED)
+                       LOG_INFO("target %s was not halted when resume was requested", target_name(target));
+
+               /* poll target in an attempt to make its internal state consistent */
+               if (retval != ERROR_OK) {
+                       retval = target_poll(target);
+                       if (retval != ERROR_OK)
+                               LOG_DEBUG("error polling target %s after failed resume", target_name(target));
                }
+
+               /*
+                * We don't report errors to gdb here, move frontend_state to
+                * TARGET_RUNNING to stay in sync with gdb's expectation of the
+                * target state
+                */
+               gdb_connection->frontend_state = TARGET_RUNNING;
+               target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
+
                return true;
        }
 
        /* single-step or step-over-breakpoint */
        if (parse[0] == 's') {
                if (strncmp(parse, "s:", 2) == 0) {
-                       int handle_breakpoint = 1;
                        struct target *ct = target;
+                       int current_pc = 1;
                        int64_t thread_id;
                        char *endp;
 
@@ -2629,21 +2719,63 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
                                        parse += 1;
                                        packet_size -= 1;
 
-                                       handle_breakpoint = 0;
+                                       /* check if thread-id follows */
+                                       if (parse[0] == ':') {
+                                               int64_t tid;
+                                               parse += 1;
+                                               packet_size -= 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;*/
+                                               }
+                                       }
                                }
                        }
 
                        LOG_DEBUG("target %s single-step thread %"PRId64, target_name(ct), thread_id);
-                       retval = target_step(ct, 1, 0, handle_breakpoint);
+                       log_add_callback(gdb_log_callback, connection);
+                       target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
+
+                       /* support for gdb_sync command */
+                       if (gdb_connection->sync) {
+                               gdb_connection->sync = false;
+                               if (ct->state == TARGET_HALTED) {
+                                       LOG_WARNING("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;
+                       }
+
+                       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) {
-                               gdb_signal_reply(target, connection);
+                               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);
                        } else
-                       if (retval == ERROR_TARGET_TIMEOUT) {
                                gdb_connection->frontend_state = TARGET_RUNNING;
-                               target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
-                       }
                } else {
                        LOG_ERROR("Unknown vCont packet");
                        return false;
@@ -3119,6 +3251,8 @@ static int gdb_input_inner(struct connection *connection)
                                if (target->rtos)
                                        target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &t);
                                retval = target_halt(t);
+                               if (retval == ERROR_OK)
+                                       retval = target_poll(t);
                                if (retval != ERROR_OK)
                                        target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
                                gdb_con->ctrl_c = 0;
@@ -3442,3 +3576,9 @@ int gdb_register_commands(struct command_context *cmd_ctx)
        gdb_port_next = strdup("3333");
        return register_commands(cmd_ctx, NULL, gdb_command_handlers);
 }
+
+void gdb_service_free(void)
+{
+       free(gdb_port);
+       free(gdb_port_next);
+}