diff options
Diffstat (limited to 'gdbstub')
-rw-r--r-- | gdbstub/gdbstub.c | 447 | ||||
-rw-r--r-- | gdbstub/internals.h | 23 | ||||
-rw-r--r-- | gdbstub/meson.build | 40 | ||||
-rw-r--r-- | gdbstub/syscalls.c | 11 | ||||
-rw-r--r-- | gdbstub/system.c | 52 | ||||
-rw-r--r-- | gdbstub/user-target.c | 43 | ||||
-rw-r--r-- | gdbstub/user.c | 180 |
7 files changed, 477 insertions, 319 deletions
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index b357499..dd5fb56 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -20,7 +20,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see <http://www.gnu.org/licenses/>. * - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "qemu/osdep.h" @@ -28,8 +28,10 @@ #include "qemu/cutils.h" #include "qemu/module.h" #include "qemu/error-report.h" +#include "qemu/target-info.h" #include "trace.h" #include "exec/gdbstub.h" +#include "gdbstub/commands.h" #include "gdbstub/syscalls.h" #ifdef CONFIG_USER_ONLY #include "accel/tcg/vcpu-state.h" @@ -40,8 +42,8 @@ #endif #include "hw/core/cpu.h" -#include "sysemu/hw_accel.h" -#include "sysemu/runstate.h" +#include "system/hw_accel.h" +#include "system/runstate.h" #include "exec/replay-core.h" #include "exec/hwaddr.h" @@ -353,7 +355,6 @@ static const char *get_feature_xml(const char *p, const char **newp, GDBProcess *process) { CPUState *cpu = gdb_get_first_cpu_in_process(process); - CPUClass *cc = CPU_GET_CLASS(cpu); GDBRegisterState *r; size_t len; @@ -376,11 +377,11 @@ static const char *get_feature_xml(const char *p, const char **newp, "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">" "<target>")); - if (cc->gdb_arch_name) { + if (cpu->cc->gdb_arch_name) { g_ptr_array_add( xml, g_markup_printf_escaped("<architecture>%s</architecture>", - cc->gdb_arch_name(cpu))); + cpu->cc->gdb_arch_name(cpu))); } for (guint i = 0; i < cpu->gdb_regs->len; i++) { r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); @@ -519,11 +520,10 @@ GArray *gdb_get_register_list(CPUState *cpu) int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) { - CPUClass *cc = CPU_GET_CLASS(cpu); GDBRegisterState *r; - if (reg < cc->gdb_num_core_regs) { - return cc->gdb_read_register(cpu, buf, reg); + if (reg < cpu->cc->gdb_num_core_regs) { + return cpu->cc->gdb_read_register(cpu, buf, reg); } for (guint i = 0; i < cpu->gdb_regs->len; i++) { @@ -535,13 +535,12 @@ int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) return 0; } -static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) +int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) { - CPUClass *cc = CPU_GET_CLASS(cpu); GDBRegisterState *r; - if (reg < cc->gdb_num_core_regs) { - return cc->gdb_write_register(cpu, mem_buf, reg); + if (reg < cpu->cc->gdb_num_core_regs) { + return cpu->cc->gdb_write_register(cpu, mem_buf, reg); } for (guint i = 0; i < cpu->gdb_regs->len; i++) { @@ -567,15 +566,30 @@ static void gdb_register_feature(CPUState *cpu, int base_reg, g_array_append_val(cpu->gdb_regs, s); } +static const char *gdb_get_core_xml_file(CPUState *cpu) +{ + CPUClass *cc = cpu->cc; + + /* + * The CPU class can provide the XML filename via a method, + * or as a simple fixed string field. + */ + if (cc->gdb_get_core_xml_file) { + return cc->gdb_get_core_xml_file(cpu); + } + return cc->gdb_core_xml_file; +} + void gdb_init_cpu(CPUState *cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); + CPUClass *cc = cpu->cc; const GDBFeature *feature; + const char *xmlfile = gdb_get_core_xml_file(cpu); cpu->gdb_regs = g_array_new(false, false, sizeof(GDBRegisterState)); - if (cc->gdb_core_xml_file) { - feature = gdb_find_static_feature(cc->gdb_core_xml_file); + if (xmlfile) { + feature = gdb_find_static_feature(xmlfile); gdb_register_feature(cpu, 0, cc->gdb_read_register, cc->gdb_write_register, feature); @@ -617,6 +631,19 @@ void gdb_register_coprocessor(CPUState *cpu, } } +void gdb_unregister_coprocessor_all(CPUState *cpu) +{ + /* + * Safe to nuke everything. GDBRegisterState::xml is static const char so + * it won't be freed + */ + g_array_free(cpu->gdb_regs, true); + + cpu->gdb_regs = NULL; + cpu->gdb_num_regs = 0; + cpu->gdb_num_g_regs = 0; +} + static void gdb_process_breakpoint_remove_all(GDBProcess *p) { CPUState *cpu = gdb_get_first_cpu_in_process(p); @@ -920,60 +947,24 @@ static int cmd_parse_params(const char *data, const char *schema, return 0; } -typedef void (*GdbCmdHandler)(GArray *params, void *user_ctx); - -/* - * cmd_startswith -> cmd is compared using startswith - * - * allow_stop_reply -> true iff the gdbstub can respond to this command with a - * "stop reply" packet. The list of commands that accept such response is - * defined at the GDB Remote Serial Protocol documentation. see: - * https://sourceware.org/gdb/onlinedocs/gdb/Stop-Reply-Packets.html#Stop-Reply-Packets. - * - * schema definitions: - * Each schema parameter entry consists of 2 chars, - * the first char represents the parameter type handling - * the second char represents the delimiter for the next parameter - * - * Currently supported schema types: - * 'l' -> unsigned long (stored in .val_ul) - * 'L' -> unsigned long long (stored in .val_ull) - * 's' -> string (stored in .data) - * 'o' -> single char (stored in .opcode) - * 't' -> thread id (stored in .thread_id) - * '?' -> skip according to delimiter - * - * Currently supported delimiters: - * '?' -> Stop at any delimiter (",;:=\0") - * '0' -> Stop at "\0" - * '.' -> Skip 1 char unless reached "\0" - * Any other value is treated as the delimiter value itself - */ -typedef struct GdbCmdParseEntry { - GdbCmdHandler handler; - const char *cmd; - bool cmd_startswith; - const char *schema; - bool allow_stop_reply; -} GdbCmdParseEntry; - static inline int startswith(const char *string, const char *pattern) { return !strncmp(string, pattern, strlen(pattern)); } -static int process_string_cmd(const char *data, - const GdbCmdParseEntry *cmds, int num_cmds) +static bool process_string_cmd(const char *data, + const GdbCmdParseEntry *cmds, int num_cmds) { int i; g_autoptr(GArray) params = g_array_new(false, true, sizeof(GdbCmdVariant)); if (!cmds) { - return -1; + return false; } for (i = 0; i < num_cmds; i++) { const GdbCmdParseEntry *cmd = &cmds[i]; + void *user_ctx = NULL; g_assert(cmd->handler && cmd->cmd); if ((cmd->cmd_startswith && !startswith(data, cmd->cmd)) || @@ -984,16 +975,20 @@ static int process_string_cmd(const char *data, if (cmd->schema) { if (cmd_parse_params(&data[strlen(cmd->cmd)], cmd->schema, params)) { - return -1; + return false; } } + if (cmd->need_cpu_context) { + user_ctx = (void *)gdbserver_state.g_cpu; + } + gdbserver_state.allow_stop_reply = cmd->allow_stop_reply; - cmd->handler(params, NULL); - return 0; + cmd->handler(params, user_ctx); + return true; } - return -1; + return false; } static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) @@ -1007,7 +1002,7 @@ static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) /* In case there was an error during the command parsing we must * send a NULL packet to indicate the command is not supported */ - if (process_string_cmd(data, cmd, 1)) { + if (!process_string_cmd(data, cmd, 1)) { gdb_put_packet(""); } } @@ -1023,7 +1018,7 @@ static void handle_detach(GArray *params, void *user_ctx) return; } - pid = get_param(params, 0)->val_ul; + pid = gdb_get_cmd_param(params, 0)->val_ul; } #ifdef CONFIG_USER_ONLY @@ -1061,13 +1056,13 @@ static void handle_thread_alive(GArray *params, void *user_ctx) return; } - if (get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { + if (gdb_get_cmd_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { gdb_put_packet("E22"); return; } - cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid, - get_param(params, 0)->thread_id.tid); + cpu = gdb_get_cpu(gdb_get_cmd_param(params, 0)->thread_id.pid, + gdb_get_cmd_param(params, 0)->thread_id.tid); if (!cpu) { gdb_put_packet("E22"); return; @@ -1079,7 +1074,7 @@ static void handle_thread_alive(GArray *params, void *user_ctx) static void handle_continue(GArray *params, void *user_ctx) { if (params->len) { - gdb_set_cpu_pc(get_param(params, 0)->val_ull); + gdb_set_cpu_pc(gdb_get_cmd_param(params, 0)->val_ull); } gdbserver_state.signal = 0; @@ -1095,7 +1090,7 @@ static void handle_cont_with_sig(GArray *params, void *user_ctx) * omit the addr parameter */ if (params->len) { - signal = get_param(params, 0)->val_ul; + signal = gdb_get_cmd_param(params, 0)->val_ul; } gdbserver_state.signal = gdb_signal_to_target(signal); @@ -1115,18 +1110,18 @@ static void handle_set_thread(GArray *params, void *user_ctx) return; } - if (get_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) { + if (gdb_get_cmd_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) { gdb_put_packet("E22"); return; } - if (get_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) { + if (gdb_get_cmd_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) { gdb_put_packet("OK"); return; } - pid = get_param(params, 1)->thread_id.pid; - tid = get_param(params, 1)->thread_id.tid; + pid = gdb_get_cmd_param(params, 1)->thread_id.pid; + tid = gdb_get_cmd_param(params, 1)->thread_id.tid; #ifdef CONFIG_USER_ONLY if (gdb_handle_set_thread_user(pid, tid)) { return; @@ -1142,7 +1137,7 @@ static void handle_set_thread(GArray *params, void *user_ctx) * Note: This command is deprecated and modern gdb's will be using the * vCont command instead. */ - switch (get_param(params, 0)->opcode) { + switch (gdb_get_cmd_param(params, 0)->opcode) { case 'c': gdbserver_state.c_cpu = cpu; gdb_put_packet("OK"); @@ -1167,9 +1162,9 @@ static void handle_insert_bp(GArray *params, void *user_ctx) } res = gdb_breakpoint_insert(gdbserver_state.c_cpu, - get_param(params, 0)->val_ul, - get_param(params, 1)->val_ull, - get_param(params, 2)->val_ull); + gdb_get_cmd_param(params, 0)->val_ul, + gdb_get_cmd_param(params, 1)->val_ull, + gdb_get_cmd_param(params, 2)->val_ull); if (res >= 0) { gdb_put_packet("OK"); return; @@ -1191,9 +1186,9 @@ static void handle_remove_bp(GArray *params, void *user_ctx) } res = gdb_breakpoint_remove(gdbserver_state.c_cpu, - get_param(params, 0)->val_ul, - get_param(params, 1)->val_ull, - get_param(params, 2)->val_ull); + gdb_get_cmd_param(params, 0)->val_ul, + gdb_get_cmd_param(params, 1)->val_ull, + gdb_get_cmd_param(params, 2)->val_ull); if (res >= 0) { gdb_put_packet("OK"); return; @@ -1225,10 +1220,10 @@ static void handle_set_reg(GArray *params, void *user_ctx) return; } - reg_size = strlen(get_param(params, 1)->data) / 2; - gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 1)->data, reg_size); + reg_size = strlen(gdb_get_cmd_param(params, 1)->data) / 2; + gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 1)->data, reg_size); gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data, - get_param(params, 0)->val_ull); + gdb_get_cmd_param(params, 0)->val_ull); gdb_put_packet("OK"); } @@ -1243,7 +1238,7 @@ static void handle_get_reg(GArray *params, void *user_ctx) reg_size = gdb_read_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf, - get_param(params, 0)->val_ull); + gdb_get_cmd_param(params, 0)->val_ull); if (!reg_size) { gdb_put_packet("E14"); return; @@ -1264,16 +1259,16 @@ static void handle_write_mem(GArray *params, void *user_ctx) } /* gdb_hextomem() reads 2*len bytes */ - if (get_param(params, 1)->val_ull > - strlen(get_param(params, 2)->data) / 2) { + if (gdb_get_cmd_param(params, 1)->val_ull > + strlen(gdb_get_cmd_param(params, 2)->data) / 2) { gdb_put_packet("E22"); return; } - gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 2)->data, - get_param(params, 1)->val_ull); + gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 2)->data, + gdb_get_cmd_param(params, 1)->val_ull); if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, - get_param(params, 0)->val_ull, + gdb_get_cmd_param(params, 0)->val_ull, gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len, true)) { gdb_put_packet("E14"); @@ -1291,16 +1286,16 @@ static void handle_read_mem(GArray *params, void *user_ctx) } /* gdb_memtohex() doubles the required space */ - if (get_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) { + if (gdb_get_cmd_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) { gdb_put_packet("E22"); return; } g_byte_array_set_size(gdbserver_state.mem_buf, - get_param(params, 1)->val_ull); + gdb_get_cmd_param(params, 1)->val_ull); if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, - get_param(params, 0)->val_ull, + gdb_get_cmd_param(params, 0)->val_ull, gdbserver_state.mem_buf->data, gdbserver_state.mem_buf->len, false)) { gdb_put_packet("E14"); @@ -1324,8 +1319,8 @@ static void handle_write_all_regs(GArray *params, void *user_ctx) } cpu_synchronize_state(gdbserver_state.g_cpu); - len = strlen(get_param(params, 0)->data) / 2; - gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len); + len = strlen(gdb_get_cmd_param(params, 0)->data) / 2; + gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 0)->data, len); registers = gdbserver_state.mem_buf->data; for (reg_id = 0; reg_id < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0; @@ -1349,8 +1344,8 @@ static void handle_read_all_regs(GArray *params, void *user_ctx) len += gdb_read_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf, reg_id); + g_assert(len == gdbserver_state.mem_buf->len); } - g_assert(len == gdbserver_state.mem_buf->len); gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len); gdb_put_strbuf(); @@ -1360,7 +1355,7 @@ static void handle_read_all_regs(GArray *params, void *user_ctx) static void handle_step(GArray *params, void *user_ctx) { if (params->len) { - gdb_set_cpu_pc(get_param(params, 0)->val_ull); + gdb_set_cpu_pc(gdb_get_cmd_param(params, 0)->val_ull); } cpu_single_step(gdbserver_state.c_cpu, gdbserver_state.sstep_flags); @@ -1373,7 +1368,7 @@ static void handle_backward(GArray *params, void *user_ctx) gdb_put_packet("E22"); } if (params->len == 1) { - switch (get_param(params, 0)->opcode) { + switch (gdb_get_cmd_param(params, 0)->opcode) { case 's': if (replay_reverse_step()) { gdb_continue(); @@ -1408,7 +1403,7 @@ static void handle_v_cont(GArray *params, void *user_ctx) return; } - res = gdb_handle_vcont(get_param(params, 0)->data); + res = gdb_handle_vcont(gdb_get_cmd_param(params, 0)->data); if ((res == -EINVAL) || (res == -ERANGE)) { gdb_put_packet("E22"); } else if (res) { @@ -1426,7 +1421,7 @@ static void handle_v_attach(GArray *params, void *user_ctx) goto cleanup; } - process = gdb_get_process(get_param(params, 0)->val_ul); + process = gdb_get_process(gdb_get_cmd_param(params, 0)->val_ul); if (!process) { goto cleanup; } @@ -1464,26 +1459,26 @@ static const GdbCmdParseEntry gdb_v_commands_table[] = { { .handler = handle_v_cont_query, .cmd = "Cont?", - .cmd_startswith = 1 + .cmd_startswith = true }, { .handler = handle_v_cont, .cmd = "Cont", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, .schema = "s0" }, { .handler = handle_v_attach, .cmd = "Attach;", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, .schema = "l0" }, { .handler = handle_v_kill, .cmd = "Kill;", - .cmd_startswith = 1 + .cmd_startswith = true }, #ifdef CONFIG_USER_ONLY /* @@ -1493,25 +1488,25 @@ static const GdbCmdParseEntry gdb_v_commands_table[] = { { .handler = gdb_handle_v_file_open, .cmd = "File:open:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s,L,L0" }, { .handler = gdb_handle_v_file_close, .cmd = "File:close:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l0" }, { .handler = gdb_handle_v_file_pread, .cmd = "File:pread:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l,L,L0" }, { .handler = gdb_handle_v_file_readlink, .cmd = "File:readlink:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }, #endif @@ -1523,9 +1518,9 @@ static void handle_v_commands(GArray *params, void *user_ctx) return; } - if (process_string_cmd(get_param(params, 0)->data, - gdb_v_commands_table, - ARRAY_SIZE(gdb_v_commands_table))) { + if (!process_string_cmd(gdb_get_cmd_param(params, 0)->data, + gdb_v_commands_table, + ARRAY_SIZE(gdb_v_commands_table))) { gdb_put_packet(""); } } @@ -1555,7 +1550,7 @@ static void handle_set_qemu_sstep(GArray *params, void *user_ctx) return; } - new_sstep_flags = get_param(params, 0)->val_ul; + new_sstep_flags = gdb_get_cmd_param(params, 0)->val_ul; if (new_sstep_flags & ~gdbserver_state.supported_sstep_flags) { gdb_put_packet("E22"); @@ -1603,6 +1598,18 @@ static void handle_query_threads(GArray *params, void *user_ctx) gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); } +static void handle_query_gdb_server_version(GArray *params, void *user_ctx) +{ +#if defined(CONFIG_USER_ONLY) + g_string_printf(gdbserver_state.str_buf, "name:qemu-%s;version:%s;", + target_name(), QEMU_VERSION); +#else + g_string_printf(gdbserver_state.str_buf, "name:qemu-system-%s;version:%s;", + target_name(), QEMU_VERSION); +#endif + gdb_put_strbuf(); +} + static void handle_query_first_threads(GArray *params, void *user_ctx) { gdbserver_state.query_cpu = gdb_first_attached_cpu(); @@ -1615,13 +1622,13 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx) CPUState *cpu; if (!params->len || - get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { + gdb_get_cmd_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { gdb_put_packet("E22"); return; } - cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid, - get_param(params, 0)->thread_id.tid); + cpu = gdb_get_cpu(gdb_get_cmd_param(params, 0)->thread_id.pid, + gdb_get_cmd_param(params, 0)->thread_id.tid); if (!cpu) { return; } @@ -1645,13 +1652,27 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx) gdb_put_strbuf(); } -static void handle_query_supported(GArray *params, void *user_ctx) + +static char **extra_query_flags; + +void gdb_extend_qsupported_features(char *qflags) { - CPUClass *cc; + if (!extra_query_flags) { + extra_query_flags = g_new0(char *, 2); + extra_query_flags[0] = g_strdup(qflags); + } else if (!g_strv_contains((const gchar * const *) extra_query_flags, + qflags)) { + int len = g_strv_length(extra_query_flags); + extra_query_flags = g_realloc_n(extra_query_flags, len + 2, + sizeof(char *)); + extra_query_flags[len] = g_strdup(qflags); + } +} +static void handle_query_supported(GArray *params, void *user_ctx) +{ g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH); - cc = CPU_GET_CLASS(first_cpu); - if (cc->gdb_core_xml_file) { + if (gdb_get_core_xml_file(first_cpu)) { g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); } @@ -1673,7 +1694,7 @@ static void handle_query_supported(GArray *params, void *user_ctx) #endif if (params->len) { - const char *gdb_supported = get_param(params, 0)->data; + const char *gdb_supported = gdb_get_cmd_param(params, 0)->data; if (strstr(gdb_supported, "multiprocess+")) { gdbserver_state.multiprocess = true; @@ -1684,13 +1705,20 @@ static void handle_query_supported(GArray *params, void *user_ctx) } g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+"); + + if (extra_query_flags) { + int extras = g_strv_length(extra_query_flags); + for (int i = 0; i < extras; i++) { + g_string_append(gdbserver_state.str_buf, extra_query_flags[i]); + } + } + gdb_put_strbuf(); } static void handle_query_xfer_features(GArray *params, void *user_ctx) { GDBProcess *process; - CPUClass *cc; unsigned long len, total_len, addr; const char *xml; const char *p; @@ -1701,21 +1729,20 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx) } process = gdb_get_cpu_process(gdbserver_state.g_cpu); - cc = CPU_GET_CLASS(gdbserver_state.g_cpu); - if (!cc->gdb_core_xml_file) { + if (!gdb_get_core_xml_file(gdbserver_state.g_cpu)) { gdb_put_packet(""); return; } - p = get_param(params, 0)->data; + p = gdb_get_cmd_param(params, 0)->data; xml = get_feature_xml(p, &p, process); if (!xml) { gdb_put_packet("E00"); return; } - addr = get_param(params, 1)->val_ul; - len = get_param(params, 2)->val_ul; + addr = gdb_get_cmd_param(params, 1)->val_ul; + len = gdb_get_cmd_param(params, 2)->val_ul; total_len = strlen(xml); if (addr > total_len) { gdb_put_packet("E00"); @@ -1760,11 +1787,65 @@ static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = { { .handler = handle_set_qemu_sstep, .cmd = "qemu.sstep=", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l0" }, }; +/** + * extend_table() - extend one of the command tables + * @table: the command table to extend (or NULL) + * @extensions: a list of GdbCmdParseEntry pointers + * + * The entries themselves should be pointers to static const + * GdbCmdParseEntry entries. If the entry is already in the table we + * skip adding it again. + * + * Returns (a potentially freshly allocated) GPtrArray of GdbCmdParseEntry + */ +static GPtrArray *extend_table(GPtrArray *table, GPtrArray *extensions) +{ + if (!table) { + table = g_ptr_array_new(); + } + + for (int i = 0; i < extensions->len; i++) { + gpointer entry = g_ptr_array_index(extensions, i); + if (!g_ptr_array_find(table, entry, NULL)) { + g_ptr_array_add(table, entry); + } + } + + return table; +} + +/** + * process_extended_table() - run through an extended command table + * @table: the command table to check + * @data: parameters + * + * returns true if the command was found and executed + */ +static bool process_extended_table(GPtrArray *table, const char *data) +{ + for (int i = 0; i < table->len; i++) { + const GdbCmdParseEntry *entry = g_ptr_array_index(table, i); + if (process_string_cmd(data, entry, 1)) { + return true; + } + } + return false; +} + + +/* Ptr to GdbCmdParseEntry */ +static GPtrArray *extended_query_table; + +void gdb_extend_query_table(GPtrArray *new_queries) +{ + extended_query_table = extend_table(extended_query_table, new_queries); +} + static const GdbCmdParseEntry gdb_gen_query_table[] = { { .handler = handle_query_curr_tid, @@ -1775,13 +1856,17 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { .cmd = "sThreadInfo", }, { + .handler = handle_query_gdb_server_version, + .cmd = "GDBServerVersion", + }, + { .handler = handle_query_first_threads, .cmd = "fThreadInfo", }, { .handler = handle_query_thread_extra, .cmd = "ThreadExtraInfo,", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "t0" }, #ifdef CONFIG_USER_ONLY @@ -1793,14 +1878,14 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { { .handler = gdb_handle_query_rcmd, .cmd = "Rcmd,", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }, #endif { .handler = handle_query_supported, .cmd = "Supported:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }, { @@ -1811,7 +1896,7 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { { .handler = handle_query_xfer_features, .cmd = "Xfer:features:read:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s:l,l0" }, #if defined(CONFIG_USER_ONLY) @@ -1819,27 +1904,27 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { { .handler = gdb_handle_query_xfer_auxv, .cmd = "Xfer:auxv:read::", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l,l0" }, { .handler = gdb_handle_query_xfer_siginfo, .cmd = "Xfer:siginfo:read::", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l,l0" }, #endif { .handler = gdb_handle_query_xfer_exec_file, .cmd = "Xfer:exec-file:read:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l:l,l0" }, #endif { .handler = gdb_handle_query_attached, .cmd = "Attached:", - .cmd_startswith = 1 + .cmd_startswith = true }, { .handler = gdb_handle_query_attached, @@ -1857,19 +1942,27 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { #endif }; +/* Ptr to GdbCmdParseEntry */ +static GPtrArray *extended_set_table; + +void gdb_extend_set_table(GPtrArray *new_set) +{ + extended_set_table = extend_table(extended_set_table, new_set); +} + static const GdbCmdParseEntry gdb_gen_set_table[] = { /* Order is important if has same prefix */ { .handler = handle_set_qemu_sstep, .cmd = "qemu.sstep:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l0" }, #ifndef CONFIG_USER_ONLY { .handler = gdb_handle_set_qemu_phy_mem_mode, .cmd = "qemu.PhyMemMode:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l0" }, #endif @@ -1877,7 +1970,7 @@ static const GdbCmdParseEntry gdb_gen_set_table[] = { { .handler = gdb_handle_set_catch_syscalls, .cmd = "CatchSyscalls:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0", }, #endif @@ -1885,40 +1978,64 @@ static const GdbCmdParseEntry gdb_gen_set_table[] = { static void handle_gen_query(GArray *params, void *user_ctx) { + const char *data; + if (!params->len) { return; } - if (!process_string_cmd(get_param(params, 0)->data, - gdb_gen_query_set_common_table, - ARRAY_SIZE(gdb_gen_query_set_common_table))) { + data = gdb_get_cmd_param(params, 0)->data; + + if (process_string_cmd(data, + gdb_gen_query_set_common_table, + ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(get_param(params, 0)->data, + if (process_string_cmd(data, gdb_gen_query_table, ARRAY_SIZE(gdb_gen_query_table))) { - gdb_put_packet(""); + return; + } + + if (extended_query_table && + process_extended_table(extended_query_table, data)) { + return; } + + /* Can't handle query, return Empty response. */ + gdb_put_packet(""); } static void handle_gen_set(GArray *params, void *user_ctx) { + const char *data; + if (!params->len) { return; } - if (!process_string_cmd(get_param(params, 0)->data, - gdb_gen_query_set_common_table, - ARRAY_SIZE(gdb_gen_query_set_common_table))) { + data = gdb_get_cmd_param(params, 0)->data; + + if (process_string_cmd(data, + gdb_gen_query_set_common_table, + ARRAY_SIZE(gdb_gen_query_set_common_table))) { return; } - if (process_string_cmd(get_param(params, 0)->data, + if (process_string_cmd(data, gdb_gen_set_table, ARRAY_SIZE(gdb_gen_set_table))) { - gdb_put_packet(""); + return; } + + if (extended_set_table && + process_extended_table(extended_set_table, data)) { + return; + } + + /* Can't handle set, return Empty response. */ + gdb_put_packet(""); } static void handle_target_halt(GArray *params, void *user_ctx) @@ -1953,7 +2070,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry target_halted_cmd_desc = { .handler = handle_target_halt, .cmd = "?", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, }; cmd_parser = &target_halted_cmd_desc; @@ -1964,7 +2081,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry continue_cmd_desc = { .handler = handle_continue, .cmd = "c", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, .schema = "L0" }; @@ -1976,7 +2093,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry cont_with_sig_cmd_desc = { .handler = handle_cont_with_sig, .cmd = "C", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, .schema = "l0" }; @@ -1988,7 +2105,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry v_cmd_desc = { .handler = handle_v_commands, .cmd = "v", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }; cmd_parser = &v_cmd_desc; @@ -2005,7 +2122,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry detach_cmd_desc = { .handler = handle_detach, .cmd = "D", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "?.l0" }; cmd_parser = &detach_cmd_desc; @@ -2016,7 +2133,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry step_cmd_desc = { .handler = handle_step, .cmd = "s", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, .schema = "L0" }; @@ -2028,7 +2145,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry backward_cmd_desc = { .handler = handle_backward, .cmd = "b", - .cmd_startswith = 1, + .cmd_startswith = true, .allow_stop_reply = true, .schema = "o0" }; @@ -2040,7 +2157,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry file_io_cmd_desc = { .handler = gdb_handle_file_io, .cmd = "F", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L,L,o0" }; cmd_parser = &file_io_cmd_desc; @@ -2051,7 +2168,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry read_all_regs_cmd_desc = { .handler = handle_read_all_regs, .cmd = "g", - .cmd_startswith = 1 + .cmd_startswith = true }; cmd_parser = &read_all_regs_cmd_desc; } @@ -2061,7 +2178,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry write_all_regs_cmd_desc = { .handler = handle_write_all_regs, .cmd = "G", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }; cmd_parser = &write_all_regs_cmd_desc; @@ -2072,7 +2189,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry read_mem_cmd_desc = { .handler = handle_read_mem, .cmd = "m", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L,L0" }; cmd_parser = &read_mem_cmd_desc; @@ -2083,7 +2200,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry write_mem_cmd_desc = { .handler = handle_write_mem, .cmd = "M", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L,L:s0" }; cmd_parser = &write_mem_cmd_desc; @@ -2094,7 +2211,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry get_reg_cmd_desc = { .handler = handle_get_reg, .cmd = "p", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L0" }; cmd_parser = &get_reg_cmd_desc; @@ -2105,7 +2222,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry set_reg_cmd_desc = { .handler = handle_set_reg, .cmd = "P", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L?s0" }; cmd_parser = &set_reg_cmd_desc; @@ -2116,7 +2233,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry insert_bp_cmd_desc = { .handler = handle_insert_bp, .cmd = "Z", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l?L?L0" }; cmd_parser = &insert_bp_cmd_desc; @@ -2127,7 +2244,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry remove_bp_cmd_desc = { .handler = handle_remove_bp, .cmd = "z", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l?L?L0" }; cmd_parser = &remove_bp_cmd_desc; @@ -2138,7 +2255,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry set_thread_cmd_desc = { .handler = handle_set_thread, .cmd = "H", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "o.t0" }; cmd_parser = &set_thread_cmd_desc; @@ -2149,7 +2266,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry thread_alive_cmd_desc = { .handler = handle_thread_alive, .cmd = "T", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "t0" }; cmd_parser = &thread_alive_cmd_desc; @@ -2160,7 +2277,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry gen_query_cmd_desc = { .handler = handle_gen_query, .cmd = "q", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }; cmd_parser = &gen_query_cmd_desc; @@ -2171,7 +2288,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry gen_set_cmd_desc = { .handler = handle_gen_set, .cmd = "Q", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }; cmd_parser = &gen_set_cmd_desc; diff --git a/gdbstub/internals.h b/gdbstub/internals.h index 32f9f63..bf5a5c6 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -106,9 +106,7 @@ static inline int tohex(int v) */ void gdb_put_strbuf(void); -int gdb_put_packet(const char *buf); int gdb_put_packet_binary(const char *buf, int len, bool dump); -void gdb_hextomem(GByteArray *mem, const char *buf, int len); void gdb_memtohex(GString *buf, const uint8_t *mem, int len); void gdb_memtox(GString *buf, const char *mem, int len); void gdb_read_byte(uint8_t ch); @@ -166,27 +164,6 @@ void gdb_put_buffer(const uint8_t *buf, int len); */ void gdb_init_gdbserver_state(void); -typedef enum GDBThreadIdKind { - GDB_ONE_THREAD = 0, - GDB_ALL_THREADS, /* One process, all threads */ - GDB_ALL_PROCESSES, - GDB_READ_THREAD_ERR -} GDBThreadIdKind; - -typedef union GdbCmdVariant { - const char *data; - uint8_t opcode; - unsigned long val_ul; - unsigned long long val_ull; - struct { - GDBThreadIdKind kind; - uint32_t pid; - uint32_t tid; - } thread_id; -} GdbCmdVariant; - -#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i)) - void gdb_handle_query_rcmd(GArray *params, void *ctx); /* system */ void gdb_handle_query_offsets(GArray *params, void *user_ctx); /* user */ void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx); /*user */ diff --git a/gdbstub/meson.build b/gdbstub/meson.build index da5721d..15c666f 100644 --- a/gdbstub/meson.build +++ b/gdbstub/meson.build @@ -4,36 +4,18 @@ # types such as hwaddr. # -# We need to build the core gdb code via a library to be able to tweak -# cflags so: - -gdb_user_ss = ss.source_set() -gdb_system_ss = ss.source_set() - # We build two versions of gdbstub, one for each mode -gdb_user_ss.add(files('gdbstub.c', 'user.c')) -gdb_system_ss.add(files('gdbstub.c', 'system.c')) - -gdb_user_ss = gdb_user_ss.apply({}) -gdb_system_ss = gdb_system_ss.apply({}) - -libgdb_user = static_library('gdb_user', - gdb_user_ss.sources() + genh, - name_suffix: 'fa', - c_args: '-DCONFIG_USER_ONLY', - build_by_default: false) - -libgdb_system = static_library('gdb_system', - gdb_system_ss.sources() + genh, - name_suffix: 'fa', - build_by_default: false) - -gdb_user = declare_dependency(link_whole: libgdb_user) -user_ss.add(gdb_user) -gdb_system = declare_dependency(link_whole: libgdb_system) -system_ss.add(gdb_system) - -common_ss.add(files('syscalls.c')) +user_ss.add(files( + 'gdbstub.c', + 'syscalls.c', + 'user.c' +)) + +system_ss.add(files( + 'gdbstub.c', + 'syscalls.c', + 'system.c' +)) # The user-target is specialised by the guest specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-target.c')) diff --git a/gdbstub/syscalls.c b/gdbstub/syscalls.c index 02e3a8f..e855df2 100644 --- a/gdbstub/syscalls.c +++ b/gdbstub/syscalls.c @@ -7,15 +7,16 @@ * Copyright (c) 2003-2005 Fabrice Bellard * Copyright (c) 2023 Linaro Ltd * - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "semihosting/semihost.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "gdbstub/user.h" #include "gdbstub/syscalls.h" +#include "gdbstub/commands.h" #include "trace.h" #include "internals.h" @@ -154,9 +155,9 @@ void gdb_handle_file_io(GArray *params, void *user_ctx) uint64_t ret; int err; - ret = get_param(params, 0)->val_ull; + ret = gdb_get_cmd_param(params, 0)->val_ull; if (params->len >= 2) { - err = get_param(params, 1)->val_ull; + err = gdb_get_cmd_param(params, 1)->val_ull; } else { err = 0; } @@ -196,7 +197,7 @@ void gdb_handle_file_io(GArray *params, void *user_ctx) gdbserver_syscall_state.current_syscall_cb = NULL; } - if (params->len >= 3 && get_param(params, 2)->opcode == (uint8_t)'C') { + if (params->len >= 3 && gdb_get_cmd_param(params, 2)->opcode == (uint8_t)'C') { gdb_put_packet("T02"); return; } diff --git a/gdbstub/system.c b/gdbstub/system.c index d235403..8a32d8e 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -7,7 +7,7 @@ * Copyright (c) 2003-2005 Fabrice Bellard * Copyright (c) 2022 Linaro Ltd * - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "qemu/osdep.h" @@ -16,11 +16,14 @@ #include "qemu/cutils.h" #include "exec/gdbstub.h" #include "gdbstub/syscalls.h" +#include "gdbstub/commands.h" #include "exec/hwaddr.h" #include "exec/tb-flush.h" -#include "sysemu/cpus.h" -#include "sysemu/runstate.h" -#include "sysemu/replay.h" +#include "system/accel-ops.h" +#include "system/cpus.h" +#include "system/runstate.h" +#include "system/replay.h" +#include "system/tcg.h" #include "hw/core/cpu.h" #include "hw/cpu/cluster.h" #include "hw/boards.h" @@ -170,7 +173,9 @@ static void gdb_vm_state_change(void *opaque, bool running, RunState state) } else { trace_gdbstub_hit_break(); } - tb_flush(cpu); + if (tcg_enabled()) { + tb_flush(cpu); + } ret = GDB_SIGNAL_TRAP; break; case RUN_STATE_PAUSED: @@ -238,7 +243,7 @@ static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend, *be_opened = false; } -static void char_gdb_class_init(ObjectClass *oc, void *data) +static void char_gdb_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -329,26 +334,27 @@ static void create_processes(GDBState *s) gdb_create_default_process(s); } -int gdbserver_start(const char *device) +bool gdbserver_start(const char *device, Error **errp) { Chardev *chr = NULL; Chardev *mon_chr; g_autoptr(GString) cs = g_string_new(device); if (!first_cpu) { - error_report("gdbstub: meaningless to attach gdb to a " - "machine without any CPU."); - return -1; + error_setg(errp, "gdbstub: meaningless to attach gdb to a " + "machine without any CPU."); + return false; } if (!gdb_supports_guest_debug()) { - error_report("gdbstub: current accelerator doesn't " - "support guest debugging"); - return -1; + error_setg(errp, "gdbstub: current accelerator doesn't " + "support guest debugging"); + return false; } if (cs->len == 0) { - return -1; + error_setg(errp, "gdbstub: missing connection string"); + return false; } trace_gdbstub_op_start(cs->str); @@ -373,7 +379,8 @@ int gdbserver_start(const char *device) */ chr = qemu_chr_new_noreplay("gdb", cs->str, true, NULL); if (!chr) { - return -1; + error_setg(errp, "gdbstub: couldn't create chardev"); + return false; } } @@ -405,7 +412,7 @@ int gdbserver_start(const char *device) gdbserver_system_state.mon_chr = mon_chr; gdb_syscall_reset(); - return 0; + return true; } static void register_types(void) @@ -449,8 +456,6 @@ static int phy_memory_mode; int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, uint8_t *buf, int len, bool is_write) { - CPUClass *cc; - if (phy_memory_mode) { if (is_write) { cpu_physical_memory_write(addr, buf, len); @@ -460,9 +465,8 @@ int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, return 0; } - cc = CPU_GET_CLASS(cpu); - if (cc->memory_rw_debug) { - return cc->memory_rw_debug(cpu, addr, buf, len, is_write); + if (cpu->cc->memory_rw_debug) { + return cpu->cc->memory_rw_debug(cpu, addr, buf, len, is_write); } return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); @@ -501,7 +505,7 @@ void gdb_handle_set_qemu_phy_mem_mode(GArray *params, void *ctx) return; } - if (!get_param(params, 0)->val_ul) { + if (!gdb_get_cmd_param(params, 0)->val_ul) { phy_memory_mode = 0; } else { phy_memory_mode = 1; @@ -519,7 +523,7 @@ void gdb_handle_query_rcmd(GArray *params, void *ctx) return; } - len = strlen(get_param(params, 0)->data); + len = strlen(gdb_get_cmd_param(params, 0)->data); if (len % 2) { gdb_put_packet("E01"); return; @@ -527,7 +531,7 @@ void gdb_handle_query_rcmd(GArray *params, void *ctx) g_assert(gdbserver_state.mem_buf->len == 0); len = len / 2; - gdb_hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len); + gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_param(params, 0)->data, len); g_byte_array_append(gdbserver_state.mem_buf, &zero, 1); qemu_chr_be_write(gdbserver_system_state.mon_chr, gdbserver_state.mem_buf->data, diff --git a/gdbstub/user-target.c b/gdbstub/user-target.c index a9c6c64..43231e6 100644 --- a/gdbstub/user-target.c +++ b/gdbstub/user-target.c @@ -4,11 +4,12 @@ * Copyright (c) 2003-2005 Fabrice Bellard * Copyright (c) 2022 Linaro Ltd * - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "qemu/osdep.h" #include "exec/gdbstub.h" +#include "gdbstub/commands.h" #include "qemu.h" #include "internals.h" #ifdef CONFIG_LINUX @@ -232,10 +233,8 @@ void gdb_handle_query_offsets(GArray *params, void *user_ctx) static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, uint8_t *buf, int len, bool is_write) { - CPUClass *cc; - cc = CPU_GET_CLASS(cpu); - if (cc->memory_rw_debug) { - return cc->memory_rw_debug(cpu, addr, buf, len, is_write); + if (cpu->cc->memory_rw_debug) { + return cpu->cc->memory_rw_debug(cpu, addr, buf, len, is_write); } return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); } @@ -250,8 +249,8 @@ void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx) return; } - offset = get_param(params, 0)->val_ul; - len = get_param(params, 1)->val_ul; + offset = gdb_get_cmd_param(params, 0)->val_ul; + len = gdb_get_cmd_param(params, 1)->val_ul; ts = get_task_state(gdbserver_state.c_cpu); saved_auxv = ts->info->saved_auxv; auxv_len = ts->info->auxv_len; @@ -288,7 +287,7 @@ void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx) static const char *get_filename_param(GArray *params, int i) { - const char *hex_filename = get_param(params, i)->data; + const char *hex_filename = gdb_get_cmd_param(params, i)->data; gdb_hextomem(gdbserver_state.mem_buf, hex_filename, strlen(hex_filename) / 2); g_byte_array_append(gdbserver_state.mem_buf, (const guint8 *)"", 1); @@ -306,8 +305,8 @@ static void hostio_reply_with_data(const void *buf, size_t n) void gdb_handle_v_file_open(GArray *params, void *user_ctx) { const char *filename = get_filename_param(params, 0); - uint64_t flags = get_param(params, 1)->val_ull; - uint64_t mode = get_param(params, 2)->val_ull; + uint64_t flags = gdb_get_cmd_param(params, 1)->val_ull; + uint64_t mode = gdb_get_cmd_param(params, 2)->val_ull; #ifdef CONFIG_LINUX int fd = do_guest_openat(cpu_env(gdbserver_state.g_cpu), 0, filename, @@ -316,19 +315,19 @@ void gdb_handle_v_file_open(GArray *params, void *user_ctx) int fd = open(filename, flags, mode); #endif if (fd < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); } else { - g_string_printf(gdbserver_state.str_buf, "F%d", fd); + g_string_printf(gdbserver_state.str_buf, "F%x", fd); } gdb_put_strbuf(); } void gdb_handle_v_file_close(GArray *params, void *user_ctx) { - int fd = get_param(params, 0)->val_ul; + int fd = gdb_get_cmd_param(params, 0)->val_ul; if (close(fd) == -1) { - g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); gdb_put_strbuf(); return; } @@ -338,9 +337,9 @@ void gdb_handle_v_file_close(GArray *params, void *user_ctx) void gdb_handle_v_file_pread(GArray *params, void *user_ctx) { - int fd = get_param(params, 0)->val_ul; - size_t count = get_param(params, 1)->val_ull; - off_t offset = get_param(params, 2)->val_ull; + int fd = gdb_get_cmd_param(params, 0)->val_ul; + size_t count = gdb_get_cmd_param(params, 1)->val_ull; + off_t offset = gdb_get_cmd_param(params, 2)->val_ull; size_t bufsiz = MIN(count, BUFSIZ); g_autofree char *buf = g_try_malloc(bufsiz); @@ -351,7 +350,7 @@ void gdb_handle_v_file_pread(GArray *params, void *user_ctx) ssize_t n = pread(fd, buf, bufsiz, offset); if (n < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); gdb_put_strbuf(); return; } @@ -374,7 +373,7 @@ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx) ssize_t n = readlink(filename, buf, BUFSIZ); #endif if (n < 0) { - g_string_printf(gdbserver_state.str_buf, "F-1,%d", errno); + g_string_printf(gdbserver_state.str_buf, "F-1,%x", errno); gdb_put_strbuf(); return; } @@ -383,9 +382,9 @@ void gdb_handle_v_file_readlink(GArray *params, void *user_ctx) void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx) { - uint32_t pid = get_param(params, 0)->val_ul; - uint32_t offset = get_param(params, 1)->val_ul; - uint32_t length = get_param(params, 2)->val_ul; + uint32_t pid = gdb_get_cmd_param(params, 0)->val_ul; + uint32_t offset = gdb_get_cmd_param(params, 1)->val_ul; + uint32_t length = gdb_get_cmd_param(params, 2)->val_ul; GDBProcess *process = gdb_get_process(pid); if (!process) { diff --git a/gdbstub/user.c b/gdbstub/user.c index e34b58b..67403e5 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -6,20 +6,23 @@ * Copyright (c) 2003-2005 Fabrice Bellard * Copyright (c) 2022 Linaro Ltd * - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "qemu/cutils.h" #include "qemu/sockets.h" +#include "qapi/error.h" #include "exec/hwaddr.h" #include "exec/tb-flush.h" #include "exec/gdbstub.h" +#include "gdbstub/commands.h" #include "gdbstub/syscalls.h" #include "gdbstub/user.h" #include "gdbstub/enums.h" #include "hw/core/cpu.h" +#include "user/signal.h" #include "trace.h" #include "internals.h" @@ -313,33 +316,20 @@ static bool gdb_accept_socket(int gdb_fd) return true; } -static int gdbserver_open_socket(const char *path) +static int gdbserver_open_socket(const char *path, Error **errp) { - struct sockaddr_un sockaddr = {}; - int fd, ret; + g_autoptr(GString) buf = g_string_new(""); + char *pid_placeholder; - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - perror("create socket"); - return -1; + pid_placeholder = strstr(path, "%d"); + if (pid_placeholder != NULL) { + g_string_append_len(buf, path, pid_placeholder - path); + g_string_append_printf(buf, "%d", qemu_get_thread_id()); + g_string_append(buf, pid_placeholder + 2); + path = buf->str; } - sockaddr.sun_family = AF_UNIX; - pstrcpy(sockaddr.sun_path, sizeof(sockaddr.sun_path) - 1, path); - ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); - if (ret < 0) { - perror("bind socket"); - close(fd); - return -1; - } - ret = listen(fd, 1); - if (ret < 0) { - perror("listen socket"); - close(fd); - return -1; - } - - return fd; + return unix_listen(path, errp); } static bool gdb_accept_tcp(int gdb_fd) @@ -371,14 +361,14 @@ static bool gdb_accept_tcp(int gdb_fd) return true; } -static int gdbserver_open_port(int port) +static int gdbserver_open_port(int port, Error **errp) { struct sockaddr_in sockaddr; int fd, ret; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) { - perror("socket"); + error_setg_errno(errp, errno, "Failed to create socket"); return -1; } qemu_set_cloexec(fd); @@ -390,13 +380,13 @@ static int gdbserver_open_port(int port) sockaddr.sin_addr.s_addr = 0; ret = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); if (ret < 0) { - perror("bind"); + error_setg_errno(errp, errno, "Failed to bind socket"); close(fd); return -1; } ret = listen(fd, 1); if (ret < 0) { - perror("listen"); + error_setg_errno(errp, errno, "Failed to listen to socket"); close(fd); return -1; } @@ -404,31 +394,122 @@ static int gdbserver_open_port(int port) return fd; } -int gdbserver_start(const char *port_or_path) +static bool gdbserver_accept(int port, int gdb_fd, const char *path) { - int port = g_ascii_strtoull(port_or_path, NULL, 10); - int gdb_fd; + bool ret; if (port > 0) { - gdb_fd = gdbserver_open_port(port); + ret = gdb_accept_tcp(gdb_fd); } else { - gdb_fd = gdbserver_open_socket(port_or_path); + ret = gdb_accept_socket(gdb_fd); + if (ret) { + gdbserver_user_state.socket_path = g_strdup(path); + } } - if (gdb_fd < 0) { - return -1; + if (!ret) { + close(gdb_fd); + } + + return ret; +} + +struct { + int port; + int gdb_fd; + char *path; +} gdbserver_args; + +static void do_gdb_handlesig(CPUState *cs, run_on_cpu_data arg) +{ + int sig; + + sig = target_to_host_signal(gdb_handlesig(cs, 0, NULL, NULL, 0)); + if (sig >= 1 && sig < NSIG) { + qemu_kill_thread(gdb_get_cpu_index(cs), sig); + } +} + +static void *gdbserver_accept_thread(void *arg) +{ + if (gdbserver_accept(gdbserver_args.port, gdbserver_args.gdb_fd, + gdbserver_args.path)) { + CPUState *cs = first_cpu; + + async_safe_run_on_cpu(cs, do_gdb_handlesig, RUN_ON_CPU_NULL); + qemu_kill_thread(gdb_get_cpu_index(cs), host_interrupt_signal); + } + + g_free(gdbserver_args.path); + gdbserver_args.path = NULL; + + return NULL; +} + +#define USAGE "\nUsage: -g {port|path}[,suspend={y|n}]" + +bool gdbserver_start(const char *args, Error **errp) +{ + g_auto(GStrv) argv = g_strsplit(args, ",", 0); + const char *port_or_path = NULL; + bool suspend = true; + int gdb_fd, port; + GStrv arg; + + for (arg = argv; *arg; arg++) { + g_auto(GStrv) tokens = g_strsplit(*arg, "=", 2); + + if (g_strcmp0(tokens[0], "suspend") == 0) { + if (tokens[1] == NULL) { + error_setg(errp, + "gdbstub: missing \"suspend\" option value" USAGE); + return false; + } else if (!qapi_bool_parse(tokens[0], tokens[1], + &suspend, errp)) { + return false; + } + } else { + if (port_or_path) { + error_setg(errp, "gdbstub: unknown option \"%s\"" USAGE, *arg); + return false; + } + port_or_path = *arg; + } + } + if (!port_or_path) { + error_setg(errp, "gdbstub: port or path not specified" USAGE); + return false; } - if (port > 0 && gdb_accept_tcp(gdb_fd)) { - return 0; - } else if (gdb_accept_socket(gdb_fd)) { - gdbserver_user_state.socket_path = g_strdup(port_or_path); - return 0; + port = g_ascii_strtoull(port_or_path, NULL, 10); + if (port > 0) { + gdb_fd = gdbserver_open_port(port, errp); + } else { + gdb_fd = gdbserver_open_socket(port_or_path, errp); + } + if (gdb_fd < 0) { + return false; } - /* gone wrong */ - close(gdb_fd); - return -1; + if (suspend) { + if (gdbserver_accept(port, gdb_fd, port_or_path)) { + gdb_handlesig(first_cpu, 0, NULL, NULL, 0); + return true; + } else { + error_setg(errp, "gdbstub: failed to accept connection"); + return false; + } + } else { + QemuThread thread; + + gdbserver_args.port = port; + gdbserver_args.gdb_fd = gdb_fd; + gdbserver_args.path = g_strdup(port_or_path); + qemu_thread_create(&thread, "gdb-accept", + &gdbserver_accept_thread, NULL, + QEMU_THREAD_DETACHED); + return true; + } } void gdbserver_fork_start(void) @@ -662,11 +743,8 @@ int gdb_continue_partial(char *newstates) int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, uint8_t *buf, int len, bool is_write) { - CPUClass *cc; - - cc = CPU_GET_CLASS(cpu); - if (cc->memory_rw_debug) { - return cc->memory_rw_debug(cpu, addr, buf, len, is_write); + if (cpu->cc->memory_rw_debug) { + return cpu->cc->memory_rw_debug(cpu, addr, buf, len, is_write); } return cpu_memory_rw_debug(cpu, addr, buf, len, is_write); } @@ -793,7 +871,7 @@ void gdb_syscall_return(CPUState *cs, int num) void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx) { - const char *param = get_param(params, 0)->data; + const char *param = gdb_get_cmd_param(params, 0)->data; GDBSyscallsMask catch_syscalls_mask; bool catch_all_syscalls; unsigned int num; @@ -858,8 +936,8 @@ void gdb_handle_query_xfer_siginfo(GArray *params, void *user_ctx) unsigned long offset, len; uint8_t *siginfo_offset; - offset = get_param(params, 0)->val_ul; - len = get_param(params, 1)->val_ul; + offset = gdb_get_cmd_param(params, 0)->val_ul; + len = gdb_get_cmd_param(params, 1)->val_ul; if (offset + len > gdbserver_user_state.siginfo_len) { /* Invalid offset and/or requested length. */ |