tdesc: bitfields may carry a type
[openocd.git] / src / server / gdb_server.c
index 2912c7433c9f12714a422ab273bb13613191d8f1..428547b460764982da6088bd373ffb4e83a43f11 100644 (file)
@@ -31,9 +31,7 @@
  *   GNU General Public License for more details.                          *
  *                                                                         *
  *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
@@ -43,6 +41,8 @@
 #include <target/breakpoints.h>
 #include <target/target_request.h>
 #include <target/register.h>
+#include <target/target.h>
+#include <target/target_type.h>
 #include "server.h"
 #include <flash/nor/core.h>
 #include "gdb_server.h"
@@ -73,8 +73,8 @@ struct gdb_connection {
        int ctrl_c;
        enum target_state frontend_state;
        struct image *vflash_image;
-       int closed;
-       int busy;
+       bool closed;
+       bool busy;
        int noack_mode;
        /* 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
@@ -92,6 +92,8 @@ struct gdb_connection {
        bool attached;
        /* temporarily used for target description support */
        struct target_desc_format target_desc;
+       /* temporarily used for thread list support */
+       char *thread_list;
 };
 
 #if 0
@@ -110,6 +112,8 @@ static char *gdb_port_next;
 static void gdb_log_callback(void *priv, const char *file, unsigned line,
                const char *function, const char *string);
 
+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;
@@ -215,7 +219,7 @@ 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) {
-                       gdb_con->closed = 1;
+                       gdb_con->closed = true;
                        return ERROR_SERVER_REMOTE_CLOSED;
                }
 
@@ -227,10 +231,10 @@ static int gdb_get_char_inner(struct connection *connection, int *next_char)
                                usleep(1000);
                                break;
                        case WSAECONNABORTED:
-                               gdb_con->closed = 1;
+                               gdb_con->closed = true;
                                return ERROR_SERVER_REMOTE_CLOSED;
                        case WSAECONNRESET:
-                               gdb_con->closed = 1;
+                               gdb_con->closed = true;
                                return ERROR_SERVER_REMOTE_CLOSED;
                        default:
                                LOG_ERROR("read: %d", errno);
@@ -242,14 +246,14 @@ static int gdb_get_char_inner(struct connection *connection, int *next_char)
                                usleep(1000);
                                break;
                        case ECONNABORTED:
-                               gdb_con->closed = 1;
+                               gdb_con->closed = true;
                                return ERROR_SERVER_REMOTE_CLOSED;
                        case ECONNRESET:
-                               gdb_con->closed = 1;
+                               gdb_con->closed = true;
                                return ERROR_SERVER_REMOTE_CLOSED;
                        default:
                                LOG_ERROR("read: %s", strerror(errno));
-                               gdb_con->closed = 1;
+                               gdb_con->closed = true;
                                return ERROR_SERVER_REMOTE_CLOSED;
                }
 #endif
@@ -341,7 +345,7 @@ static int gdb_write(struct connection *connection, void *data, int len)
 
        if (connection_write(connection, data, len) == len)
                return ERROR_OK;
-       gdb_con->closed = 1;
+       gdb_con->closed = true;
        return ERROR_SERVER_REMOTE_CLOSED;
 }
 
@@ -448,7 +452,7 @@ static int gdb_put_packet_inner(struct connection *connection,
                                return ERROR_OK;
                        } else {
                                LOG_ERROR("unknown character(1) 0x%2.2x in reply, dropping connection", reply);
-                               gdb_con->closed = 1;
+                               gdb_con->closed = true;
                                return ERROR_SERVER_REMOTE_CLOSED;
                        }
                } else if (reply == '$') {
@@ -458,7 +462,7 @@ static int gdb_put_packet_inner(struct connection *connection,
                } else {
                        LOG_ERROR("unknown character(2) 0x%2.2x in reply, dropping connection",
                                reply);
-                       gdb_con->closed = 1;
+                       gdb_con->closed = true;
                        return ERROR_SERVER_REMOTE_CLOSED;
                }
        }
