X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Fserver%2Fgdb_server.c;h=12c03a51547fa0d1896c000d14910ef012dc5484;hb=f476c9eec42d551bc9015089abc47b09058f06b5;hp=ef2ead44f3837be943c9928dbc6434743f851c04;hpb=db429c34d047ac40dadf0087f3b3434551c855d4;p=fw%2Fopenocd diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index ef2ead44f..12c03a515 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "server.h" #include #include "gdb_server.h" @@ -67,7 +68,7 @@ struct target_desc_format { /* private connection data for GDB */ struct gdb_connection { - char buffer[GDB_BUFFER_SIZE]; + char buffer[GDB_BUFFER_SIZE + 1]; /* Extra byte for nul-termination */ char *buf_p; int buf_cnt; int ctrl_c; @@ -724,7 +725,7 @@ 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[45]; + char sig_reply[65]; char stop_reason[20]; char current_thread[25]; int sig_reply_len; @@ -735,17 +736,25 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio if (target->debug_reason == DBG_REASON_EXIT) { sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "W00"); } else { + struct target *ct; + if (target->rtos != NULL) { + target->rtos->current_threadid = target->rtos->current_thread; + target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct); + } else { + ct = target; + } + if (gdb_connection->ctrl_c) { signal_var = 0x2; } else - signal_var = gdb_last_signal(target); + signal_var = gdb_last_signal(ct); stop_reason[0] = '\0'; - if (target->debug_reason == DBG_REASON_WATCHPOINT) { + if (ct->debug_reason == DBG_REASON_WATCHPOINT) { enum watchpoint_rw hit_wp_type; target_addr_t hit_wp_address; - if (watchpoint_hit(target, &hit_wp_type, &hit_wp_address) == ERROR_OK) { + if (watchpoint_hit(ct, &hit_wp_type, &hit_wp_address) == ERROR_OK) { switch (hit_wp_type) { case WPT_WRITE: @@ -767,15 +776,9 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio } current_thread[0] = '\0'; - if (target->rtos != NULL) { - struct target *ct; - snprintf(current_thread, sizeof(current_thread), "thread:%016" PRIx64 ";", + if (target->rtos != NULL) + snprintf(current_thread, sizeof(current_thread), "thread:%" 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); @@ -797,7 +800,7 @@ static void gdb_fileio_reply(struct target *target, struct connection *connectio if (strcmp(target->fileio_info->identifier, "open") == 0) sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 ",%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, - target->fileio_info->param_2, + target->fileio_info->param_2 + 1, /* len + trailing zero */ target->fileio_info->param_3, target->fileio_info->param_4); else if (strcmp(target->fileio_info->identifier, "close") == 0) @@ -821,13 +824,13 @@ static void gdb_fileio_reply(struct target *target, struct connection *connectio else if (strcmp(target->fileio_info->identifier, "rename") == 0) sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64 "/%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, - target->fileio_info->param_2, + target->fileio_info->param_2 + 1, /* len + trailing zero */ target->fileio_info->param_3, - target->fileio_info->param_4); + target->fileio_info->param_4 + 1); /* len + trailing zero */ else if (strcmp(target->fileio_info->identifier, "unlink") == 0) sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, - target->fileio_info->param_2); + target->fileio_info->param_2 + 1); /* len + trailing zero */ else if (strcmp(target->fileio_info->identifier, "stat") == 0) sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64 ",%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, @@ -847,7 +850,7 @@ static void gdb_fileio_reply(struct target *target, struct connection *connectio else if (strcmp(target->fileio_info->identifier, "system") == 0) sprintf(fileio_command, "F%s,%" PRIx64 "/%" PRIx64, target->fileio_info->identifier, target->fileio_info->param_1, - target->fileio_info->param_2); + target->fileio_info->param_2 + 1); /* len + trailing zero */ else if (strcmp(target->fileio_info->identifier, "exit") == 0) { /* If target hits exit syscall, report to GDB the program is terminated. * In addition, let target run its own exit syscall handler. */ @@ -903,7 +906,6 @@ static void gdb_frontend_halted(struct target *target, struct connection *connec static int gdb_target_callback_event_handler(struct target *target, enum target_event event, void *priv) { - int retval; struct connection *connection = priv; struct gdb_service *gdb_service = connection->service->priv; @@ -917,11 +919,6 @@ static int gdb_target_callback_event_handler(struct target *target, case TARGET_EVENT_HALTED: target_call_event_callbacks(target, TARGET_EVENT_GDB_END); break; - case TARGET_EVENT_GDB_FLASH_ERASE_START: - retval = jtag_execute_queue(); - if (retval != ERROR_OK) - return retval; - break; default: break; } @@ -1306,7 +1303,7 @@ static int gdb_get_register_packet(struct connection *connection, if ((target->rtos != NULL) && (ERROR_OK == rtos_get_gdb_reg(connection, reg_num))) return ERROR_OK; - retval = target_get_gdb_reg_list(target, ®_list, ®_list_size, + retval = target_get_gdb_reg_list_noread(target, ®_list, ®_list_size, REG_CLASS_ALL); if (retval != ERROR_OK) return gdb_error(connection, retval); @@ -1342,37 +1339,49 @@ static int gdb_set_register_packet(struct connection *connection, { struct target *target = get_target_from_connection(connection); char *separator; - uint8_t *bin_buf; int reg_num = strtoul(packet + 1, &separator, 16); struct reg **reg_list; int reg_list_size; int retval; +#ifdef _DEBUG_GDB_IO_ LOG_DEBUG("-"); +#endif - retval = target_get_gdb_reg_list(target, ®_list, ®_list_size, + if (*separator != '=') { + LOG_ERROR("GDB 'set register packet', but no '=' following the register number"); + return ERROR_SERVER_REMOTE_CLOSED; + } + size_t chars = strlen(separator + 1); + uint8_t *bin_buf = malloc(chars / 2); + gdb_target_to_reg(target, separator + 1, chars, bin_buf); + + if ((target->rtos != NULL) && + (ERROR_OK == rtos_set_reg(connection, reg_num, bin_buf))) { + free(bin_buf); + gdb_put_packet(connection, "OK", 2); + return ERROR_OK; + } + + retval = target_get_gdb_reg_list_noread(target, ®_list, ®_list_size, REG_CLASS_ALL); - if (retval != ERROR_OK) + if (retval != ERROR_OK) { + free(bin_buf); return gdb_error(connection, retval); + } if (reg_list_size <= reg_num) { LOG_ERROR("gdb requested a non-existing register"); + free(bin_buf); + free(reg_list); return ERROR_SERVER_REMOTE_CLOSED; } - if (*separator != '=') { - LOG_ERROR("GDB 'set register packet', but no '=' following the register number"); - return ERROR_SERVER_REMOTE_CLOSED; - } - - /* convert from GDB-string (target-endian) to hex-string (big-endian) */ - bin_buf = malloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8)); - int chars = (DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2); - - if ((unsigned int)chars != strlen(separator + 1)) { - LOG_ERROR("gdb sent %zu bits for a %d-bit register (%s)", - strlen(separator + 1) * 4, chars * 4, reg_list[reg_num]->name); + if (chars != (DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2)) { + LOG_ERROR("gdb sent %d bits for a %d-bit register (%s)", + (int) chars * 4, reg_list[reg_num]->size, reg_list[reg_num]->name); free(bin_buf); + free(reg_list); return ERROR_SERVER_REMOTE_CLOSED; } @@ -1407,8 +1416,6 @@ static int gdb_error(struct connection *connection, int retval) /* We don't have to worry about the default 2 second timeout for GDB packets, * because GDB breaks up large memory reads into smaller reads. - * - * 8191 bytes by the looks of it. Why 8191 bytes instead of 8192????? */ static int gdb_read_memory_packet(struct connection *connection, char const *packet, int packet_size) @@ -1437,7 +1444,7 @@ static int gdb_read_memory_packet(struct connection *connection, if (!len) { LOG_WARNING("invalid read memory packet received (len == 0)"); - gdb_put_packet(connection, NULL, 0); + gdb_put_packet(connection, "", 0); return ERROR_OK; } @@ -1642,7 +1649,7 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection, char *separator; int retval; - LOG_DEBUG("-"); + LOG_DEBUG("[%s]", target_name(target)); type = strtoul(packet + 1, &separator, 16); @@ -1722,8 +1729,8 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection, /* print out a string and allocate more space as needed, * mainly used for XML at this point */ -static void xml_printf(int *retval, char **xml, int *pos, int *size, - const char *fmt, ...) +static __attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 5, 6))) void xml_printf(int *retval, + char **xml, int *pos, int *size, const char *fmt, ...) { if (*retval != ERROR_OK) return; @@ -1864,7 +1871,7 @@ static int gdb_memory_map(struct connection *connection, if (ram_start < p->base) xml_printf(&retval, &xml, &pos, &size, "\n", + "length=\"" TARGET_ADDR_FMT "\"/>\n", ram_start, p->base - ram_start); /* Report adjacent groups of same-size sectors. So for @@ -1921,11 +1928,10 @@ static int gdb_memory_map(struct connection *connection, if (ram_start != 0) xml_printf(&retval, &xml, &pos, &size, "\n", - ram_start, 0-ram_start); - /* ELSE a flash chip could be at the very end of the 32 bit address - * space, in which case ram_start will be precisely 0 - */ + "length=\"" TARGET_ADDR_FMT "\"/>\n", + ram_start, target_address_max(target) - ram_start + 1); + /* ELSE a flash chip could be at the very end of the address space, in + * which case ram_start will be precisely 0 */ free(banks); @@ -2200,16 +2206,13 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o int reg_list_size; char const *architecture; 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, ®_list, + retval = target_get_gdb_reg_list_noread(target, ®_list, ®_list_size, REG_CLASS_ALL); if (retval != ERROR_OK) { @@ -2249,7 +2252,10 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o /* generate target description according to register list */ if (features != NULL) { while (features[current_feature]) { + char const **arch_defined_types = NULL; + int num_arch_defined_types = 0; + arch_defined_types = calloc(1, sizeof(char *)); xml_printf(&retval, &tdesc, &pos, &size, "\n", features[current_feature]); @@ -2314,6 +2320,7 @@ static int gdb_generate_target_description(struct target *target, char **tdesc_o "\n"); current_feature++; + free(arch_defined_types); } } @@ -2323,7 +2330,6 @@ 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; @@ -2397,7 +2403,7 @@ static int gdb_target_description_supported(struct target *target, int *supporte char const *architecture = target_get_gdb_arch(target); - retval = target_get_gdb_reg_list(target, ®_list, + retval = target_get_gdb_reg_list_noread(target, ®_list, ®_list_size, REG_CLASS_ALL); if (retval != ERROR_OK) { LOG_ERROR("get register list failed"); @@ -2463,7 +2469,7 @@ static int gdb_generate_thread_list(struct target *target, char **thread_list_ou xml_printf(&retval, &thread_list, &pos, &size, ", "); xml_printf(&retval, &thread_list, &pos, &size, - thread_detail->extra_info_str); + "%s", thread_detail->extra_info_str); } xml_printf(&retval, &thread_list, &pos, &size, @@ -2615,7 +2621,7 @@ static int gdb_query_packet(struct connection *connection, &pos, &size, "PacketSize=%x;qXfer:memory-map:read%c;qXfer:features:read%c;qXfer:threads:read+;QStartNoAckMode+;vContSupported+", - (GDB_BUFFER_SIZE - 1), + GDB_BUFFER_SIZE, ((gdb_use_memory_map == 1) && (flash_get_bank_count() > 0)) ? '+' : '-', (gdb_target_desc_supported == 1) ? '+' : '-'); @@ -2792,13 +2798,11 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p 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) { @@ -2850,7 +2854,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p if (gdb_connection->sync) { gdb_connection->sync = false; if (ct->state == TARGET_HALTED) { - LOG_WARNING("stepi ignored. GDB will now fetch the register state " \ + LOG_DEBUG("stepi ignored. GDB will now fetch the register state " \ "from the target."); gdb_sig_halted(connection); log_remove_callback(gdb_log_callback, connection); @@ -2884,14 +2888,103 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p return false; } +static char *next_hex_encoded_field(const char **str, char sep) +{ + size_t hexlen; + const char *hex = *str; + if (hex[0] == '\0') + return NULL; + + const char *end = strchr(hex, sep); + if (end == NULL) + hexlen = strlen(hex); + else + hexlen = end - hex; + *str = hex + hexlen + 1; + + if (hexlen % 2 != 0) { + /* Malformed hex data */ + return NULL; + } + + size_t count = hexlen / 2; + char *decoded = malloc(count + 1); + if (decoded == NULL) + return NULL; + + size_t converted = unhexify((void *)decoded, hex, count); + if (converted != count) { + free(decoded); + return NULL; + } + + decoded[count] = '\0'; + return decoded; +} + +/* handle extended restart packet */ +static void gdb_restart_inferior(struct connection *connection, const char *packet, int packet_size) +{ + struct gdb_connection *gdb_con = connection->priv; + struct target *target = get_target_from_connection(connection); + + 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 */ + gdb_con->attached = true; + /* info rtos parts */ + gdb_thread_packet(connection, packet, packet_size); +} + +static bool gdb_handle_vrun_packet(struct connection *connection, const char *packet, int packet_size) +{ + struct target *target = get_target_from_connection(connection); + const char *parse = packet; + + /* Skip "vRun" */ + parse += 4; + + if (parse[0] != ';') + return false; + parse++; + + /* Skip first field "filename"; don't know what to do with it. */ + free(next_hex_encoded_field(&parse, ';')); + + char *cmdline = next_hex_encoded_field(&parse, ';'); + char *arg; + while (cmdline != NULL && (arg = next_hex_encoded_field(&parse, ';')) != NULL) { + char *new_cmdline = alloc_printf("%s %s", cmdline, arg); + free(cmdline); + free(arg); + cmdline = new_cmdline; + } + + if (cmdline != NULL) { + if (target->semihosting != NULL) { + LOG_INFO("GDB set inferior command line to '%s'", cmdline); + free(target->semihosting->cmdline); + target->semihosting->cmdline = cmdline; + } else { + LOG_INFO("GDB set inferior command line to '%s' but semihosting is unavailable", cmdline); + free(cmdline); + } + } + + gdb_restart_inferior(connection, packet, packet_size); + gdb_put_packet(connection, "S00", 3); + return true; +} + static int gdb_v_packet(struct connection *connection, char const *packet, int packet_size) { struct gdb_connection *gdb_connection = connection->priv; - struct target *target; int result; - target = get_target_from_connection(connection); + struct target *target = get_target_from_connection(connection); if (strncmp(packet, "vCont", 5) == 0) { bool handled; @@ -2906,6 +2999,16 @@ static int gdb_v_packet(struct connection *connection, return ERROR_OK; } + if (strncmp(packet, "vRun", 4) == 0) { + bool handled; + + handled = gdb_handle_vrun_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) { @@ -3118,7 +3221,7 @@ static void gdb_sig_halted(struct connection *connection) static int gdb_input_inner(struct connection *connection) { /* Do not allocate this on the stack */ - static char gdb_packet_buffer[GDB_BUFFER_SIZE]; + static char gdb_packet_buffer[GDB_BUFFER_SIZE + 1]; /* Extra byte for nul-termination */ struct target *target; char const *packet = gdb_packet_buffer; @@ -3141,7 +3244,7 @@ static int gdb_input_inner(struct connection *connection) * drain the rest of the buffer. */ do { - packet_size = GDB_BUFFER_SIZE-1; + packet_size = GDB_BUFFER_SIZE; retval = gdb_get_packet(connection, gdb_packet_buffer, &packet_size); if (retval != ERROR_OK) return retval; @@ -3234,7 +3337,7 @@ static int gdb_input_inner(struct connection *connection) * make only the single stepping have the sync feature... */ nostep = true; - LOG_WARNING("stepi ignored. GDB will now fetch the register state " \ + LOG_DEBUG("stepi ignored. GDB will now fetch the register state " \ "from the target."); } gdb_con->sync = false; @@ -3298,14 +3401,7 @@ static int gdb_input_inner(struct connection *connection) break; case 'R': /* handle extended restart packet */ - 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 */ - gdb_con->attached = true; - /* info rtos parts */ - gdb_thread_packet(connection, packet, packet_size); + gdb_restart_inferior(connection, packet, packet_size); break; case 'j': @@ -3337,7 +3433,7 @@ static int gdb_input_inner(struct connection *connection) default: /* ignore unknown packets */ LOG_DEBUG("ignoring 0x%2.2x packet", packet[0]); - gdb_put_packet(connection, NULL, 0); + gdb_put_packet(connection, "", 0); break; } @@ -3459,7 +3555,7 @@ static int gdb_target_add_one(struct target *target) if (parse_long(gdb_port_next, &portnumber) == ERROR_OK) { free(gdb_port_next); if (portnumber) { - gdb_port_next = alloc_printf("%d", portnumber+1); + gdb_port_next = alloc_printf("%ld", portnumber+1); } else { /* Don't increment if gdb_port is 0, since we're just * trying to allocate an unused port. */ @@ -3495,7 +3591,7 @@ COMMAND_HANDLER(handle_gdb_sync_command) return ERROR_COMMAND_SYNTAX_ERROR; if (current_gdb_connection == NULL) { - command_print(CMD_CTX, + command_print(CMD, "gdb_sync command can only be run from within gdb using \"monitor gdb_sync\""); return ERROR_FAIL; } @@ -3700,6 +3796,7 @@ static const struct command_registration gdb_command_handlers[] = { .handler = handle_gdb_save_tdesc_command, .mode = COMMAND_EXEC, .help = "Save the target description file", + .usage = "", }, COMMAND_REGISTRATION_DONE };