X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Fserver%2Fgdb_server.c;h=d8dbc2c8b75df536e7d63396d8b7507ab332962a;hb=HEAD;hp=fcc87fba1c45a542e5d8f6855107dcee551d3bfe;hpb=5ebb1bdea1dfff9cce430bd17d08e340eea38e03;p=fw%2Fopenocd diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index fcc87fba1..d8dbc2c8b 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /*************************************************************************** * Copyright (C) 2005 by Dominic Rath * * Dominic.Rath@gmx.de * @@ -19,19 +21,6 @@ * * * Copyright (C) 2013 Franck Jullien * * elec4fun@gmail.com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * ***************************************************************************/ #ifdef HAVE_CONFIG_H @@ -1334,6 +1323,8 @@ static int gdb_set_registers_packet(struct connection *connection, packet_p = packet; for (i = 0; i < reg_list_size; i++) { uint8_t *bin_buf; + if (!reg_list[i] || !reg_list[i]->exist || reg_list[i]->hidden) + continue; int chars = (DIV_ROUND_UP(reg_list[i]->size, 8) * 2); if (packet_p + chars > packet + packet_size) @@ -1386,7 +1377,8 @@ static int gdb_get_register_packet(struct connection *connection, if (retval != ERROR_OK) return gdb_error(connection, retval); - if (reg_list_size <= reg_num) { + if ((reg_list_size <= reg_num) || !reg_list[reg_num] || + !reg_list[reg_num]->exist || reg_list[reg_num]->hidden) { LOG_ERROR("gdb requested a non-existing register (reg_num=%d)", reg_num); return ERROR_SERVER_REMOTE_CLOSED; } @@ -1448,7 +1440,8 @@ static int gdb_set_register_packet(struct connection *connection, return gdb_error(connection, retval); } - if (reg_list_size <= reg_num) { + if ((reg_list_size <= reg_num) || !reg_list[reg_num] || + !reg_list[reg_num]->exist || reg_list[reg_num]->hidden) { LOG_ERROR("gdb requested a non-existing register (reg_num=%d)", reg_num); free(bin_buf); free(reg_list); @@ -1781,7 +1774,10 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection, case 1: if (packet[0] == 'Z') { retval = breakpoint_add(target, address, size, bp_type); - if (retval != ERROR_OK) { + if (retval == ERROR_NOT_IMPLEMENTED) { + /* Send empty reply to report that breakpoints of this type are not supported */ + gdb_put_packet(connection, "", 0); + } else if (retval != ERROR_OK) { retval = gdb_error(connection, retval); if (retval != ERROR_OK) return retval; @@ -1798,7 +1794,10 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection, { if (packet[0] == 'Z') { retval = watchpoint_add(target, address, size, wp_type, 0, 0xffffffffu); - if (retval != ERROR_OK) { + if (retval == ERROR_NOT_IMPLEMENTED) { + /* Send empty reply to report that watchpoints of this type are not supported */ + gdb_put_packet(connection, "", 0); + } else if (retval != ERROR_OK) { retval = gdb_error(connection, retval); if (retval != ERROR_OK) return retval; @@ -2750,6 +2749,7 @@ static int gdb_query_packet(struct connection *connection, if (strncmp(packet, "qRcmd,", 6) == 0) { if (packet_size > 6) { + Jim_Interp *interp = cmd_ctx->interp; char *cmd; cmd = malloc((packet_size - 6) / 2 + 1); size_t len = unhexify((uint8_t *)cmd, packet + 6, (packet_size - 6) / 2); @@ -2761,20 +2761,31 @@ static int gdb_query_packet(struct connection *connection, /* some commands need to know the GDB connection, make note of current * GDB connection. */ current_gdb_connection = gdb_connection; + struct target *saved_target_override = cmd_ctx->current_target_override; - cmd_ctx->current_target_override = target; + cmd_ctx->current_target_override = NULL; - int retval = Jim_EvalObj(cmd_ctx->interp, Jim_NewStringObj(cmd_ctx->interp, cmd, -1)); + struct command_context *old_context = Jim_GetAssocData(interp, "context"); + Jim_DeleteAssocData(interp, "context"); + int retval = Jim_SetAssocData(interp, "context", NULL, cmd_ctx); + if (retval == JIM_OK) { + retval = Jim_EvalObj(interp, Jim_NewStringObj(interp, cmd, -1)); + Jim_DeleteAssocData(interp, "context"); + } + int inner_retval = Jim_SetAssocData(interp, "context", NULL, old_context); + if (retval == JIM_OK) + retval = inner_retval; cmd_ctx->current_target_override = saved_target_override; + current_gdb_connection = NULL; target_call_timer_callbacks_now(); gdb_connection->output_flag = GDB_OUTPUT_NO; free(cmd); if (retval == JIM_RETURN) - retval = cmd_ctx->interp->returnCode; + retval = interp->returnCode; int lenmsg; - const char *cretmsg = Jim_GetString(Jim_GetResult(cmd_ctx->interp), &lenmsg); + const char *cretmsg = Jim_GetString(Jim_GetResult(interp), &lenmsg); char *retmsg; if (lenmsg && cretmsg[lenmsg - 1] != '\n') { retmsg = alloc_printf("%s\n", cretmsg); @@ -2954,6 +2965,11 @@ static int gdb_query_packet(struct connection *connection, gdb_connection->noack_mode = 1; gdb_put_packet(connection, "OK", 2); return ERROR_OK; + } else if (target->type->gdb_query_custom) { + char *buffer = NULL; + int ret = target->type->gdb_query_custom(target, packet, &buffer); + gdb_put_packet(connection, buffer, strlen(buffer)); + return ret; } gdb_put_packet(connection, "", 0); @@ -3014,128 +3030,127 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p gdb_running_type = 's'; bool fake_step = false; - if (strncmp(parse, "s:", 2) == 0) { - struct target *ct = target; - int current_pc = 1; - int64_t thread_id; + struct target *ct = target; + int current_pc = 1; + int64_t thread_id; + parse++; + packet_size--; + if (parse[0] == ':') { char *endp; - - parse += 2; - packet_size -= 2; - + parse++; + packet_size--; thread_id = strtoll(parse, &endp, 16); if (endp) { packet_size -= endp - parse; parse = endp; } + } else { + thread_id = 0; + } - if (target->rtos) { - /* FIXME: why is this necessary? rtos state should be up-to-date here already! */ - rtos_update_threads(target); + if (target->rtos) { + /* FIXME: why is this necessary? rtos state should be up-to-date here already! */ + rtos_update_threads(target); - target->rtos->gdb_target_for_threadid(connection, thread_id, &ct); + target->rtos->gdb_target_for_threadid(connection, thread_id, &ct); - /* - * check if the thread to be stepped is the current rtos thread - * if not, we must fake the step - */ - if (target->rtos->current_thread != thread_id) - fake_step = true; - } + /* + * check if the thread to be stepped is the current rtos thread + * if not, we must fake the step + */ + if (target->rtos->current_thread != thread_id) + fake_step = true; + } + + if (parse[0] == ';') { + ++parse; + --packet_size; - if (parse[0] == ';') { - ++parse; - --packet_size; + if (parse[0] == 'c') { + parse += 1; - if (parse[0] == 'c') { + /* check if thread-id follows */ + if (parse[0] == ':') { + int64_t tid; parse += 1; - /* check if thread-id follows */ - if (parse[0] == ':') { - int64_t tid; - parse += 1; - - tid = strtoll(parse, &endp, 16); - if (tid == thread_id) { - /* - * Special case: only step a single thread (core), - * keep the other threads halted. Currently, only - * aarch64 target understands it. Other target types don't - * care (nobody checks the actual value of 'current') - * and it doesn't really matter. This deserves - * a symbolic constant and a formal interface documentation - * at a later time. - */ - LOG_DEBUG("request to step current core only"); - /* uncomment after checking that indeed other targets are safe */ - /*current_pc = 2;*/ - } + tid = strtoll(parse, NULL, 16); + if (tid == thread_id) { + /* + * Special case: only step a single thread (core), + * keep the other threads halted. Currently, only + * aarch64 target understands it. Other target types don't + * care (nobody checks the actual value of 'current') + * and it doesn't really matter. This deserves + * a symbolic constant and a formal interface documentation + * at a later time. + */ + LOG_DEBUG("request to step current core only"); + /* uncomment after checking that indeed other targets are safe */ + /*current_pc = 2;*/ } } } + } - LOG_DEBUG("target %s single-step thread %"PRIx64, target_name(ct), thread_id); - gdb_connection->output_flag = GDB_OUTPUT_ALL; - 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("target %s single-step thread %"PRIx64, target_name(ct), thread_id); + gdb_connection->output_flag = GDB_OUTPUT_ALL; + target_call_event_callbacks(ct, TARGET_EVENT_GDB_START); - LOG_DEBUG("fake step thread %"PRIx64, thread_id); + /* + * work around an annoying gdb behaviour: when the current thread + * is changed in gdb, it assumes that the target can follow and also + * make the thread current. This is an assumption that cannot hold + * for a real target running a multi-threading OS. We just fake + * the step to not trigger an internal error in gdb. See + * https://sourceware.org/bugzilla/show_bug.cgi?id=22925 for details + */ + if (fake_step) { + int sig_reply_len; + char sig_reply[128]; - sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), - "T05thread:%016"PRIx64";", thread_id); + LOG_DEBUG("fake step thread %"PRIx64, thread_id); - gdb_put_packet(connection, sig_reply, sig_reply_len); - gdb_connection->output_flag = GDB_OUTPUT_NO; + sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), + "T05thread:%016"PRIx64";", thread_id); - return true; - } - - /* support for gdb_sync command */ - if (gdb_connection->sync) { - gdb_connection->sync = false; - if (ct->state == TARGET_HALTED) { - LOG_DEBUG("stepi ignored. GDB will now fetch the register state " - "from the target."); - gdb_sig_halted(connection); - gdb_connection->output_flag = GDB_OUTPUT_NO; - } else - gdb_connection->frontend_state = TARGET_RUNNING; - return true; - } + gdb_put_packet(connection, sig_reply, sig_reply_len); + gdb_connection->output_flag = GDB_OUTPUT_NO; - retval = target_step(ct, current_pc, 0, 0); - if (retval == ERROR_TARGET_NOT_HALTED) - LOG_INFO("target %s was not halted when step was requested", target_name(ct)); + return true; + } - /* if step was successful send a reply back to gdb */ - if (retval == ERROR_OK) { - retval = target_poll(ct); - if (retval != ERROR_OK) - LOG_DEBUG("error polling target %s after successful step", target_name(ct)); - /* send back signal information */ - gdb_signal_reply(ct, connection); - /* stop forwarding log packets! */ + /* support for gdb_sync command */ + if (gdb_connection->sync) { + gdb_connection->sync = false; + if (ct->state == TARGET_HALTED) { + LOG_DEBUG("stepi ignored. GDB will now fetch the register state " + "from the target."); + gdb_sig_halted(connection); gdb_connection->output_flag = GDB_OUTPUT_NO; } else gdb_connection->frontend_state = TARGET_RUNNING; - } else { - LOG_ERROR("Unknown vCont packet"); - return false; + return true; } + + retval = target_step(ct, current_pc, 0, 0); + if (retval == ERROR_TARGET_NOT_HALTED) + LOG_INFO("target %s was not halted when step was requested", target_name(ct)); + + /* if step was successful send a reply back to gdb */ + if (retval == ERROR_OK) { + retval = target_poll(ct); + if (retval != ERROR_OK) + LOG_DEBUG("error polling target %s after successful step", target_name(ct)); + /* send back signal information */ + gdb_signal_reply(ct, connection); + /* stop forwarding log packets! */ + gdb_connection->output_flag = GDB_OUTPUT_NO; + } else + gdb_connection->frontend_state = TARGET_RUNNING; return true; } - + LOG_ERROR("Unknown vCont packet"); return false; } @@ -3657,12 +3672,14 @@ static int gdb_input_inner(struct connection *connection) break; case 'j': + /* DEPRECATED */ /* packet supported only by smp target i.e cortex_a.c*/ /* handle smp packet replying coreid played to gbd */ gdb_read_smp_packet(connection, packet, packet_size); break; case 'J': + /* DEPRECATED */ /* packet supported only by smp target i.e cortex_a.c */ /* handle smp packet setting coreid to be played at next * resume to gdb */