@@ -471,9 +475,9 @@ static int gdb_put_packet_inner(struct connection *connection,
 int gdb_put_packet(struct connection *connection, char *buffer, int len)
 {
        struct gdb_connection *gdb_con = connection->priv;
-       gdb_con->busy = 1;
+       gdb_con->busy = true;
        int retval = gdb_put_packet_inner(connection, buffer, len);
-       gdb_con->busy = 0;
+       gdb_con->busy = false;
 
        /* we sent some data, reset timer for keep alive messages */
        kept_alive();
@@ -628,7 +632,7 @@ static int gdb_get_packet_inner(struct connection *connection,
                                         * warning only about subsequent ACK's. */
                                        if (gdb_con->noack_mode > 1) {
                                                LOG_WARNING("acknowledgment received, but no packet pending");
-                                       } else {
+                                       } else if (gdb_con->noack_mode) {
                                                LOG_DEBUG("Received first acknowledgment after entering noack mode. Ignoring it.");
                                                gdb_con->noack_mode = 2;
                                        }
@@ -679,9 +683,9 @@ static int gdb_get_packet_inner(struct connection *connection,
 static int gdb_get_packet(struct connection *connection, char *buffer, int *len)
 {
        struct gdb_connection *gdb_con = connection->priv;
-       gdb_con->busy = 1;
+       gdb_con->busy = true;
        int retval = gdb_get_packet_inner(connection, buffer, len);
-       gdb_con->busy = 0;
+       gdb_con->busy = false;
        return retval;
 }
 
@@ -697,7 +701,8 @@ static int gdb_output_con(struct connection *connection, const char *line)
                return ERROR_GDB_BUFFER_TOO_SMALL;
 
        hex_buffer[0] = 'O';
-       int pkt_len = hexify(hex_buffer + 1, line, bin_size, bin_size * 2 + 1);
+       size_t pkt_len = hexify(hex_buffer + 1, (const uint8_t *)line, bin_size,
+               bin_size * 2 + 1);
        int retval = gdb_put_packet(connection, hex_buffer, pkt_len + 1);
 
        free(hex_buffer);
@@ -714,39 +719,41 @@ static int gdb_output(struct command_context *context, const char *line)
 static void gdb_signal_reply(struct target *target, struct connection *connection)
 {
        struct gdb_connection *gdb_connection = connection->priv;
-       char sig_reply[20];
+       char sig_reply[45];
        char stop_reason[20];
+       char current_thread[25];
        int sig_reply_len;
        int signal_var;
 
+       rtos_update_threads(target);
+
        if (target->debug_reason == DBG_REASON_EXIT) {
                sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "W00");
        } else {
                if (gdb_connection->ctrl_c) {
                        signal_var = 0x2;
-                       gdb_connection->ctrl_c = 0;
                } else
                        signal_var = gdb_last_signal(target);
 
                stop_reason[0] = '\0';
                if (target->debug_reason == DBG_REASON_WATCHPOINT) {
                        enum watchpoint_rw hit_wp_type;
-                       uint32_t hit_wp_address;
+                       target_addr_t hit_wp_address;
 
                        if (watchpoint_hit(target, &hit_wp_type, &hit_wp_address) == ERROR_OK) {
 
                                switch (hit_wp_type) {
                                        case WPT_WRITE:
                                                snprintf(stop_reason, sizeof(stop_reason),
-                                                               "watch:%08" PRIx32 ";", hit_wp_address);
+                                                               "watch:%08" TARGET_PRIxADDR ";", hit_wp_address);
                                                break;
                                        case WPT_READ:
                                                snprintf(stop_reason, sizeof(stop_reason),
-                                                               "rwatch:%08" PRIx32 ";", hit_wp_address);
+                                                               "rwatch:%08" TARGET_PRIxADDR ";", hit_wp_address);
                                                break;
                                        case WPT_ACCESS:
                                                snprintf(stop_reason, sizeof(stop_reason),
-                                                               "awatch:%08" PRIx32 ";", hit_wp_address);
+                                                               "awatch:%08" TARGET_PRIxADDR ";", hit_wp_address);
                                                break;
                                        default:
                                                break;
@@ -754,13 +761,25 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
                        }
                }
 
-               sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s",
-                               signal_var, stop_reason);
+               current_thread[0] = '\0';
+               if (target->rtos != NULL) {
+                       struct target *ct;
+                       snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";",
+                                       target->rtos->current_thread);
+                       target->rtos->current_threadid = target->rtos->current_thread;
+                       target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &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);
        gdb_connection->frontend_state = TARGET_HALTED;
-       rtos_update_threads(target);
 }
 
 static void gdb_fileio_reply(struct target *target, struct connection *connection)
