diff --git a/src/gdb_main.c b/src/gdb_main.c index 06c506599a9..dc145247cc9 100644 --- a/src/gdb_main.c +++ b/src/gdb_main.c @@ -67,10 +67,10 @@ typedef enum gdb_signal { GDB_SIGLOST = 29, } gdb_signal_e; -#define ERROR_IF_NO_TARGET() \ - if (!cur_target) { \ - gdb_putpacketz("EFF"); \ - break; \ +#define ERROR_IF_NO_TARGET() \ + if (!cur_target) { \ + gdb_put_packet_error(0xffU); \ + break; \ } typedef struct cmd_executer { @@ -83,16 +83,16 @@ target_s *last_target; bool gdb_target_running = false; static bool gdb_needs_detach_notify = false; -static void handle_q_packet(char *packet, size_t len); -static void handle_v_packet(char *packet, size_t len); -static void handle_z_packet(char *packet, size_t len); +static void handle_q_packet(const gdb_packet_s *packet); +static void handle_v_packet(const gdb_packet_s *packet); +static void handle_z_packet(const gdb_packet_s *packet); static void handle_kill_target(void); static void gdb_target_destroy_callback(target_controller_s *tc, target_s *t) { (void)tc; if (cur_target == t) { - gdb_put_notificationz("%Stop:W00"); + gdb_put_notification_str("%Stop:W00"); gdb_out("You are now detached from the previous target.\n"); cur_target = NULL; gdb_needs_detach_notify = true; @@ -114,13 +114,13 @@ target_controller_s gdb_controller = { }; /* execute gdb remote command stored in 'pbuf'. returns immediately, no busy waiting. */ -int32_t gdb_main_loop(target_controller_s *tc, char *pbuf, size_t pbuf_size, size_t size, bool in_syscall) +int32_t gdb_main_loop(target_controller_s *const tc, const gdb_packet_s *const packet, const bool in_syscall) { bool single_step = false; const char *rest = NULL; /* GDB protocol main loop */ - switch (pbuf[0]) { + switch (packet->data[0]) { /* Implementation of these is mandatory! */ case 'g': { /* 'g': Read general registers */ ERROR_IF_NO_TARGET(); @@ -128,28 +128,35 @@ int32_t gdb_main_loop(target_controller_s *tc, char *pbuf, size_t pbuf_size, siz if (reg_size) { uint8_t *gp_regs = alloca(reg_size); target_regs_read(cur_target, gp_regs); - gdb_putpacket(hexify(pbuf, gp_regs, reg_size), reg_size * 2U); + gdb_put_packet_hex(gp_regs, reg_size); } else { - gdb_putpacketz("00"); + /** + * Register data is unavailable + * See: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html#read-registers-packet + * + * ... the stub may also return a string of literal ‘x’ in place of the register data digits, + * to indicate that the corresponding register’s value is unavailable. + */ + gdb_put_packet_str("xx"); } break; } case 'm': { /* 'm addr,len': Read len bytes from addr */ uint32_t addr, len; ERROR_IF_NO_TARGET(); - if (read_hex32(pbuf + 1, &rest, &addr, ',') && read_hex32(rest, NULL, &len, READ_HEX_NO_FOLLOW)) { - if (len > pbuf_size / 2U) { - gdb_putpacketz("E02"); + if (read_hex32(packet->data + 1, &rest, &addr, ',') && read_hex32(rest, NULL, &len, READ_HEX_NO_FOLLOW)) { + if (len > packet->size / 2U) { + gdb_put_packet_error(2U); break; } DEBUG_GDB("m packet: addr = %" PRIx32 ", len = %" PRIx32 "\n", addr, len); uint8_t *mem = alloca(len); if (target_mem32_read(cur_target, mem, addr, len)) - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); else - gdb_putpacket(hexify(pbuf, mem, len), len * 2U); + gdb_put_packet_hex(mem, len); } else - gdb_putpacketz("EFF"); + gdb_put_packet_error(0xffU); break; } case 'G': { /* 'G XX': Write general registers */ @@ -157,30 +164,30 @@ int32_t gdb_main_loop(target_controller_s *tc, char *pbuf, size_t pbuf_size, siz const size_t reg_size = target_regs_size(cur_target); if (reg_size) { uint8_t *gp_regs = alloca(reg_size); - unhexify(gp_regs, &pbuf[1], reg_size); + unhexify(gp_regs, packet->data + 1, reg_size); target_regs_write(cur_target, gp_regs); } - gdb_putpacketz("OK"); + gdb_put_packet_ok(); break; } case 'M': { /* 'M addr,len:XX': Write len bytes to addr */ uint32_t addr = 0; uint32_t len = 0; ERROR_IF_NO_TARGET(); - if (read_hex32(pbuf + 1, &rest, &addr, ',') && read_hex32(rest, &rest, &len, ':')) { - if (len > (size - (size_t)(rest - pbuf)) / 2U) { - gdb_putpacketz("E02"); + if (read_hex32(packet->data + 1, &rest, &addr, ',') && read_hex32(rest, &rest, &len, ':')) { + if (len > (packet->size - (size_t)(rest - packet->data)) / 2U) { + gdb_put_packet_error(2U); break; } DEBUG_GDB("M packet: addr = %" PRIx32 ", len = %" PRIx32 "\n", addr, len); uint8_t *mem = alloca(len); unhexify(mem, rest, len); if (target_mem32_write(cur_target, addr, mem, len)) - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); else - gdb_putpacketz("OK"); + gdb_put_packet_ok(); } else - gdb_putpacketz("EFF"); + gdb_put_packet_error(0xffU); break; } /* @@ -193,10 +200,10 @@ int32_t gdb_main_loop(target_controller_s *tc, char *pbuf, size_t pbuf_size, siz * Since we don't care about the operation just skip it but check there is at least 3 characters * in the packet. */ - if (size >= 3 && read_hex32(pbuf + 2, NULL, &thread_id, READ_HEX_NO_FOLLOW) && thread_id <= 1) - gdb_putpacketz("OK"); + if (packet->size >= 3 && read_hex32(packet->data + 2, NULL, &thread_id, READ_HEX_NO_FOLLOW) && thread_id <= 1) + gdb_put_packet_ok(); else - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); break; } case 's': /* 's [addr]': Single step [start at addr] */ @@ -205,7 +212,7 @@ int32_t gdb_main_loop(target_controller_s *tc, char *pbuf, size_t pbuf_size, siz case 'c': /* 'c [addr]': Continue [at addr] */ case 'C': /* 'C sig[;addr]': Continue with signal [at addr] */ if (!cur_target) { - gdb_putpacketz("X1D"); + gdb_put_packet_str("X1D"); break; } @@ -218,7 +225,7 @@ int32_t gdb_main_loop(target_controller_s *tc, char *pbuf, size_t pbuf_size, siz * but GDB doesn't work without it. */ if (!cur_target) { - gdb_putpacketz("W00"); /* Report "target exited" if no target */ + gdb_put_packet_str("W00"); /* Report "target exited" if no target */ break; } @@ -237,18 +244,25 @@ int32_t gdb_main_loop(target_controller_s *tc, char *pbuf, size_t pbuf_size, siz ERROR_IF_NO_TARGET(); if (cur_target->reg_read) { uint32_t reg; - if (!read_hex32(pbuf + 1, NULL, ®, READ_HEX_NO_FOLLOW)) - gdb_putpacketz("EFF"); + if (!read_hex32(packet->data + 1, NULL, ®, READ_HEX_NO_FOLLOW)) + gdb_put_packet_error(0xffU); else { uint8_t val[8]; const size_t length = target_reg_read(cur_target, reg, val, sizeof(val)); if (length != 0) - gdb_putpacket(hexify(pbuf, val, length), length * 2U); + gdb_put_packet_hex(val, length); else - gdb_putpacketz("EFF"); + gdb_put_packet_error(0xffU); } } else { - gdb_putpacketz("00"); + /** + * Register data is unavailable + * See: https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html#read-registers-packet + * + * ... the stub may also return a string of literal ‘x’ in place of the register data digits, + * to indicate that the corresponding register’s value is unavailable. + */ + gdb_put_packet_str("xx"); } break; } @@ -265,25 +279,25 @@ int32_t gdb_main_loop(target_controller_s *tc, char *pbuf, size_t pbuf_size, siz uint32_t reg; /* Extract the register number and check that '=' follows it */ - if (!read_hex32(pbuf + 1, &rest, ®, '=')) { - gdb_putpacketz("EFF"); + if (!read_hex32(packet->data + 1, &rest, ®, '=')) { + gdb_put_packet_error(0xffU); break; } const size_t value_length = strlen(rest) / 2U; /* If the value is bigger than 4 bytes report error */ if (value_length > 4U) { - gdb_putpacketz("EFF"); + gdb_put_packet_error(0xffU); break; } uint8_t value[4] = {0}; unhexify(value, rest, value_length); /* Finally, write the converted value to the target */ if (target_reg_write(cur_target, reg, value, sizeof(value)) != 0) - gdb_putpacketz("OK"); + gdb_put_packet_ok(); else - gdb_putpacketz("EFF"); + gdb_put_packet_error(0xffU); } else { - gdb_putpacketz("OK"); + gdb_put_packet_ok(); } break; } @@ -291,10 +305,10 @@ int32_t gdb_main_loop(target_controller_s *tc, char *pbuf, size_t pbuf_size, siz case 'F': /* Semihosting call finished */ if (in_syscall) /* Trim off the 'F' before calling semihosting_reply so that it doesn't have to skip it */ - return semihosting_reply(tc, pbuf + 1); + return semihosting_reply(tc, packet->data + 1); else { - DEBUG_GDB("*** F packet when not in syscall! '%s'\n", pbuf); - gdb_putpacketz(""); + DEBUG_GDB("*** F packet when not in syscall! '%s'\n", packet->data); + gdb_put_packet_empty(); } break; @@ -304,7 +318,7 @@ int32_t gdb_main_loop(target_controller_s *tc, char *pbuf, size_t pbuf_size, siz * protocol anyway, but GDB will never send us a 'R' * packet unless we answer 'OK' here. */ - gdb_putpacketz("OK"); + gdb_put_packet_ok(); break; case '\x04': @@ -319,8 +333,8 @@ int32_t gdb_main_loop(target_controller_s *tc, char *pbuf, size_t pbuf_size, siz last_target = cur_target; cur_target = NULL; } - if (pbuf[0] == 'D') - gdb_putpacketz("OK"); + if (packet->data[0] == 'D') + gdb_put_packet_ok(); gdb_set_noackmode(false); break; @@ -344,45 +358,45 @@ int32_t gdb_main_loop(target_controller_s *tc, char *pbuf, size_t pbuf_size, siz target_addr32_t addr; uint32_t len; ERROR_IF_NO_TARGET(); - if (read_hex32(pbuf + 1, &rest, &addr, ',') && read_hex32(rest, &rest, &len, ':')) { - if (len > (size - (size_t)(rest - pbuf))) { - gdb_putpacketz("E02"); + if (read_hex32(packet->data + 1, &rest, &addr, ',') && read_hex32(rest, &rest, &len, ':')) { + if (len > (packet->size - (size_t)(rest - packet->data))) { + gdb_put_packet_error(2U); break; } DEBUG_GDB("X packet: addr = %" PRIx32 ", len = %" PRIx32 "\n", addr, len); if (target_mem32_write(cur_target, addr, rest, len)) - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); else - gdb_putpacketz("OK"); + gdb_put_packet_ok(); } else - gdb_putpacketz("EFF"); + gdb_put_packet_error(0xffU); break; } case 'Q': /* General set packet */ case 'q': /* General query packet */ - handle_q_packet(pbuf, size); + handle_q_packet(packet); break; case 'v': /* Verbose command packet */ - handle_v_packet(pbuf, size); + handle_v_packet(packet); break; /* These packet implement hardware break-/watchpoints */ case 'Z': /* Z type,addr,len: Set breakpoint packet */ case 'z': /* z type,addr,len: Clear breakpoint packet */ ERROR_IF_NO_TARGET(); - handle_z_packet(pbuf, size); + handle_z_packet(packet); break; default: /* Packet not implemented */ - DEBUG_GDB("*** Unsupported packet: %s\n", pbuf); - gdb_putpacketz(""); + DEBUG_GDB("*** Unsupported packet: %s\n", packet->data); + gdb_put_packet_empty(); } return 0; } -static bool exec_command(char *packet, const size_t length, const cmd_executer_s *exec) +static bool exec_command(const char *const packet, const size_t length, const cmd_executer_s *exec) { while (exec->cmd_prefix) { const size_t prefix_length = strlen(exec->cmd_prefix); @@ -408,14 +422,12 @@ static void exec_q_rcmd(const char *packet, const size_t length) const int result = command_process(cur_target, data); if (result < 0) - gdb_putpacketz(""); + gdb_put_packet_empty(); else if (result == 0) - gdb_putpacketz("OK"); + gdb_put_packet_ok(); else { - const char *const response = "Failed\n"; - const size_t response_length = strlen(response); - char *pbuf = alloca(response_length * 2 + 1); - gdb_putpacket(hexify(pbuf, response, response_length), 2 * response_length); + static const char response[] = "Failed\n"; + gdb_put_packet_hex(response, ARRAY_LENGTH(response)); } } @@ -427,21 +439,21 @@ static void handle_q_string_reply(const char *reply, const char *param) const char *rest = NULL; if (!read_hex32(param, &rest, &addr, ',') || !read_hex32(rest, NULL, &len, READ_HEX_NO_FOLLOW)) { - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); return; } if (addr > reply_length) { - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); return; } if (addr == reply_length) { - gdb_putpacketz("l"); + gdb_put_packet_str("l"); return; } size_t output_len = reply_length - addr; if (output_len > len) output_len = len; - gdb_putpacket2("m", 1U, reply + addr, output_len); + gdb_put_packet("m", 1U, reply + addr, output_len, false); } static void exec_q_supported(const char *packet, const size_t length) @@ -455,8 +467,13 @@ static void exec_q_supported(const char *packet, const size_t length) */ gdb_set_noackmode(false); - gdb_putpacket_f("PacketSize=%X;qXfer:memory-map:read+;qXfer:features:read+;" - "vContSupported+" GDB_QSUPPORTED_NOACKMODE, + /* + * The Remote Protocol documentation is not clear on what format the PacketSize feature should be in, + * according to the GDB source code (as of version 15.2) it should be a hexadecimal encoded number + * to be parsed by strtoul() with a base of 16. + */ + gdb_putpacket_str_f("PacketSize=%x;qXfer:memory-map:read+;qXfer:features:read+;" + "vContSupported+" GDB_QSUPPORTED_NOACKMODE, GDB_PACKET_BUFFER_SIZE); } @@ -469,7 +486,7 @@ static void exec_q_memory_map(const char *packet, const size_t length) if (!target) target = last_target; if (!target) { - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); return; } char buf[1024]; @@ -486,7 +503,7 @@ static void exec_q_feature_read(const char *packet, const size_t length) if (!target) target = last_target; if (!target) { - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); return; } const char *const description = target_regs_description(target); @@ -505,14 +522,14 @@ static void exec_q_crc(const char *packet, const size_t length) const char *rest = NULL; if (read_hex32(packet, &rest, &addr, ',') && read_hex32(rest, NULL, &addr_length, READ_HEX_NO_FOLLOW)) { if (!cur_target) { - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); return; } uint32_t crc; if (!bmd_crc32(cur_target, &crc, addr, addr_length)) - gdb_putpacketz("E03"); + gdb_put_packet_error(3U); else - gdb_putpacket_f("C%" PRIx32, crc); + gdb_putpacket_str_f("C%" PRIx32, crc); } } @@ -524,7 +541,7 @@ static void exec_q_c(const char *packet, const size_t length) { (void)packet; (void)length; - gdb_putpacketz("QC1"); + gdb_put_packet_str("QC1"); } /* @@ -538,9 +555,9 @@ static void exec_q_thread_info(const char *packet, const size_t length) { (void)length; if (packet[-11] == 'f' && cur_target) - gdb_putpacketz("m1"); + gdb_put_packet_str("m1"); else - gdb_putpacketz("l"); + gdb_put_packet_str("l"); } /* @@ -554,7 +571,7 @@ static void exec_q_noackmode(const char *packet, const size_t length) (void)packet; (void)length; gdb_set_noackmode(true); - gdb_putpacketz("OK"); + gdb_put_packet_ok(); } /* @@ -575,12 +592,12 @@ static void exec_q_attached(const char *packet, const size_t length) { /* If the packet has a trailing PID, or we're not attached to anything, error response */ if ((length && packet[0] == ':') || !cur_target) - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); /* Check if the target tollerates being reset */ else if (cur_target->target_options & TOPT_INHIBIT_NRST) - gdb_putpacketz("1"); /* It does not. */ + gdb_put_packet_str("1"); /* It does not. */ else - gdb_putpacketz("0"); /* It does tolelrate reset */ + gdb_put_packet_str("0"); /* It does tolelrate reset */ } static const cmd_executer_s q_commands[] = { @@ -607,15 +624,15 @@ static void handle_kill_target(void) } } -static void handle_q_packet(char *packet, const size_t length) +static void handle_q_packet(const gdb_packet_s *const packet) { - if (exec_command(packet, length, q_commands)) + if (exec_command(packet->data, packet->size, q_commands)) return; - DEBUG_GDB("*** Unsupported packet: %s\n", packet); - gdb_putpacket("", 0); + DEBUG_GDB("*** Unsupported packet: %s\n", packet->data); + gdb_put_packet_empty(); } -static void exec_v_attach(const char *packet, const size_t length) +static void exec_v_attach(const char *const packet, const size_t length) { (void)length; @@ -634,13 +651,13 @@ static void exec_v_attach(const char *packet, const size_t length) * https://sourceware.org/pipermail/gdb-patches/2022-April/188058.html * https://sourceware.org/pipermail/gdb-patches/2022-July/190869.html */ - gdb_putpacket_f("T%02Xthread:1;", GDB_SIGTRAP); + gdb_putpacket_str_f("T%02Xthread:1;", GDB_SIGTRAP); } else - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); } else { DEBUG_GDB("*** Unsupported packet: %s\n", packet); - gdb_putpacket("", 0); + gdb_put_packet_empty(); } } @@ -650,7 +667,7 @@ static void exec_v_kill(const char *packet, const size_t length) (void)length; /* Kill the target - we don't actually care about the PID that follows "vKill;" */ handle_kill_target(); - gdb_putpacketz("OK"); + gdb_put_packet_ok(); } static void exec_v_run(const char *packet, const size_t length) @@ -700,7 +717,7 @@ static void exec_v_run(const char *packet, const size_t length) if (cur_target) { target_set_cmdline(cur_target, cmdline, offset); target_reset(cur_target); - gdb_putpacketz("T05"); + gdb_put_packet_str("T05"); } else if (last_target) { cur_target = target_attach(last_target, &gdb_controller); @@ -709,12 +726,12 @@ static void exec_v_run(const char *packet, const size_t length) target_set_cmdline(cur_target, cmdline, offset); target_reset(cur_target); morse(NULL, false); - gdb_putpacketz("T05"); + gdb_put_packet_str("T05"); } else - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); } else - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); } static void exec_v_cont(const char *packet, const size_t length) @@ -733,13 +750,13 @@ static void exec_v_cont(const char *packet, const size_t length) * * TODO: Support the 't' (stop) action needed for non-stop debug so GDB can request a halt. */ - gdb_putpacketz("vCont;c;C;s;t"); + gdb_put_packet_str("vCont;c;C;s;t"); return; } /* Otherwise it's a standard `vCont` packet, check if we're presently attached to a target */ if (!cur_target) { - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); return; } @@ -751,7 +768,7 @@ static void exec_v_cont(const char *packet, const size_t length) case 'c': /* 'c': Continue */ case 'C': /* 'C sig': Continue with signal */ if (!cur_target) { - gdb_putpacketz("X1D"); + gdb_put_packet_str("X1D"); break; } @@ -775,18 +792,18 @@ static void exec_v_flash_erase(const char *packet, const size_t length) /* Erase Flash Memory */ DEBUG_GDB("Flash Erase %08" PRIX32 " %08" PRIX32 "\n", addr, len); if (!cur_target) { - gdb_putpacketz("EFF"); + gdb_put_packet_error(0xffU); return; } if (target_flash_erase(cur_target, addr, len)) - gdb_putpacketz("OK"); + gdb_put_packet_ok(); else { target_flash_complete(cur_target); - gdb_putpacketz("EFF"); + gdb_put_packet_error(0xffU); } } else - gdb_putpacketz("EFF"); + gdb_put_packet_error(0xffU); } static void exec_v_flash_write(const char *packet, const size_t length) @@ -798,13 +815,13 @@ static void exec_v_flash_write(const char *packet, const size_t length) const uint32_t count = length - (size_t)(rest - packet); DEBUG_GDB("Flash Write %08" PRIX32 " %08" PRIX32 "\n", addr, count); if (cur_target && target_flash_write(cur_target, addr, (const uint8_t *)rest, count)) - gdb_putpacketz("OK"); + gdb_put_packet_ok(); else { target_flash_complete(cur_target); - gdb_putpacketz("EFF"); + gdb_put_packet_error(0xffU); } } else - gdb_putpacketz("EFF"); + gdb_put_packet_error(0xffU); } static void exec_v_flash_done(const char *packet, const size_t length) @@ -813,9 +830,9 @@ static void exec_v_flash_done(const char *packet, const size_t length) (void)length; /* Commit flash operations. */ if (target_flash_complete(cur_target)) - gdb_putpacketz("OK"); + gdb_put_packet_ok(); else - gdb_putpacketz("EFF"); + gdb_put_packet_error(0xffU); } static void exec_v_stopped(const char *packet, const size_t length) @@ -823,10 +840,10 @@ static void exec_v_stopped(const char *packet, const size_t length) (void)packet; (void)length; if (gdb_needs_detach_notify) { - gdb_putpacketz("W00"); + gdb_put_packet_str("W00"); gdb_needs_detach_notify = false; } else - gdb_putpacketz("OK"); + gdb_put_packet_ok(); } static const cmd_executer_s v_commands[] = { @@ -841,53 +858,54 @@ static const cmd_executer_s v_commands[] = { {NULL, NULL}, }; -static void handle_v_packet(char *packet, const size_t plen) +static void handle_v_packet(const gdb_packet_s *const packet) { - if (exec_command(packet, plen, v_commands)) + if (exec_command(packet->data, packet->size, v_commands)) return; +#ifndef DEBUG_GDB_IS_NOOP /* * The vMustReplyEmpty is used as a feature test to check how gdbserver handles * unknown packets, don't print an error message for it. */ - if (strcmp(packet, "vMustReplyEmpty") != 0) - DEBUG_GDB("*** Unsupported packet: %s\n", packet); - gdb_putpacketz(""); + static const char must_reply_empty[] = "vMustReplyEmpty"; + if (strncmp(packet->data, must_reply_empty, ARRAY_LENGTH(must_reply_empty) - 1U) != 0) + DEBUG_GDB("*** Unsupported packet: %s\n", packet->data); +#endif + gdb_put_packet_empty(); } -static void handle_z_packet(char *packet, const size_t plen) +static void handle_z_packet(const gdb_packet_s *const packet) { - (void)plen; - uint32_t type; uint32_t len; uint32_t addr; const char *rest = NULL; - if (read_dec32(packet + 1, &rest, &type, ',') && read_hex32(rest, &rest, &addr, ',') && + if (read_dec32(packet->data + 1U, &rest, &type, ',') && read_hex32(rest, &rest, &addr, ',') && read_dec32(rest, NULL, &len, READ_HEX_NO_FOLLOW)) { int ret = 0; - if (packet[0] == 'Z') + if (packet->data[0] == 'Z') ret = target_breakwatch_set(cur_target, type, addr, len); else ret = target_breakwatch_clear(cur_target, type, addr, len); /* If the target handler was unable to set/clear the break/watch-point, return an error */ if (ret < 0) - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); /* If the handler does not support the kind requested, return empty string */ else if (ret > 0) - gdb_putpacketz(""); + gdb_put_packet_empty(); /* Otherwise let GDB know that everything went well */ else - gdb_putpacketz("OK"); + gdb_put_packet_ok(); } else - gdb_putpacketz("E01"); + gdb_put_packet_error(1U); } -void gdb_main(char *pbuf, size_t pbuf_size, size_t size) +void gdb_main(const gdb_packet_s *const packet) { - gdb_main_loop(&gdb_controller, pbuf, pbuf_size, size, false); + gdb_main_loop(&gdb_controller, packet, false); } /* Request halt on the active target */ @@ -897,7 +915,7 @@ void gdb_halt_target(void) target_halt_request(cur_target); else /* Report "target exited" if no target */ - gdb_putpacketz("W00"); + gdb_put_packet_str("W00"); } /* Poll the running target to see if it halted yet */ @@ -905,7 +923,7 @@ void gdb_poll_target(void) { if (!cur_target) { /* Report "target exited" if no target */ - gdb_putpacketz("W00"); + gdb_put_packet_str("W00"); return; } @@ -922,19 +940,19 @@ void gdb_poll_target(void) /* Translate reason to GDB signal */ switch (reason) { case TARGET_HALT_ERROR: - gdb_putpacket_f("X%02X", GDB_SIGLOST); + gdb_putpacket_str_f("X%02X", GDB_SIGLOST); morse("TARGET LOST.", true); break; case TARGET_HALT_REQUEST: - gdb_putpacket_f("T%02Xthread:1;", GDB_SIGINT); + gdb_putpacket_str_f("T%02Xthread:1;", GDB_SIGINT); break; case TARGET_HALT_WATCHPOINT: - gdb_putpacket_f("T%02Xwatch:%08" PRIX32 ";", GDB_SIGTRAP, watch); + gdb_putpacket_str_f("T%02Xwatch:%08" PRIX32 ";", GDB_SIGTRAP, watch); break; case TARGET_HALT_FAULT: - gdb_putpacket_f("T%02Xthread:1;", GDB_SIGSEGV); + gdb_putpacket_str_f("T%02Xthread:1;", GDB_SIGSEGV); break; default: - gdb_putpacket_f("T%02Xthread:1;", GDB_SIGTRAP); + gdb_putpacket_str_f("T%02Xthread:1;", GDB_SIGTRAP); } } diff --git a/src/gdb_packet.c b/src/gdb_packet.c index ed4022757eb..5295ccacfe7 100644 --- a/src/gdb_packet.c +++ b/src/gdb_packet.c @@ -41,6 +41,15 @@ typedef enum packet_state { static bool noackmode = false; +/* This has to be aligned so the remote protocol can re-use it without causing Problems */ +static gdb_packet_s BMD_ALIGN_DEF(8) packet_buffer; + +char *gdb_packet_buffer(void) +{ + /* Return the static packet data buffer */ + return packet_buffer.data; +} + /* https://sourceware.org/gdb/onlinedocs/gdb/Packet-Acknowledgment.html */ void gdb_set_noackmode(bool enable) { @@ -53,7 +62,7 @@ void gdb_set_noackmode(bool enable) * If we were asked after the connection was terminated, sending the ack will have no effect. */ if (!enable && noackmode) - gdb_if_putchar(GDB_PACKET_ACK, 1U); + gdb_if_putchar(GDB_PACKET_ACK, true); /* Log only changes */ if (noackmode != enable) @@ -62,6 +71,43 @@ void gdb_set_noackmode(bool enable) noackmode = enable; } +#ifndef DEBUG_GDB_IS_NOOP +static void gdb_packet_debug(const char *const func, const gdb_packet_s *const packet) +{ + /* Log packet for debugging */ + DEBUG_GDB("%s: ", func); + for (size_t i = 0; i < packet->size; i++) { + const char value = packet->data[i]; + if (value >= ' ' && value < '\x7f') + DEBUG_GDB("%c", value); + else + DEBUG_GDB("\\x%02X", (uint8_t)value); + } + DEBUG_GDB("\n"); +} +#endif + +static inline bool gdb_packet_is_reserved(const char character) +{ + /* Check if the character is a reserved GDB packet character */ + return character == GDB_PACKET_START || character == GDB_PACKET_END || character == GDB_PACKET_ESCAPE || + character == GDB_PACKET_RUNLENGTH_START; +} + +static uint8_t gdb_packet_checksum(const gdb_packet_s *const packet) +{ + /* Calculate the checksum of the packet */ + uint8_t checksum = 0; + for (size_t i = 0; i < packet->size; i++) { + const char character = packet->data[i]; + if (gdb_packet_is_reserved(character)) + checksum += GDB_PACKET_ESCAPE + (character ^ GDB_PACKET_ESCAPE_XOR); + else + checksum += character; + } + return checksum; +} + packet_state_e consume_remote_packet(char *const packet, const size_t size) { #if CONFIG_BMDA == 0 @@ -117,12 +163,9 @@ packet_state_e consume_remote_packet(char *const packet, const size_t size) #endif } -size_t gdb_getpacket(char *const packet, const size_t size) +gdb_packet_s *gdb_packet_receive(void) { packet_state_e state = PACKET_IDLE; /* State of the packet capture */ - - size_t offset = 0; - uint8_t checksum = 0; uint8_t rx_checksum = 0; while (true) { @@ -130,12 +173,12 @@ size_t gdb_getpacket(char *const packet, const size_t size) switch (state) { case PACKET_IDLE: - packet[0U] = rx_char; + packet_buffer.data[0U] = rx_char; if (rx_char == GDB_PACKET_START) { /* Start of GDB packet */ state = PACKET_GDB_CAPTURE; - offset = 0; - checksum = 0; + packet_buffer.size = 0; + packet_buffer.notification = false; } #if CONFIG_BMDA == 0 else if (rx_char == REMOTE_SOM) { @@ -144,23 +187,22 @@ size_t gdb_getpacket(char *const packet, const size_t size) * Let consume_remote_packet handle this * returns PACKET_IDLE or PACKET_GDB_CAPTURE if it detects the start of a GDB packet */ - state = consume_remote_packet(packet, size); - offset = 0; - checksum = 0; + state = consume_remote_packet(packet_buffer.data, GDB_PACKET_BUFFER_SIZE); + packet_buffer.size = 0; } #endif /* EOT (end of transmission) - connection was closed */ - if (packet[0U] == '\x04') { - packet[1U] = 0; - return 1U; + else if (rx_char == '\x04') { + packet_buffer.data[1U] = '\0'; /* Null terminate */ + packet_buffer.size = 1U; + return &packet_buffer; } break; case PACKET_GDB_CAPTURE: if (rx_char == GDB_PACKET_START) { /* Restart GDB packet capture */ - offset = 0; - checksum = 0; + packet_buffer.size = 0; break; } if (rx_char == GDB_PACKET_END) { @@ -171,24 +213,18 @@ size_t gdb_getpacket(char *const packet, const size_t size) break; } - /* Not start or end of packet, add to checksum */ - checksum += rx_char; - /* Add to packet buffer, unless it is an escape char */ if (rx_char == GDB_PACKET_ESCAPE) /* GDB Escaped char */ state = PACKET_GDB_ESCAPE; else /* Add to packet buffer */ - packet[offset++] = rx_char; + packet_buffer.data[packet_buffer.size++] = rx_char; break; case PACKET_GDB_ESCAPE: - /* Add to checksum */ - checksum += rx_char; - /* Resolve escaped char */ - packet[offset++] = rx_char ^ GDB_PACKET_ESCAPE_XOR; + packet_buffer.data[packet_buffer.size++] = rx_char ^ GDB_PACKET_ESCAPE_XOR; /* Return to normal packet capture */ state = PACKET_GDB_CAPTURE; @@ -209,31 +245,25 @@ size_t gdb_getpacket(char *const packet, const size_t size) rx_checksum |= unhex_digit(rx_char); /* BITWISE OR lower nibble with upper nibble */ /* (N)Acknowledge packet */ - gdb_if_putchar(rx_checksum == checksum ? GDB_PACKET_ACK : GDB_PACKET_NACK, 1U); + const bool checksum_ok = gdb_packet_checksum(&packet_buffer) == rx_checksum; + gdb_if_putchar(checksum_ok ? GDB_PACKET_ACK : GDB_PACKET_NACK, true); + if (!checksum_ok) { + /* Checksum error, restart packet capture */ + state = PACKET_IDLE; + break; + } } - if (noackmode || rx_checksum == checksum) { - /* Null terminate packet */ - packet[offset] = '\0'; - - /* Log packet for debugging */ - DEBUG_GDB("%s: ", __func__); - for (size_t j = 0; j < offset; j++) { - const char value = packet[j]; - if (value >= ' ' && value < '\x7f') - DEBUG_GDB("%c", value); - else - DEBUG_GDB("\\x%02X", (uint8_t)value); - } - DEBUG_GDB("\n"); + /* Null terminate packet */ + packet_buffer.data[packet_buffer.size] = '\0'; - /* Return packet captured size */ - return offset; - } +#ifndef DEBUG_GDB_IS_NOOP + /* Log packet for debugging */ + gdb_packet_debug(__func__, &packet_buffer); +#endif - /* Restart packet capture */ - state = PACKET_IDLE; - break; + /* Return captured packet */ + return &packet_buffer; default: /* Something is not right, restart packet capture */ @@ -241,133 +271,192 @@ size_t gdb_getpacket(char *const packet, const size_t size) break; } - if (offset >= size) + if (packet_buffer.size >= GDB_PACKET_BUFFER_SIZE) /* Buffer overflow, restart packet capture */ state = PACKET_IDLE; } } -static void gdb_next_char(const char value, uint8_t *const csum) +static inline bool gdb_get_ack(const uint32_t timeout) { - if (value >= ' ' && value < '\x7f') - DEBUG_GDB("%c", value); - else - DEBUG_GDB("\\x%02X", (uint8_t)value); - if (value == GDB_PACKET_START || value == GDB_PACKET_END || value == GDB_PACKET_ESCAPE || - value == GDB_PACKET_RUNLENGTH_START) { - gdb_if_putchar(GDB_PACKET_ESCAPE, 0); - gdb_if_putchar((char)((uint8_t)value ^ GDB_PACKET_ESCAPE_XOR), 0); - *csum += GDB_PACKET_ESCAPE + ((uint8_t)value ^ GDB_PACKET_ESCAPE_XOR); - } else { - gdb_if_putchar(value, 0); - *csum += value; - } + /* Return true early if NoAckMode is enabled */ + if (noackmode) + return true; + + /* Wait for ACK/NACK */ + return gdb_if_getchar_to(timeout) == GDB_PACKET_ACK; } -void gdb_putpacket2(const char *const packet1, const size_t size1, const char *const packet2, const size_t size2) +static inline void gdb_if_putchar_escaped(const char value) { - char xmit_csum[3]; - size_t tries = 0; - - do { - DEBUG_GDB("%s: ", __func__); - uint8_t csum = 0; - gdb_if_putchar(GDB_PACKET_START, 0); - - for (size_t i = 0; i < size1; ++i) - gdb_next_char(packet1[i], &csum); - for (size_t i = 0; i < size2; ++i) - gdb_next_char(packet2[i], &csum); - - gdb_if_putchar(GDB_PACKET_END, 0); - snprintf(xmit_csum, sizeof(xmit_csum), "%02X", csum); - gdb_if_putchar(xmit_csum[0], 0); - gdb_if_putchar(xmit_csum[1], 1); - DEBUG_GDB("\n"); - } while (!noackmode && gdb_if_getchar_to(2000) != GDB_PACKET_ACK && tries++ < 3U); + /* Escape reserved characters */ + if (gdb_packet_is_reserved(value)) { + gdb_if_putchar(GDB_PACKET_ESCAPE, false); + gdb_if_putchar((char)((uint8_t)value ^ GDB_PACKET_ESCAPE_XOR), false); + } else { + gdb_if_putchar(value, false); + } } -void gdb_putpacket(const char *const packet, const size_t size) +void gdb_packet_send(const gdb_packet_s *const packet) { - char xmit_csum[3]; - size_t tries = 0; - - do { - DEBUG_GDB("%s: ", __func__); - uint8_t csum = 0; - gdb_if_putchar(GDB_PACKET_START, 0); - for (size_t i = 0; i < size; ++i) - gdb_next_char(packet[i], &csum); - gdb_if_putchar(GDB_PACKET_END, 0); - snprintf(xmit_csum, sizeof(xmit_csum), "%02X", csum); - gdb_if_putchar(xmit_csum[0], 0); - gdb_if_putchar(xmit_csum[1], 1); - DEBUG_GDB("\n"); - } while (!noackmode && gdb_if_getchar_to(2000) != GDB_PACKET_ACK && tries++ < 3U); + /* Calculate checksum first to avoid re-calculation */ + const uint8_t checksum = gdb_packet_checksum(packet); + + /* Attempt packet transmission up to retries */ + for (size_t attempt = 0U; attempt < GDB_PACKET_RETRIES; attempt++) { + /* Write start of packet */ + gdb_if_putchar(packet->notification ? GDB_PACKET_NOTIFICATION_START : GDB_PACKET_START, false); + + /* Write packet data */ + for (size_t i = 0; i < packet->size; i++) + gdb_if_putchar_escaped(packet->data[i]); + + /* Write end of packet */ + gdb_if_putchar(GDB_PACKET_END, false); + + /* Write checksum and flush the buffer */ + gdb_if_putchar(hex_digit(checksum >> 4U), false); + gdb_if_putchar(hex_digit(checksum & 0xfU), true); + +#ifndef DEBUG_GDB_IS_NOOP + /* Log packet for debugging */ + gdb_packet_debug(__func__, packet); +#endif + + /* Wait for ACK/NACK on standard packets */ + if (packet->notification || gdb_get_ack(2000U)) + break; + } } -void gdb_put_notification(const char *const packet, const size_t size) +void gdb_put_packet(const char *preamble, size_t preamble_size, const char *data, size_t data_size, bool hex_data) { - char xmit_csum[3]; - - DEBUG_GDB("%s: ", __func__); - uint8_t csum = 0; - gdb_if_putchar(GDB_PACKET_NOTIFICATION_START, 0); - for (size_t i = 0; i < size; ++i) - gdb_next_char(packet[i], &csum); - gdb_if_putchar(GDB_PACKET_END, 0); - snprintf(xmit_csum, sizeof(xmit_csum), "%02X", csum); - gdb_if_putchar(xmit_csum[0], 0); - gdb_if_putchar(xmit_csum[1], 1); - DEBUG_GDB("\n"); + /* + * Create a packet using the internal packet buffer + * This destroys the previous packet in the buffer + * any packets obtained from gdb_packet_receive() will be invalidated + */ + packet_buffer.notification = false; + packet_buffer.size = 0; + + /* + * Copy the preamble and data into the packet buffer, limited by the buffer size + * + * This considers GDB_PACKET_BUFFER_SIZE to be the maximum size of the packet + * But it does not take into consideration the extra space needed for escaping + * This is safe because the escaping is done during the actual packet transmission + * but it will result in a packet larger than what we told GDB we could handle + */ + if (preamble != NULL && preamble_size > 0) { + preamble_size = MIN(preamble_size, GDB_PACKET_BUFFER_SIZE); + memcpy(packet_buffer.data, preamble, preamble_size); + packet_buffer.size = preamble_size; + } + + /* Add the data to the packet buffer and transform it if needed */ + if (data != NULL && data_size > 0) { + /* Hex data doubles in size */ + if (hex_data) + data_size *= 2U; + + /* Limit the data size to the remaining space in the packet buffer */ + const size_t remaining_size = GDB_PACKET_BUFFER_SIZE - packet_buffer.size; + data_size = MIN(data_size, remaining_size); + + /* Copy the data into the packet buffer */ + if (hex_data) + hexify(packet_buffer.data + packet_buffer.size, data, data_size / 2U); + else + memcpy(packet_buffer.data + packet_buffer.size, data, data_size); + packet_buffer.size += data_size; + } + + /* Transmit the packet */ + gdb_packet_send(&packet_buffer); } -void gdb_putpacket_f(const char *const fmt, ...) +void gdb_putpacket_str_f(const char *const fmt, ...) { - va_list ap; - char *buf = NULL; + /* + * Create a packet using the internal packet buffer + * This destroys the previous packet in the buffer + * any packets obtained from gdb_packet_receive() will be invalidated + */ + packet_buffer.notification = false; + /* + * Format the string directly into the packet buffer + * This considers GDB_PACKET_BUFFER_SIZE to be the maximum size of the string + * But it does not take into consideration the extra space needed for escaping + * This is safe because the escaping is done during the actual packet transmission + * but it will result in a packet larger than what we told GDB we could handle + */ + va_list ap; va_start(ap, fmt); - const int size = vasprintf(&buf, fmt, ap); - if (size < 0) { - /* Heap exhaustion. Report with puts() elsewhere. */ - DEBUG_ERROR("gdb_putpacket_f: vasprintf failed\n"); - } else { - gdb_putpacket(buf, size); - free(buf); - } + vsnprintf(packet_buffer.data, sizeof(packet_buffer.data), fmt, ap); va_end(ap); + + /* Get the size of the formatted string */ + packet_buffer.size = strnlen(packet_buffer.data, GDB_PACKET_BUFFER_SIZE); + + /* Transmit the packet */ + gdb_packet_send(&packet_buffer); } -void gdb_out(const char *const buf) +void gdb_put_notification_str(const char *const str) { - const size_t buf_len = strlen(buf); - char *hexdata = calloc(1, 2U * buf_len + 1U); - if (!hexdata) - return; - - hexify(hexdata, buf, buf_len); - gdb_putpacket2("O", 1, hexdata, 2U * buf_len); - free(hexdata); + /* + * Create a packet using the internal packet buffer + * This destroys the previous packet in the buffer + * any packets obtained from gdb_packet_receive() will be invalidated + */ + packet_buffer.notification = true; + + packet_buffer.size = strnlen(str, GDB_PACKET_BUFFER_SIZE); + memcpy(packet_buffer.data, str, packet_buffer.size); + + /* Transmit the packet */ + gdb_packet_send(&packet_buffer); +} + +void gdb_out(const char *const str) +{ + /** + * Program console output packet + * See https://sourceware.org/gdb/current/onlinedocs/gdb.html/Stop-Reply-Packets.html#Stop-Reply-Packets + * + * Format: ‘O XX…’ + * ‘XX…’ is hex encoding of ASCII data, to be written as the program’s console output. + * + * Can happen at any time while the program is running and the debugger should continue to wait for ‘W’, ‘T’, etc. + * This reply is not permitted in non-stop mode. + */ + gdb_put_packet("O", 1U, str, strnlen(str, GDB_OUT_PACKET_MAX_SIZE), true); } void gdb_voutf(const char *const fmt, va_list ap) { - char *buf = NULL; - if (vasprintf(&buf, fmt, ap) < 0) { - /* Heap exhaustion. Report with puts() elsewhere. */ - DEBUG_ERROR("gdb_voutf: vasprintf failed\n"); - return; - } + /* + * We could technically do the formatting and transformation in a single buffer reducing stack usage + * But it is a bit more complex and likely slower, we would need to spread the characters out such + * that each occupies two bytes, and then we could hex them in place + * + * If this stack usage proves to be a problem, we can revisit this + */ + char str_scratch[GDB_OUT_PACKET_MAX_SIZE + 1U]; - gdb_out(buf); - free(buf); + /* Format the string into the scratch buffer */ + vsnprintf(str_scratch, sizeof(str_scratch), fmt, ap); + + /* Delegate the rest of the work to gdb_out */ + gdb_out(str_scratch); } void gdb_outf(const char *const fmt, ...) { + /* Wrap the va_list version of gdb_voutf */ va_list ap; - va_start(ap, fmt); gdb_voutf(fmt, ap); va_end(ap); diff --git a/src/hex_utils.c b/src/hex_utils.c index f482a916588..fe60c42155b 100644 --- a/src/hex_utils.c +++ b/src/hex_utils.c @@ -52,8 +52,7 @@ char *hexify(char *const dst, const void *const buf, const size_t size) dst[dst_idx++] = hex_digit(src[src_idx] & 0xfU); } - /* Make sure the result is NUL terminated */ - dst[dst_idx] = '\0'; + /* The hexifued string is *NOT* NUL terminated */ return dst; } diff --git a/src/include/gdb_if.h b/src/include/gdb_if.h index 529ea808061..bf9d0964365 100644 --- a/src/include/gdb_if.h +++ b/src/include/gdb_if.h @@ -31,6 +31,7 @@ char gdb_if_getchar(void); char gdb_if_getchar_to(uint32_t timeout); /* sending gdb_if_putchar(0, true) seems to work as keep alive */ -void gdb_if_putchar(char c, int flush); +void gdb_if_putchar(char c, bool flush); +void gdb_if_flush(bool force); #endif /* INCLUDE_GDB_IF_H */ diff --git a/src/include/gdb_main.h b/src/include/gdb_main.h index 066841e5909..29982b94e02 100644 --- a/src/include/gdb_main.h +++ b/src/include/gdb_main.h @@ -22,18 +22,13 @@ #define INCLUDE_GDB_MAIN_H #include "target.h" - -/* Allow override in other platforms if needed */ -#ifndef GDB_PACKET_BUFFER_SIZE -#define GDB_PACKET_BUFFER_SIZE 1024U -#endif +#include "gdb_packet.h" extern bool gdb_target_running; extern target_s *cur_target; void gdb_poll_target(void); -void gdb_main(char *pbuf, size_t pbuf_size, size_t size); -int32_t gdb_main_loop(target_controller_s *tc, char *pbuf, size_t pbuf_size, size_t size, bool in_syscall); -char *gdb_packet_buffer(void); +void gdb_main(const gdb_packet_s *packet); +int32_t gdb_main_loop(target_controller_s *tc, const gdb_packet_s *packet, bool in_syscall); #endif /* INCLUDE_GDB_MAIN_H */ diff --git a/src/include/gdb_packet.h b/src/include/gdb_packet.h index a35666a50c1..7046cfe86c7 100644 --- a/src/include/gdb_packet.h +++ b/src/include/gdb_packet.h @@ -25,6 +25,14 @@ #include #include +/* Allow override in other platforms if needed */ +#ifndef GDB_PACKET_BUFFER_SIZE +#define GDB_PACKET_BUFFER_SIZE 1024U +#endif + +/* Limit out packet string size to the maximum packet size before hexifying */ +#define GDB_OUT_PACKET_MAX_SIZE ((GDB_PACKET_BUFFER_SIZE - 1U) / 2U) + #define GDB_PACKET_START '$' #define GDB_PACKET_END '#' #define GDB_PACKET_ACK '+' @@ -34,6 +42,8 @@ #define GDB_PACKET_NOTIFICATION_START '%' #define GDB_PACKET_ESCAPE_XOR (0x20U) +#define GDB_PACKET_RETRIES 3U /* Number of times to retry sending a packet */ + #if defined(__MINGW32__) || defined(__MINGW64__) || defined(__CYGWIN__) #define GDB_FORMAT_ATTR __attribute__((format(__MINGW_PRINTF_FORMAT, 1, 2))) #elif defined(__GNUC__) || defined(__clang__) @@ -42,17 +52,99 @@ #define GDB_FORMAT_ATTR #endif +/* + * GDB packet structure + * This is used to store the packet data during transmission and reception + * This will be statically allocated and aligned to 8 bytes to allow the remote protocol to re-use it + * A single packet instance exists in the system and is re-used for all packet operations + * This means transmiting a packet will invalidate any previously obtained packets + * Do not use this structure directly or you might risk runing out of memory + */ +typedef struct gdb_packet { + /* Data must be first to ensure alignment */ + char data[GDB_PACKET_BUFFER_SIZE + 1U]; /* Packet data */ + size_t size; /* Packet data size */ + bool notification; /* Notification packet */ +} gdb_packet_s; + +/* GDB packet transmission configuration */ void gdb_set_noackmode(bool enable); -size_t gdb_getpacket(char *packet, size_t size); -void gdb_putpacket(const char *packet, size_t size); -void gdb_putpacket2(const char *packet1, size_t size1, const char *packet2, size_t size2); -#define gdb_putpacketz(packet) gdb_putpacket((packet), strlen(packet)) -void gdb_putpacket_f(const char *packet, ...) GDB_FORMAT_ATTR; -void gdb_put_notification(const char *packet, size_t size); -#define gdb_put_notificationz(packet) gdb_put_notification((packet), strlen(packet)) - -void gdb_out(const char *buf); -void gdb_voutf(const char *fmt, va_list); + +/* Raw GDB packet transmission */ +gdb_packet_s *gdb_packet_receive(void); +void gdb_packet_send(const gdb_packet_s *packet); + +char *gdb_packet_buffer(void); + +/* Convenience wrappers */ +void gdb_put_packet(const char *preamble, size_t preamble_size, const char *data, size_t data_size, bool hex_data); + +static inline void gdb_put_packet_empty(void) +{ + /** + * Empty response packet + * See https://sourceware.org/gdb/current/onlinedocs/gdb.html/Standard-Replies.html#Standard-Replies + * + * An empty response (raw character sequence ‘$#00’) means the command is not supported by the stub. + */ + gdb_put_packet(NULL, 0, NULL, 0, false); +} + +static inline void gdb_put_packet_str(const char *const str) +{ + gdb_put_packet(str, strlen(str), NULL, 0, false); +} + +static inline void gdb_put_packet_hex(const void *const data, const size_t size) +{ + gdb_put_packet(NULL, 0, (const char *)data, size, true); +} + +static inline void gdb_put_packet_ok(void) +{ + /** + * OK response packet + * + * This is a common response to acknowledge a command was successful. + */ + gdb_put_packet_str("OK"); +} + +static inline void gdb_put_packet_error(const uint8_t error) +{ + /* + * Error response packet + * See https://sourceware.org/gdb/current/onlinedocs/gdb.html/Standard-Replies.html#Standard-Replies + * + * Format: ‘E xx’ + * xx is a two-digit hexadecimal error number. + * In almost all cases, the protocol does not specify the meaning of the error numbers + * GDB usually ignores the numbers, or displays them to the user without further interpretation. + * + * Textual error messages send the error text instead of the error number, but this response + * is not guaranteed to be understood by GDB for all requests, the GDB feature error-message + * lets us know if it is supported. + * + * TODO: implement the error-message GDB feature, so we can send textual error messages. + * + * Format: ‘E.errtext’ + * errtext is the textual error message, encoded in ASCII. + */ + gdb_put_packet("E", 1U, (const char *)&error, 1U, true); +} + +void gdb_put_notification_str(const char *const str); + +/* Formatted output */ +void gdb_putpacket_str_f(const char *fmt, ...) GDB_FORMAT_ATTR; + +/** + * Warning: gdb_(v)out(f) functions may truncate the output string if it is too long + * The output string is limited by the constant GDB_OUT_PACKET_MAX_SIZE derived from + * GDB_PACKET_BUFFER_SIZE. By default this is 511 characters. + */ +void gdb_out(const char *str); +void gdb_voutf(const char *fmt, va_list ap); void gdb_outf(const char *fmt, ...) GDB_FORMAT_ATTR; #endif /* INCLUDE_GDB_PACKET_H */ diff --git a/src/include/general.h b/src/include/general.h index 5a2f2d421da..34278e949c5 100644 --- a/src/include/general.h +++ b/src/include/general.h @@ -166,22 +166,6 @@ void debug_serial_send_stdout(const uint8_t *data, size_t len); #ifdef _MSC_VER #define strcasecmp _stricmp #define strncasecmp _strnicmp - -// FIXME: BMDA still uses this function in gdb_packet.c -// It's defined here as an export from utils.c would pollute the ABI of libbmd -static inline int vasprintf(char **strp, const char *const fmt, va_list ap) -{ - const int actual_size = vsnprintf(NULL, 0, fmt, ap); - if (actual_size < 0) - return -1; - - *strp = malloc(actual_size + 1); - if (!*strp) - return -1; - - return vsnprintf(*strp, actual_size + 1, fmt, ap); -} - #endif /* _MSC_VER */ #ifndef PLATFORM_IDENT_DYNAMIC diff --git a/src/include/stdio_newlib.h b/src/include/stdio_newlib.h index 095d154e1f0..36eb963aec0 100644 --- a/src/include/stdio_newlib.h +++ b/src/include/stdio_newlib.h @@ -43,4 +43,9 @@ #endif #define snprintf sniprintf +#ifdef vsnprintf +#undef vsnprintf +#endif +#define vsnprintf vsniprintf + #endif /* STDIO_NEWLIB_H */ diff --git a/src/main.c b/src/main.c index ffa9a7ee333..00f7dd9e153 100644 --- a/src/main.c +++ b/src/main.c @@ -33,14 +33,6 @@ #include "rtt.h" #endif -/* This has to be aligned so the remote protocol can re-use it without causing Problems */ -static char BMD_ALIGN_DEF(8) pbuf[GDB_PACKET_BUFFER_SIZE + 1U]; - -char *gdb_packet_buffer() -{ - return pbuf; -} - static void bmp_poll_loop(void) { SET_IDLE_STATE(false); @@ -62,11 +54,11 @@ static void bmp_poll_loop(void) } SET_IDLE_STATE(true); - size_t size = gdb_getpacket(pbuf, GDB_PACKET_BUFFER_SIZE); + const gdb_packet_s *const packet = gdb_packet_receive(); // If port closed and target detached, stay idle - if (pbuf[0] != '\x04' || cur_target) + if (packet->data[0] != '\x04' || cur_target) SET_IDLE_STATE(false); - gdb_main(pbuf, GDB_PACKET_BUFFER_SIZE, size); + gdb_main(packet); } #if CONFIG_BMDA == 1 @@ -85,7 +77,7 @@ int main(void) } CATCH () { default: - gdb_putpacketz("EFF"); + gdb_put_packet_error(0xffU); target_list_free(); gdb_outf("Uncaught exception: %s\n", exception_frame.msg); morse("TARGET LOST.", true); diff --git a/src/platforms/common/stm32/gdb_if.c b/src/platforms/common/stm32/gdb_if.c index 62db91c7165..516ffda36fa 100644 --- a/src/platforms/common/stm32/gdb_if.c +++ b/src/platforms/common/stm32/gdb_if.c @@ -41,32 +41,39 @@ static volatile uint32_t count_new; static char double_buffer_out[CDCACM_PACKET_SIZE]; #endif -void gdb_if_putchar(const char c, const int flush) +void gdb_if_putchar(const char c, const bool flush) { buffer_in[count_in++] = c; - if (flush || count_in == CDCACM_PACKET_SIZE) { - /* Refuse to send if USB isn't configured, and - * don't bother if nobody's listening */ - if (usb_get_config() != 1 || !gdb_serial_get_dtr()) { - count_in = 0; - return; - } - while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, buffer_in, count_in) <= 0) - continue; + if (flush || count_in == CDCACM_PACKET_SIZE) + gdb_if_flush(flush); +} - if (flush && count_in == CDCACM_PACKET_SIZE) { - /* We need to send an empty packet for some hosts - * to accept this as a complete transfer. */ - /* libopencm3 needs a change for us to confirm when - * that transfer is complete, so we just send a packet - * containing a null byte for now. - */ - while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, "\0", 1) <= 0) - continue; - } +void gdb_if_flush(const bool force) +{ + /* Flush only if there is data to flush */ + if (count_in == 0U) + return; - count_in = 0; + /* Refuse to send if USB isn't configured, and don't bother if nobody's listening */ + if (usb_get_config() != 1U || !gdb_serial_get_dtr()) { + count_in = 0U; + return; } + while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, buffer_in, count_in) <= 0U) + continue; + + /* We need to send an empty packet for some hosts to accept this as a complete transfer. */ + if (force && count_in == CDCACM_PACKET_SIZE) { + /* + * libopencm3 needs a change for us to confirm when that transfer is complete, + * so we just send a packet containing a null character for now. + */ + while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, "\0", 1U) <= 0U) + continue; + } + + /* Reset the buffer */ + count_in = 0U; } #if defined(STM32F4) || defined(STM32F7) diff --git a/src/platforms/common/tm4c/gdb_if.c b/src/platforms/common/tm4c/gdb_if.c index 031e85c3f79..d1ddca2ee5e 100644 --- a/src/platforms/common/tm4c/gdb_if.c +++ b/src/platforms/common/tm4c/gdb_if.c @@ -34,20 +34,39 @@ static uint32_t count_in = 0; static volatile char buffer_out[16 * CDCACM_PACKET_SIZE]; static char buffer_in[CDCACM_PACKET_SIZE]; -void gdb_if_putchar(char c, int flush) +void gdb_if_putchar(const char c, const bool flush) { buffer_in[count_in++] = c; - if (flush || count_in == CDCACM_PACKET_SIZE) { - /* Refuse to send if USB isn't configured, and - * don't bother if nobody's listening */ - if (usb_get_config() != 1 || !gdb_serial_get_dtr()) { - count_in = 0; - return; - } - while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, buffer_in, count_in) <= 0) + if (flush || count_in == CDCACM_PACKET_SIZE) + gdb_if_flush(flush); +} + +void gdb_if_flush(const bool force) +{ + /* Flush only if there is data to flush */ + if (count_in == 0U) + return; + + /* Refuse to send if USB isn't configured, and don't bother if nobody's listening */ + if (usb_get_config() != 1U || !gdb_serial_get_dtr()) { + count_in = 0U; + return; + } + while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, buffer_in, count_in) <= 0U) + continue; + + /* We need to send an empty packet for some hosts to accept this as a complete transfer. */ + if (force && count_in == CDCACM_PACKET_SIZE) { + /* + * libopencm3 needs a change for us to confirm when that transfer is complete, + * so we just send a packet containing a null character for now. + */ + while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, "\0", 1U) <= 0U) continue; - count_in = 0; } + + /* Reset the buffer */ + count_in = 0U; } void gdb_usb_out_cb(usbd_device *dev, uint8_t ep) diff --git a/src/platforms/ctxlink/WiFi_Server.c b/src/platforms/ctxlink/WiFi_Server.c index 4b2f9eea46e..19b9616d50c 100644 --- a/src/platforms/ctxlink/WiFi_Server.c +++ b/src/platforms/ctxlink/WiFi_Server.c @@ -1475,17 +1475,28 @@ void send_swo_trace_data(uint8_t *buffer, uint8_t length) do_awo_trace_send(); } -void wifi_gdb_putchar(uint8_t ch, int flush) +void wifi_gdb_putchar(const uint8_t ch, const bool flush) { send_buffer[send_count++] = ch; - if (flush != 0 || send_count >= sizeof(send_buffer)) { - // TODO is this check required now, looks like a debug test left in place? - int len = (int)send_count; - if (len <= 0) - DEBUG_WARN("WiFi_putchar bad count\r\n"); - send_count = 0; - DEBUG_WARN("Wifi_putchar %c\r\n", send_buffer[0]); - send(gdb_client_socket, &send_buffer[0], len, 0); - memset(&send_buffer[0], 0x00, sizeof(send_buffer)); - } + if (flush || send_count >= sizeof(send_buffer)) + wifi_gdb_flush(flush); +} + +void wifi_gdb_flush(const bool force) +{ + (void)force; + + /* Flush only if there is data to flush */ + if (send_count == 0U) + return; + + // TODO is this check required now, looks like a debug test left in place? + if (send_count <= 0U) + DEBUG_WARN("WiFi_putchar bad count\r\n"); + DEBUG_WARN("Wifi_putchar %c\r\n", send_buffer[0]); + send(gdb_client_socket, &send_buffer[0], send_count, 0); + + /* Reset the buffer */ + send_count = 0U; + memset(&send_buffer[0], 0x00, sizeof(send_buffer)); } diff --git a/src/platforms/ctxlink/WiFi_Server.h b/src/platforms/ctxlink/WiFi_Server.h index 9810a83cbb8..fe34ba0d02d 100644 --- a/src/platforms/ctxlink/WiFi_Server.h +++ b/src/platforms/ctxlink/WiFi_Server.h @@ -39,7 +39,8 @@ void wifi_setup_swo_trace_server(void); bool is_swo_trace_client_connected(void); void send_swo_trace_data(uint8_t *buffer, uint8_t length); -void wifi_gdb_putchar(uint8_t ch, int flush); +void wifi_gdb_putchar(uint8_t ch, bool flush); +void wifi_gdb_flush(bool force); bool wifi_got_client(void); uint8_t wifi_get_next(void); uint8_t wifi_get_next_to(uint32_t timeout); diff --git a/src/platforms/ctxlink/ctxlink_gdb_if.c b/src/platforms/ctxlink/ctxlink_gdb_if.c index ca1cf24ff07..6288c1e0c82 100644 --- a/src/platforms/ctxlink/ctxlink_gdb_if.c +++ b/src/platforms/ctxlink/ctxlink_gdb_if.c @@ -42,32 +42,39 @@ static char buffer_in[CDCACM_PACKET_SIZE]; static volatile uint32_t count_new; static char double_buffer_out[CDCACM_PACKET_SIZE]; -void gdb_usb_putchar(const char ch, const int flush) +void gdb_usb_flush(const bool force) { - buffer_in[count_in++] = ch; - if (flush || count_in == CDCACM_PACKET_SIZE) { - /* Refuse to send if USB isn't configured, and - * don't bother if nobody's listening */ - if (usb_get_config() != 1 || !gdb_serial_get_dtr()) { - count_in = 0; - return; - } - while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, buffer_in, count_in) <= 0) + /* Flush only if there is data to flush */ + if (count_in == 0U) + return; + + /* Refuse to send if USB isn't configured, and don't bother if nobody's listening */ + if (usb_get_config() != 1U || !gdb_serial_get_dtr()) { + count_in = 0U; + return; + } + while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, buffer_in, count_in) <= 0U) + continue; + + /* We need to send an empty packet for some hosts to accept this as a complete transfer. */ + if (force && count_in == CDCACM_PACKET_SIZE) { + /* + * libopencm3 needs a change for us to confirm when that transfer is complete, + * so we just send a packet containing a null character for now. + */ + while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, "\0", 1U) <= 0U) continue; + } - if (flush && count_in == CDCACM_PACKET_SIZE) { - /* We need to send an empty packet for some hosts - * to accept this as a complete transfer. */ - /* libopencm3 needs a change for us to confirm when - * that transfer is complete, so we just send a packet - * containing a null byte for now. - */ - while (usbd_ep_write_packet(usbdev, CDCACM_GDB_ENDPOINT, "\0", 1) <= 0) - continue; - } + /* Reset the buffer */ + count_in = 0U; +} - count_in = 0; - } +void gdb_usb_putchar(const char ch, const bool flush) +{ + buffer_in[count_in++] = ch; + if (flush || count_in == CDCACM_PACKET_SIZE) + gdb_usb_flush(flush); } void gdb_usb_out_cb(usbd_device *dev, uint8_t ep) @@ -144,7 +151,7 @@ char gdb_usb_getchar_to(const uint32_t timeout) return -1; } -void gdb_if_putchar(char ch, int flush) +void gdb_if_putchar(const char ch, const bool flush) { if (is_gdb_client_connected()) wifi_gdb_putchar(ch, flush); @@ -152,6 +159,14 @@ void gdb_if_putchar(char ch, int flush) gdb_usb_putchar(ch, flush); } +void gdb_if_flush(const bool force) +{ + if (is_gdb_client_connected()) + wifi_gdb_flush(force); + else + gdb_usb_flush(force); +} + char gdb_if_getchar(void) { platform_tasks(); diff --git a/src/platforms/hosted/gdb_if.c b/src/platforms/hosted/gdb_if.c index f5bc8733b6b..16641de2269 100644 --- a/src/platforms/hosted/gdb_if.c +++ b/src/platforms/hosted/gdb_if.c @@ -350,13 +350,32 @@ char gdb_if_getchar_to(uint32_t timeout) return -1; } -void gdb_if_putchar(char c, int flush) +void gdb_if_putchar(const char c, const bool flush) { if (gdb_if_conn == INVALID_SOCKET) return; gdb_buffer[gdb_buffer_used++] = c; - if (flush || gdb_buffer_used == GDB_BUFFER_LEN) { - send(gdb_if_conn, gdb_buffer, gdb_buffer_used, 0); - gdb_buffer_used = 0; + if (flush || gdb_buffer_used == GDB_BUFFER_LEN) + gdb_if_flush(flush); +} + +void gdb_if_flush(const bool force) +{ + (void)force; + + /* Flush only if there is data to flush */ + if (gdb_buffer_used == 0U) + return; + + /* Don't bother if the connection is not valid */ + if (gdb_if_conn == INVALID_SOCKET) { + gdb_buffer_used = 0U; + return; } + + /* Send the data */ + send(gdb_if_conn, gdb_buffer, gdb_buffer_used, 0); + + /* Reset the buffer */ + gdb_buffer_used = 0; } diff --git a/src/remote.c b/src/remote.c index 911d01d68ff..ad1faa6af21 100644 --- a/src/remote.c +++ b/src/remote.c @@ -52,20 +52,20 @@ static void remote_send_buf(const void *const buffer, const size_t len) const uint8_t *const data = (const uint8_t *)buffer; for (size_t offset = 0; offset < len; ++offset) { hexify(hex, data + offset, 1U); - gdb_if_putchar(hex[0], 0); - gdb_if_putchar(hex[1], 0); + gdb_if_putchar(hex[0], false); + gdb_if_putchar(hex[1], false); } } /* Send a response with some data following */ static void remote_respond_buf(const char response_code, const void *const buffer, const size_t len) { - gdb_if_putchar(REMOTE_RESP, 0); - gdb_if_putchar(response_code, 0); + gdb_if_putchar(REMOTE_RESP, false); + gdb_if_putchar(response_code, false); remote_send_buf(buffer, len); - gdb_if_putchar(REMOTE_EOM, 1); + gdb_if_putchar(REMOTE_EOM, true); } /* Send a response with a simple result code parameter */ @@ -98,18 +98,18 @@ static void remote_respond(const char response_code, uint64_t param) /* Send a response with a string following */ static void remote_respond_string(const char response_code, const char *const str) { - gdb_if_putchar(REMOTE_RESP, 0); - gdb_if_putchar(response_code, 0); + gdb_if_putchar(REMOTE_RESP, false); + gdb_if_putchar(response_code, false); const size_t str_length = strlen(str); for (size_t idx = 0; idx < str_length; ++idx) { const char chr = str[idx]; /* Replace problematic/illegal characters with a space to not disturb the protocol */ if (chr == '$' || chr == REMOTE_SOM || chr == REMOTE_EOM) - gdb_if_putchar(' ', 0); + gdb_if_putchar(' ', false); else - gdb_if_putchar(chr, 0); + gdb_if_putchar(chr, false); } - gdb_if_putchar(REMOTE_EOM, 1); + gdb_if_putchar(REMOTE_EOM, true); } /* diff --git a/src/target/semihosting.c b/src/target/semihosting.c index 1575064cfb5..b469dfb48a9 100644 --- a/src/target/semihosting.c +++ b/src/target/semihosting.c @@ -127,7 +127,7 @@ const char *const semihosting_names[] = { static semihosting_errno_e semihosting_errno(void); #endif -int32_t semihosting_reply(target_controller_s *const tc, char *const pbuf) +int32_t semihosting_reply(target_controller_s *const tc, const char *const pbuf) { /* * File-I/O Remote Protocol Extension @@ -175,20 +175,19 @@ int32_t semihosting_reply(target_controller_s *const tc, char *const pbuf) static int32_t semihosting_get_gdb_response(target_controller_s *const tc) { - char *const packet_buffer = gdb_packet_buffer(); /* Still have to service normal 'X'/'m'-packets */ while (true) { /* Get back the next packet to process and have the main loop handle it */ - const size_t size = gdb_getpacket(packet_buffer, GDB_PACKET_BUFFER_SIZE); + const gdb_packet_s *const packet = gdb_packet_receive(); /* If this was an escape packet (or gdb_if reports link closed), fail the call */ - if (size == 1U && packet_buffer[0] == '\x04') + if (packet->size == 1U && packet->data[0] == '\x04') return -1; /* * If this was an F-packet, we are done waiting. * Check before gdb_main_loop as it may clobber the packet buffer. */ - const bool done = packet_buffer[0] == 'F'; - const int32_t result = gdb_main_loop(tc, packet_buffer, GDB_PACKET_BUFFER_SIZE, size, true); + const bool done = packet->data[0] == 'F'; + const int32_t result = gdb_main_loop(tc, packet, true); if (done) return result; } @@ -212,7 +211,7 @@ static int32_t semihosting_remote_read( return result; } #endif - gdb_putpacket_f("Fread,%08X,%08" PRIX32 ",%08" PRIX32, (unsigned)fd, buf_taddr, count); + gdb_putpacket_str_f("Fread,%08X,%08" PRIX32 ",%08" PRIX32, (unsigned)fd, buf_taddr, count); return semihosting_get_gdb_response(target->tc); } @@ -255,7 +254,7 @@ static int32_t semihosting_remote_write( return (int32_t)count; } - gdb_putpacket_f("Fwrite,%08X,%08" PRIX32 ",%08" PRIX32, (unsigned)fd, buf_taddr, count); + gdb_putpacket_str_f("Fwrite,%08X,%08" PRIX32 ",%08" PRIX32, (unsigned)fd, buf_taddr, count); return semihosting_get_gdb_response(target->tc); } @@ -408,7 +407,7 @@ int32_t semihosting_open(target_s *const target, const semihosting_s *const requ free((void *)file_name); #pragma GCC diagnostic pop #else - gdb_putpacket_f("Fopen,%08" PRIX32 "/%08" PRIX32 ",%08" PRIX32 ",%08X", file_name_taddr, file_name_length + 1U, + gdb_putpacket_str_f("Fopen,%08" PRIX32 "/%08" PRIX32 ",%08" PRIX32 ",%08X", file_name_taddr, file_name_length + 1U, open_mode, 0644U); const int32_t result = semihosting_get_gdb_response(target->tc); #endif @@ -432,7 +431,7 @@ int32_t semihosting_close(target_s *const target, const semihosting_s *const req target->tc->gdb_errno = semihosting_errno(); return result; #else - gdb_putpacket_f("Fclose,%08X", (unsigned)fd); + gdb_putpacket_str_f("Fclose,%08X", (unsigned)fd); return semihosting_get_gdb_response(target->tc); #endif } @@ -518,7 +517,7 @@ int32_t semihosting_isatty(target_s *const target, const semihosting_s *const re return result; } #endif - gdb_putpacket_f("Fisatty,%08X", (unsigned)fd); + gdb_putpacket_str_f("Fisatty,%08X", (unsigned)fd); return semihosting_get_gdb_response(target->tc); } @@ -541,7 +540,7 @@ int32_t semihosting_seek(target_s *const target, const semihosting_s *const requ return result; } #endif - gdb_putpacket_f("Flseek,%08X,%08lX,%08X", (unsigned)fd, (unsigned long)offset, SEEK_MODE_SET); + gdb_putpacket_str_f("Flseek,%08X,%08lX,%08X", (unsigned)fd, (unsigned long)offset, SEEK_MODE_SET); return semihosting_get_gdb_response(target->tc) == offset ? 0 : -1; } @@ -568,7 +567,7 @@ int32_t semihosting_rename(target_s *const target, const semihosting_s *const re #pragma GCC diagnostic pop return result; #else - gdb_putpacket_f("Frename,%08" PRIX32 "/%08" PRIX32 ",%08" PRIX32 "/%08" PRIX32, request->params[0], + gdb_putpacket_str_f("Frename,%08" PRIX32 "/%08" PRIX32 ",%08" PRIX32 "/%08" PRIX32, request->params[0], request->params[1] + 1U, request->params[2], request->params[3] + 1U); return semihosting_get_gdb_response(target->tc); #endif @@ -588,7 +587,7 @@ int32_t semihosting_remove(target_s *const target, const semihosting_s *const re #pragma GCC diagnostic pop return result; #else - gdb_putpacket_f("Funlink,%08" PRIX32 "/%08" PRIX32, request->params[0], request->params[1] + 1U); + gdb_putpacket_str_f("Funlink,%08" PRIX32 "/%08" PRIX32, request->params[0], request->params[1] + 1U); return semihosting_get_gdb_response(target->tc); #endif } @@ -596,7 +595,7 @@ int32_t semihosting_remove(target_s *const target, const semihosting_s *const re int32_t semihosting_system(target_s *const target, const semihosting_s *const request) { /* NB: Before use first enable system calls with the following gdb command: 'set remote system-call-allowed 1' */ - gdb_putpacket_f("Fsystem,%08" PRIX32 "/%08" PRIX32, request->params[0], request->params[1] + 1U); + gdb_putpacket_str_f("Fsystem,%08" PRIX32 "/%08" PRIX32, request->params[0], request->params[1] + 1U); return semihosting_get_gdb_response(target->tc); } @@ -630,7 +629,7 @@ int32_t semihosting_file_length(target_s *const target, const semihosting_s *con target->tc->semihosting_buffer_ptr = file_stat; target->tc->semihosting_buffer_len = sizeof(file_stat); /* Call GDB and ask for the file descriptor's stat info */ - gdb_putpacket_f("Ffstat,%X,%08" PRIX32, (unsigned)fd, target->ram->start); + gdb_putpacket_str_f("Ffstat,%X,%08" PRIX32, (unsigned)fd, target->ram->start); const int32_t stat_result = semihosting_get_gdb_response(target->tc); target->target_options &= ~TOPT_IN_SEMIHOSTING_SYSCALL; /* Extract the lower half of the file size from the buffer */ @@ -651,7 +650,7 @@ semihosting_time_s semihosting_get_time(target_s *const target) target->tc->semihosting_buffer_ptr = time_value; target->tc->semihosting_buffer_len = sizeof(time_value); /* Call GDB and ask for the current time using gettimeofday() */ - gdb_putpacket_f("Fgettimeofday,%08" PRIX32 ",%08" PRIX32, target->ram->start, (target_addr_t)NULL); + gdb_putpacket_str_f("Fgettimeofday,%08" PRIX32 ",%08" PRIX32, target->ram->start, (target_addr_t)NULL); const int32_t result = semihosting_get_gdb_response(target->tc); target->target_options &= ~TOPT_IN_SEMIHOSTING_SYSCALL; /* Check if the GDB remote gettimeofday() failed */ diff --git a/src/target/semihosting.h b/src/target/semihosting.h index 2f37e6aaa9c..7e477627dfc 100644 --- a/src/target/semihosting.h +++ b/src/target/semihosting.h @@ -28,6 +28,6 @@ extern uint32_t semihosting_wallclock_epoch; int32_t semihosting_request(target_s *target, uint32_t syscall, uint32_t r1); -int32_t semihosting_reply(target_controller_s *tc, char *packet); +int32_t semihosting_reply(target_controller_s *tc, const char *packet); #endif /* TARGET_SEMIHOSTING_H */