@@ -908,10 +927,11 @@ static int gdb_target_callback_event_handler(struct target *target,
 static int gdb_new_connection(struct connection *connection)
 {
        struct gdb_connection *gdb_connection = malloc(sizeof(struct gdb_connection));
-       struct gdb_service *gdb_service = connection->service->priv;
+       struct target *target;
        int retval;
        int initial_ack;
 
+       target = get_target_from_connection(connection);
        connection->priv = gdb_connection;
 
        /* initialize gdb connection information */
@@ -920,14 +940,15 @@ static int gdb_new_connection(struct connection *connection)
        gdb_connection->ctrl_c = 0;
        gdb_connection->frontend_state = TARGET_HALTED;
        gdb_connection->vflash_image = NULL;
-       gdb_connection->closed = 0;
-       gdb_connection->busy = 0;
+       gdb_connection->closed = false;
+       gdb_connection->busy = false;
        gdb_connection->noack_mode = 0;
        gdb_connection->sync = false;
        gdb_connection->mem_write_error = false;
        gdb_connection->attached = true;
        gdb_connection->target_desc.tdesc = NULL;
        gdb_connection->target_desc.tdesc_length = 0;
+       gdb_connection->thread_list = NULL;
 
        /* send ACK to GDB for debug request */
        gdb_write(connection, "+", 1);
@@ -939,12 +960,17 @@ static int gdb_new_connection(struct connection *connection)
         * GDB session could leave dangling breakpoints if e.g. communication
         * timed out.
         */
-       breakpoint_clear_target(gdb_service->target);
-       watchpoint_clear_target(gdb_service->target);
+       breakpoint_clear_target(target);
+       watchpoint_clear_target(target);
 
-       /* clean previous rtos session if supported*/
-       if ((gdb_service->target->rtos) && (gdb_service->target->rtos->type->clean))
-               gdb_service->target->rtos->type->clean(gdb_service->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);
@@ -956,7 +982,7 @@ static int gdb_new_connection(struct connection *connection)
         */
        if (initial_ack != '+')
                gdb_putback_char(connection, initial_ack);
-       target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_ATTACH);
+       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.
@@ -968,7 +994,7 @@ static int gdb_new_connection(struct connection *connection)
                for (i = 0; i < flash_get_bank_count(); i++) {
                        struct flash_bank *p;
                        p = get_flash_bank_by_num_noprobe(i);
-                       if (p->target != gdb_service->target)
+                       if (p->target != target)
                                continue;
                        retval = get_flash_bank_by_num(i, &p);
                        if (retval != ERROR_OK) {
@@ -980,10 +1006,12 @@ static int gdb_new_connection(struct connection *connection)
        }
 
        gdb_actual_connections++;
-       LOG_DEBUG("New GDB Connection: %d, Target %s, state: %s",
+       log_printf_lf(all_targets->next != NULL ? LOG_LVL_INFO : LOG_LVL_DEBUG,
+                       __FILE__, __LINE__, __func__,
+                       "New GDB Connection: %d, Target %s, state: %s",
                        gdb_actual_connections,
-                       target_name(gdb_service->target),
-                       target_state_name(gdb_service->target));
+                       target_name(target),
+                       target_state_name(target));
 
        /* DANGER! If we fail subsequently, we must remove this handler,
         * otherwise we occasionally see crashes as the timer can invoke the
@@ -997,9 +1025,11 @@ static int gdb_new_connection(struct connection *connection)
 
 static int gdb_connection_closed(struct connection *connection)
 {
-       struct gdb_service *gdb_service = connection->service->priv;
+       struct target *target;
        struct gdb_connection *gdb_connection = connection->priv;
 
+       target = get_target_from_connection(connection);
+
        /* we're done forwarding messages. Tear down callback before
         * cleaning up connection.
         */
@@ -1007,8 +1037,8 @@ static int gdb_connection_closed(struct connection *connection)
 
        gdb_actual_connections--;
        LOG_DEBUG("GDB Close, Target: %s, state: %s, gdb_actual_connections=%d",
-               target_name(gdb_service->target),
-               target_state_name(gdb_service->target),
+               target_name(target),
+               target_state_name(target),
                gdb_actual_connections);
 
        /* see if an image built with vFlash commands is left */
@@ -1019,7 +1049,7 @@ static int gdb_connection_closed(struct connection *connection)
        }
 
        /* if this connection registered a debug-message receiver delete it */
-       delete_debug_msg_receiver(connection->cmd_ctx, gdb_service->target);
+       delete_debug_msg_receiver(connection->cmd_ctx, target);
 
        if (connection->priv) {
                free(connection->priv);
@@ -1029,9 +1059,9 @@ static int gdb_connection_closed(struct connection *connection)
 
        target_unregister_event_callback(gdb_target_callback_event_handler, connection);
 
-       target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_END);
+       target_call_event_callbacks(target, TARGET_EVENT_GDB_END);
 
-       target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_DETACH);
+       target_call_event_callbacks(target, TARGET_EVENT_GDB_DETACH);
 
        return ERROR_OK;
 }
@@ -1151,6 +1181,9 @@ static int gdb_get_registers_packet(struct connection *connection,
        assert(reg_packet_size > 0);
 
        reg_packet = malloc(reg_packet_size + 1); /* plus one for string termination null */
+       if (reg_packet == NULL)
+               return ERROR_FAIL;
+
        reg_packet_p = reg_packet;
 
        for (i = 0; i < reg_list_size; i++) {
@@ -1342,7 +1375,7 @@ static int gdb_read_memory_packet(struct connection *connection,
 {
        struct target *target = get_target_from_connection(connection);
        char *separator;
-       uint32_t addr = 0;
+       uint64_t addr = 0;
        uint32_t len = 0;
 
        uint8_t *buffer;
@@ -1353,7 +1386,7 @@ static int gdb_read_memory_packet(struct connection *connection,
        /* skip command character */
        packet++;
 
-       addr = strtoul(packet, &separator, 16);
+       addr = strtoull(packet, &separator, 16);
 
        if (*separator != ',') {
                LOG_ERROR("incomplete read memory packet received, dropping connection");
@@ -1370,7 +1403,7 @@ static int gdb_read_memory_packet(struct connection *connection,
 
        buffer = malloc(len);
 
-       LOG_DEBUG("addr: 0x%8.8" PRIx32 ", len: 0x%8.8" PRIx32 "", addr, len);
+       LOG_DEBUG("addr: 0x%16.16" PRIx64 ", len: 0x%8.8" PRIx32 "", addr, len);
 
        retval = target_read_buffer(target, addr, len, buffer);
 
@@ -1395,7 +1428,7 @@ static int gdb_read_memory_packet(struct connection *connection,
        if (retval == ERROR_OK) {
                hex_buffer = malloc(len * 2 + 1);
 
-               int pkt_len = hexify(hex_buffer, (char *)buffer, len, len * 2 + 1);
+               size_t pkt_len = hexify(hex_buffer, buffer, len, len * 2 + 1);
 
                gdb_put_packet(connection, hex_buffer, pkt_len);
 
@@ -1413,7 +1446,7 @@ static int gdb_write_memory_packet(struct connection *connection,
 {
        struct target *target = get_target_from_connection(connection);
        char *separator;
-       uint32_t addr = 0;
+       uint64_t addr = 0;
        uint32_t len = 0;
 
        uint8_t *buffer;
@@ -1422,7 +1455,7 @@ static int gdb_write_memory_packet(struct connection *connection,
        /* skip command character */
        packet++;
 
-       addr = strtoul(packet, &separator, 16);
+       addr = strtoull(packet, &separator, 16);
 
        if (*separator != ',') {
                LOG_ERROR("incomplete write memory packet received, dropping connection");
@@ -1438,9 +1471,9 @@ static int gdb_write_memory_packet(struct connection *connection,
 
        buffer = malloc(len);
 
-       LOG_DEBUG("addr: 0x%8.8" PRIx32 ", len: 0x%8.8" PRIx32 "", addr, len);
+       LOG_DEBUG("addr: 0x%" PRIx64 ", len: 0x%8.8" PRIx32 "", addr, len);
 
-       if (unhexify((char *)buffer, separator, len) != (int)len)
+       if (unhexify(buffer, separator, len) != len)
                LOG_ERROR("unable to decode memory packet");
 
        retval = target_write_buffer(target, addr, len, buffer);
@@ -1460,15 +1493,19 @@ static int gdb_write_memory_binary_packet(struct connection *connection,
 {
        struct target *target = get_target_from_connection(connection);
        char *separator;
-       uint32_t addr = 0;
+       uint64_t addr = 0;
        uint32_t len = 0;
 
        int retval = ERROR_OK;
+       /* Packets larger than fast_limit bytes will be acknowledged instantly on
+        * the assumption that we're in a download and it's important to go as fast
+        * as possible. */
+       uint32_t fast_limit = 8;
 
        /* skip command character */
        packet++;
 
-       addr = strtoul(packet, &separator, 16);
+       addr = strtoull(packet, &separator, 16);
 
        if (*separator != ',') {
                LOG_ERROR("incomplete write memory binary packet received, dropping connection");
@@ -1484,31 +1521,44 @@ static int gdb_write_memory_binary_packet(struct connection *connection,
 
        struct gdb_connection *gdb_connection = connection->priv;
 
-       if (gdb_connection->mem_write_error) {
+       if (gdb_connection->mem_write_error)
                retval = ERROR_FAIL;
-               /* now that we have reported the memory write error, we can clear the condition */
-               gdb_connection->mem_write_error = false;
-       }
 
-       /* By replying the packet *immediately* GDB will send us a new packet
-        * while we write the last one to the target.
-        */
-       if (retval == ERROR_OK)
-               gdb_put_packet(connection, "OK", 2);
-       else {
+       if (retval == ERROR_OK) {
+               if (len >= fast_limit) {
+                       /* By replying the packet *immediately* GDB will send us a new packet
+                        * while we write the last one to the target.
+                        * We only do this for larger writes, so that users who do something like:
+                        * p *((int*)0xdeadbeef)=8675309
+                        * will get immediate feedback that that write failed.
+                        */
+                       gdb_put_packet(connection, "OK", 2);
+               }
+       } else {
                retval = gdb_error(connection, retval);
+               /* now that we have reported the memory write error, we can clear the condition */
+               gdb_connection->mem_write_error = false;
                if (retval != ERROR_OK)
                        return retval;
        }
 
        if (len) {
-               LOG_DEBUG("addr: 0x%8.8" PRIx32 ", len: 0x%8.8" PRIx32 "", addr, len);
+               LOG_DEBUG("addr: 0x%" PRIx64 ", len: 0x%8.8" PRIx32 "", addr, len);
 
                retval = target_write_buffer(target, addr, len, (uint8_t *)separator);
                if (retval != ERROR_OK)
                        gdb_connection->mem_write_error = true;
        }
 
+       if (len < fast_limit) {
+               if (retval != ERROR_OK) {
+                       gdb_error(connection, retval);
+                       gdb_connection->mem_write_error = false;
+               } else {
+                       gdb_put_packet(connection, "OK", 2);
+               }
+       }
+
        return ERROR_OK;
 }
 
@@ -1517,13 +1567,13 @@ static int gdb_step_continue_packet(struct connection *connection,
 {
        struct target *target = get_target_from_connection(connection);
        int current = 0;
-       uint32_t address = 0x0;
+       uint64_t address = 0x0;
        int retval = ERROR_OK;
 
        LOG_DEBUG("-");
 
        if (packet_size > 1)
-               address = strtoul(packet + 1, NULL, 16);
+               address = strtoull(packet + 1, NULL, 16);
        else
                current = 1;
 
@@ -1547,7 +1597,7 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
        int type;
        enum breakpoint_type bp_type = BKPT_SOFT /* dummy init to avoid warning */;
        enum watchpoint_rw wp_type = WPT_READ /* dummy init to avoid warning */;
-       uint32_t address;
+       uint64_t address;
        uint32_t size;
        char *separator;
        int retval;
@@ -1579,7 +1629,7 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
                return ERROR_SERVER_REMOTE_CLOSED;
        }
 
-       address = strtoul(separator + 1, &separator, 16);
+       address = strtoull(separator + 1, &separator, 16);
 
        if (*separator != ',') {
                LOG_ERROR("incomplete breakpoint/watchpoint packet received, dropping connection");
@@ -1859,6 +1909,8 @@ static int gdb_memory_map(struct connection *connection,
 static const char *gdb_get_reg_type_name(enum reg_type type)
 {
        switch (type) {
+               case REG_TYPE_BOOL:
+                       return "bool";
                case REG_TYPE_INT:
                        return "int";
                case REG_TYPE_INT8:
@@ -1871,6 +1923,8 @@ static const char *gdb_get_reg_type_name(enum reg_type type)
                        return "int64";
                case REG_TYPE_INT128:
                        return "int128";
+               case REG_TYPE_UINT:
+                       return "uint";
                case REG_TYPE_UINT8:
                        return "uint8";
                case REG_TYPE_UINT16:
@@ -1898,12 +1952,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",
@@ -1911,6 +1998,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> */
@@ -1918,7 +2019,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,
@@ -1944,13 +2044,24 @@ static int gdb_generate_reg_type_description(struct target *target,
                                        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;
                        }
                } 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> */
@@ -1981,8 +2092,9 @@ static int gdb_generate_reg_type_description(struct target *target,
                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;
                }
@@ -2043,11 +2155,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);
 
@@ -2100,8 +2216,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 {
@@ -2151,6 +2272,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;
@@ -2257,6 +2379,99 @@ error:
        return retval;
 }
 
+static int gdb_generate_thread_list(struct target *target, char **thread_list_out)
+{
+       struct rtos *rtos = target->rtos;
+       int retval = ERROR_OK;
+       char *thread_list = NULL;
+       int pos = 0;
+       int size = 0;
+
+       xml_printf(&retval, &thread_list, &pos, &size,
+                  "<?xml version=\"1.0\"?>\n"
+                  "<threads>\n");
+
+       if (rtos != NULL) {
+               for (int i = 0; i < rtos->thread_count; i++) {
+                       struct thread_detail *thread_detail = &rtos->thread_details[i];
+
+                       if (!thread_detail->exists)
+                               continue;
+
+                       xml_printf(&retval, &thread_list, &pos, &size,
+                                  "<thread id=\"%" PRIx64 "\">", thread_detail->threadid);
+
+                       if (thread_detail->thread_name_str != NULL)
+                               xml_printf(&retval, &thread_list, &pos, &size,
+                                          "Name: %s", thread_detail->thread_name_str);
+
+                       if (thread_detail->extra_info_str != NULL) {
+                               if (thread_detail->thread_name_str != NULL)
+                                       xml_printf(&retval, &thread_list, &pos, &size,
+                                                  ", ");
+                               xml_printf(&retval, &thread_list, &pos, &size,
+                                          thread_detail->extra_info_str);
+                       }
+
+                       xml_printf(&retval, &thread_list, &pos, &size,
+                                  "</thread>\n");
+               }
+       }
+
+       xml_printf(&retval, &thread_list, &pos, &size,
+                  "</threads>\n");
+
+       if (retval == ERROR_OK)
+               *thread_list_out = thread_list;
+       else
+               free(thread_list);
+
+       return retval;
+}
+
+static int gdb_get_thread_list_chunk(struct target *target, char **thread_list,
+               char **chunk, int32_t offset, uint32_t length)
+{
+       if (*thread_list == NULL) {
+               int retval = gdb_generate_thread_list(target, thread_list);
+               if (retval != ERROR_OK) {
+                       LOG_ERROR("Unable to Generate Thread List");
+                       return ERROR_FAIL;
+               }
+       }
+
+       size_t thread_list_length = strlen(*thread_list);
+       char transfer_type;
+
+       length = MIN(length, thread_list_length - offset);
+       if (length < (thread_list_length - offset))
+               transfer_type = 'm';
+       else
+               transfer_type = 'l';
+
+       *chunk = malloc(length + 2 + 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;
+       }
+
+       (*chunk)[0] = transfer_type;
+       strncpy((*chunk) + 1, (*thread_list) + offset, length);
+       (*chunk)[1 + length] = '\0';
+
+       /* After gdb-server sends out last chunk, invalidate thread list. */
+       if (transfer_type == 'l') {
+               free(*thread_list);
+               *thread_list = NULL;
+       }
+
+       return ERROR_OK;
+}
+
 static int gdb_query_packet(struct connection *connection,
                char const *packet, int packet_size)
 {
@@ -2268,7 +2483,7 @@ static int gdb_query_packet(struct connection *connection,
                if (packet_size > 6) {
                        char *cmd;
                        cmd = malloc((packet_size - 6) / 2 + 1);
-                       int len = unhexify(cmd, packet + 6, (packet_size - 6) / 2);
+                       size_t len = unhexify((uint8_t *)cmd, packet + 6, (packet_size - 6) / 2);
                        cmd[len] = 0;
 
                        /* We want to print all debug output to GDB connection */
@@ -2291,13 +2506,13 @@ static int gdb_query_packet(struct connection *connection,
                        char gdb_reply[10];
                        char *separator;
                        uint32_t checksum;
-                       uint32_t addr = 0;
+                       target_addr_t addr = 0;
                        uint32_t len = 0;
 
                        /* skip command character */
                        packet += 5;
 
-                       addr = strtoul(packet, &separator, 16);
+                       addr = strtoull(packet, &separator, 16);
 
                        if (*separator != ',') {
                                LOG_ERROR("incomplete read memory packet received, dropping connection");
@@ -2346,7 +2561,7 @@ static int gdb_query_packet(struct connection *connection,
                        &buffer,
                        &pos,
                        &size,
-                       "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;QStartNoAckMode+",
+                       "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+;vContSupported+",
                        (GDB_BUFFER_SIZE - 1),
                        ((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-',
                        (gdb_target_desc_supported == 1) ? '+' : '-');
@@ -2392,6 +2607,37 @@ static int gdb_query_packet(struct connection *connection,
 
                gdb_put_packet(connection, xml, strlen(xml));
 
+               free(xml);
+               return ERROR_OK;
+       } else if (strncmp(packet, "qXfer:threads:read:", 19) == 0) {
+               char *xml = NULL;
+               int retval = ERROR_OK;
+
+               int offset;
+               unsigned int length;
+
+               /* skip command character */
+               packet += 19;
+
+               if (decode_xfer_read(packet, NULL, &offset, &length) < 0) {
+                       gdb_send_error(connection, 01);
+                       return ERROR_OK;
+               }
+
+               /* Target should prepare correct thread list for annex.
+                * The first character of returned xml is 'm' or 'l'. 'm' for
+                * there are *more* chunks to transfer. 'l' for it is the *last*
+                * chunk of target description.
+                */
+               retval = gdb_get_thread_list_chunk(target, &gdb_connection->thread_list,
+                                                  &xml, offset, length);
+               if (retval != ERROR_OK) {
+                       gdb_error(connection, retval);
+                       return retval;
+               }
+
+               gdb_put_packet(connection, xml, strlen(xml));
+
                free(xml);
                return ERROR_OK;
        } else if (strncmp(packet, "QStartNoAckMode", 15) == 0) {
@@ -2404,13 +2650,207 @@ static int gdb_query_packet(struct connection *connection,
        return ERROR_OK;
 }
 
+static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet, int packet_size)
+{
+       struct gdb_connection *gdb_connection = connection->priv;
+       struct target *target = get_target_from_connection(connection);
+       const char *parse = packet;
+       int retval;
+
+       /* query for vCont supported */
+       if (parse[0] == '?') {
+               if (target->type->step != NULL) {
+                       /* gdb doesn't accept c without C and s without S */
+                       gdb_put_packet(connection, "vCont;c;C;s;S", 13);
+                       return true;
+               }
+               return false;
+       }
+
+       if (parse[0] == ';') {
+               ++parse;
+               --packet_size;
+       }
+
+       /* simple case, a continue packet */
+       if (parse[0] == 'c') {
+               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_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') {
+               bool fake_step = false;
+
+               if (strncmp(parse, "s:", 2) == 0) {
+                       struct target *ct = target;
+                       int current_pc = 1;
+                       int64_t thread_id;
+                       char *endp;
+
+                       parse += 2;
+                       packet_size -= 2;
+
+                       thread_id = strtoll(parse, &endp, 16);
+                       if (endp != NULL) {
+                               packet_size -= endp - parse;
+                               parse = endp;
+                       }
+
+                       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;
+
+                               if (parse[0] == 'c') {
+                                       parse += 1;
+                                       packet_size -= 1;
+
+                                       /* 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 %"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;
+                               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) {
+                               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
+                               gdb_connection->frontend_state = TARGET_RUNNING;
+               } else {
+                       LOG_ERROR("Unknown vCont packet");
+                       return false;
+               }
+               return true;
+       }
+
+       return false;
+}
+
 static int gdb_v_packet(struct connection *connection,
                char const *packet, int packet_size)
 {
        struct gdb_connection *gdb_connection = connection->priv;
-       struct gdb_service *gdb_service = connection->service->priv;
+       struct target *target;
        int result;
 
+       target = get_target_from_connection(connection);
+
+       if (strncmp(packet, "vCont", 5) == 0) {
+               bool handled;
+
+               packet += 5;
+               packet_size -= 5;
+
+               handled = gdb_handle_vcont_packet(connection, packet, packet_size);
+               if (!handled)
+                       gdb_put_packet(connection, "", 0);
+
+               return ERROR_OK;
+       }
+
        /* if flash programming disabled - send a empty reply */
 
        if (gdb_flash_program == 0) {
@@ -2447,18 +2887,18 @@ static int gdb_v_packet(struct connection *connection,
                flash_set_dirty();
 
                /* perform any target specific operations before the erase */
-               target_call_event_callbacks(gdb_service->target,
+               target_call_event_callbacks(target,
                        TARGET_EVENT_GDB_FLASH_ERASE_START);
 
                /* vFlashErase:addr,length messages require region start and
                 * end to be "block" aligned ... if padding is ever needed,
                 * GDB will have become dangerously confused.
                 */
-               result = flash_erase_address_range(gdb_service->target,
-                               false, addr, length);
+               result = flash_erase_address_range(target, false, addr,
+                       length);
 
                /* perform any target specific operations after the erase */
-               target_call_event_callbacks(gdb_service->target,
+               target_call_event_callbacks(target,
                        TARGET_EVENT_GDB_FLASH_ERASE_END);
 
                /* perform erase */
@@ -2513,10 +2953,12 @@ static int gdb_v_packet(struct connection *connection,
 
                /* process the flashing buffer. No need to erase as GDB
                 * always issues a vFlashErase first. */
-               target_call_event_callbacks(gdb_service->target,
+               target_call_event_callbacks(target,
                                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);
+               result = flash_write(target, gdb_connection->vflash_image,
+                       &written, 0);
+               target_call_event_callbacks(target,
+                       TARGET_EVENT_GDB_FLASH_WRITE_END);
                if (result != ERROR_OK) {
                        if (result == ERROR_FLASH_DST_OUT_OF_BANK)
                                gdb_put_packet(connection, "E.memtype", 9);
@@ -2540,9 +2982,8 @@ static int gdb_v_packet(struct connection *connection,
 
 static int gdb_detach(struct connection *connection)
 {
-       struct gdb_service *gdb_service = connection->service->priv;
-
-       target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_DETACH);
+       target_call_event_callbacks(get_target_from_connection(connection),
+               TARGET_EVENT_GDB_DETACH);
 
        return gdb_put_packet(connection, "OK", 2);
 }
@@ -2621,14 +3062,15 @@ static int gdb_input_inner(struct connection *connection)
        /* Do not allocate this on the stack */
        static char gdb_packet_buffer[GDB_BUFFER_SIZE];
 
-       struct gdb_service *gdb_service = connection->service->priv;
-       struct target *target = gdb_service->target;
+       struct target *target;
        char const *packet = gdb_packet_buffer;
        int packet_size;
        int retval;
        struct gdb_connection *gdb_con = connection->priv;
        static int extended_protocol;
 
+       target = get_target_from_connection(connection);
+
        /* drain input buffer. If one of the packets fail, then an error
         * packet is replied, if applicable.
         *
@@ -2798,8 +3240,8 @@ static int gdb_input_inner(struct connection *connection)
                                        break;
                                case 'R':
                                        /* handle extended restart packet */
-                                       breakpoint_clear_target(gdb_service->target);
-                                       watchpoint_clear_target(gdb_service->target);
+                                       breakpoint_clear_target(target);
+                                       watchpoint_clear_target(target);
                                        command_run_linef(connection->cmd_ctx, "ocd_gdb_restart %s",
                                                        target_name(target));
                                        /* set connection as attached after reset */
@@ -2848,7 +3290,12 @@ static int gdb_input_inner(struct connection *connection)
 
                if (gdb_con->ctrl_c) {
                        if (target->state == TARGET_RUNNING) {
-                               retval = target_halt(target);
+                               struct target *t = target;
+                               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;
@@ -2912,6 +3359,11 @@ static int gdb_target_start(struct target *target, const char *port)
 
 static int gdb_target_add_one(struct target *target)
 {
+       if (strcmp(gdb_port, "disabled") == 0) {
+               LOG_INFO("gdb port disabled");
+               return ERROR_OK;
+       }
+
        /*  one gdb instance per smp list */
        if ((target->smp) && (target->gdb_service))
                return ERROR_OK;
@@ -2926,7 +3378,13 @@ static int gdb_target_add_one(struct target *target)
                if (!*end) {
                        if (parse_long(gdb_port_next, &portnumber) == ERROR_OK) {
                                free(gdb_port_next);
-                               gdb_port_next = alloc_printf("%d", portnumber+1);
+                               if (portnumber) {
+                                       gdb_port_next = alloc_printf("%d", portnumber+1);
+                               } else {
+                                       /* Don't increment if gdb_port is 0, since we're just
+                                        * trying to allocate an unused port. */
+                                       gdb_port_next = strdup("0");
+                               }
                        }
                }
        }
@@ -2935,6 +3393,11 @@ static int gdb_target_add_one(struct target *target)
 
 int gdb_target_add_all(struct target *target)
 {
+       if (strcmp(gdb_port, "disabled") == 0) {
+               LOG_INFO("gdb server disabled");
+               return ERROR_OK;
+       }
+
        if (NULL == target) {
                LOG_WARNING("gdb services need one or more targets defined");
                return ERROR_OK;
@@ -3052,7 +3515,7 @@ COMMAND_HANDLER(handle_gdb_save_tdesc_command)
 
        tdesc_length = strlen(tdesc);
 
-       struct fileio fileio;
+       struct fileio *fileio;
        size_t size_written;
 
        char *tdesc_filename = alloc_printf("%s.xml", target_type_name(target));
@@ -3068,9 +3531,9 @@ COMMAND_HANDLER(handle_gdb_save_tdesc_command)
                goto out;
        }
 
-       retval = fileio_write(&fileio, tdesc_length, tdesc, &size_written);
+       retval = fileio_write(fileio, tdesc_length, tdesc, &size_written);
 
-       fileio_close(&fileio);
+       fileio_close(fileio);
 
        if (retval != ERROR_OK)
                LOG_ERROR("Error while writing the tdesc file");
@@ -3100,7 +3563,7 @@ static const struct command_registration gdb_command_handlers[] = {
                        "server listens for the next port number after the "
                        "base port number specified. "
                        "No arguments reports GDB port. \"pipe\" means listen to stdin "
-                       "output to stdout, an integer is base port number, \"disable\" disables "
+                       "output to stdout, an integer is base port number, \"disabled\" disables "
                        "port. Any other string is are interpreted as named pipe to listen to. "
                        "Output pipe is the same name as input pipe, but with 'o' appended.",
                .usage = "[port_num]",
@@ -3156,3 +3619,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);
+}

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)