#include "qemu/ctype.h"
#include "qemu/cutils.h"
#include "qemu/module.h"
-#include "trace-root.h"
+#include "trace/trace-root.h"
+#include "exec/gdbstub.h"
#ifdef CONFIG_USER_ONLY
#include "qemu.h"
#else
#include "monitor/monitor.h"
#include "chardev/char.h"
#include "chardev/char-fe.h"
-#include "sysemu/sysemu.h"
-#include "exec/gdbstub.h"
#include "hw/cpu/cluster.h"
#include "hw/boards.h"
#endif
#include "sysemu/hw_accel.h"
#include "sysemu/kvm.h"
#include "sysemu/runstate.h"
-#include "hw/semihosting/semihost.h"
+#include "semihosting/semihost.h"
#include "exec/exec-all.h"
+#include "sysemu/replay.h"
#ifdef CONFIG_USER_ONLY
#define GDB_ATTACHED "0"
int signal;
#ifdef CONFIG_USER_ONLY
int fd;
+ char *socket_path;
int running_state;
#else
CharBackend chr;
*/
static int sstep_flags = SSTEP_ENABLE|SSTEP_NOIRQ|SSTEP_NOTIMER;
+/* Retrieves flags for single step mode. */
+static int get_sstep_flags(void)
+{
+ /*
+ * In replay mode all events written into the log should be replayed.
+ * That is why NOIRQ flag is removed in this mode.
+ */
+ if (replay_mode != REPLAY_MODE_NONE) {
+ return SSTEP_ENABLE;
+ } else {
+ return sstep_flags;
+ }
+}
+
static GDBState gdbserver_state;
static void init_gdbserver_state(void)
bool gdb_has_xml;
#ifdef CONFIG_USER_ONLY
-/* XXX: This is not thread safe. Do we care? */
-static int gdbserver_fd = -1;
static int get_char(void)
{
return gdb_syscall_mode == GDB_SYS_ENABLED;
}
+static bool stub_can_reverse(void)
+{
+#ifdef CONFIG_USER_ONLY
+ return false;
+#else
+ return replay_mode == REPLAY_MODE_PLAY;
+#endif
+}
+
/* Resume execution. */
static inline void gdb_continue(void)
{
break; /* nothing to do here */
case 's':
trace_gdbstub_op_stepping(cpu->cpu_index);
- cpu_single_step(cpu, sstep_flags);
+ cpu_single_step(cpu, get_sstep_flags());
cpu_resume(cpu);
flag = 1;
break;
cur_action = *p++;
if (cur_action == 'C' || cur_action == 'S') {
cur_action = qemu_tolower(cur_action);
- res = qemu_strtoul(p + 1, &p, 16, &tmp);
+ res = qemu_strtoul(p, &p, 16, &tmp);
if (res) {
goto out;
}
} thread_id;
} GdbCmdVariant;
+#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i))
+
static const char *cmd_next_param(const char *param, const char delimiter)
{
static const char all_delimiters[] = ",;:=";
}
static int cmd_parse_params(const char *data, const char *schema,
- GdbCmdVariant *params, int *num_params)
+ GArray *params)
{
- int curr_param;
const char *curr_schema, *curr_data;
- *num_params = 0;
-
- if (!schema) {
- return 0;
- }
+ g_assert(schema);
+ g_assert(params->len == 0);
curr_schema = schema;
- curr_param = 0;
curr_data = data;
while (curr_schema[0] && curr_schema[1] && *curr_data) {
+ GdbCmdVariant this_param;
+
switch (curr_schema[0]) {
case 'l':
if (qemu_strtoul(curr_data, &curr_data, 16,
- ¶ms[curr_param].val_ul)) {
+ &this_param.val_ul)) {
return -EINVAL;
}
- curr_param++;
curr_data = cmd_next_param(curr_data, curr_schema[1]);
+ g_array_append_val(params, this_param);
break;
case 'L':
if (qemu_strtou64(curr_data, &curr_data, 16,
- (uint64_t *)¶ms[curr_param].val_ull)) {
+ (uint64_t *)&this_param.val_ull)) {
return -EINVAL;
}
- curr_param++;
curr_data = cmd_next_param(curr_data, curr_schema[1]);
+ g_array_append_val(params, this_param);
break;
case 's':
- params[curr_param].data = curr_data;
- curr_param++;
+ this_param.data = curr_data;
curr_data = cmd_next_param(curr_data, curr_schema[1]);
+ g_array_append_val(params, this_param);
break;
case 'o':
- params[curr_param].opcode = *(uint8_t *)curr_data;
- curr_param++;
+ this_param.opcode = *(uint8_t *)curr_data;
curr_data = cmd_next_param(curr_data, curr_schema[1]);
+ g_array_append_val(params, this_param);
break;
case 't':
- params[curr_param].thread_id.kind =
+ this_param.thread_id.kind =
read_thread_id(curr_data, &curr_data,
- ¶ms[curr_param].thread_id.pid,
- ¶ms[curr_param].thread_id.tid);
- curr_param++;
+ &this_param.thread_id.pid,
+ &this_param.thread_id.tid);
curr_data = cmd_next_param(curr_data, curr_schema[1]);
+ g_array_append_val(params, this_param);
break;
case '?':
curr_data = cmd_next_param(curr_data, curr_schema[1]);
curr_schema += 2;
}
- *num_params = curr_param;
return 0;
}
-typedef struct GdbCmdContext {
- GdbCmdVariant *params;
- int num_params;
-} GdbCmdContext;
-
-typedef void (*GdbCmdHandler)(GdbCmdContext *gdb_ctx, void *user_ctx);
+typedef void (*GdbCmdHandler)(GArray *params, void *user_ctx);
/*
* cmd_startswith -> cmd is compared using startswith
static int process_string_cmd(void *user_ctx, const char *data,
const GdbCmdParseEntry *cmds, int num_cmds)
{
- int i, schema_len, max_num_params = 0;
- GdbCmdContext gdb_ctx;
+ int i;
+ g_autoptr(GArray) params = g_array_new(false, true, sizeof(GdbCmdVariant));
if (!cmds) {
return -1;
}
if (cmd->schema) {
- schema_len = strlen(cmd->schema);
- if (schema_len % 2) {
- return -2;
+ if (cmd_parse_params(&data[strlen(cmd->cmd)],
+ cmd->schema, params)) {
+ return -1;
}
-
- max_num_params = schema_len / 2;
- }
-
- gdb_ctx.params =
- (GdbCmdVariant *)alloca(sizeof(*gdb_ctx.params) * max_num_params);
- memset(gdb_ctx.params, 0, sizeof(*gdb_ctx.params) * max_num_params);
-
- if (cmd_parse_params(&data[strlen(cmd->cmd)], cmd->schema,
- gdb_ctx.params, &gdb_ctx.num_params)) {
- return -1;
}
- cmd->handler(&gdb_ctx, user_ctx);
+ cmd->handler(params, user_ctx);
return 0;
}
}
}
-static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_detach(GArray *params, void *user_ctx)
{
GDBProcess *process;
uint32_t pid = 1;
if (gdbserver_state.multiprocess) {
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
put_packet("E22");
return;
}
- pid = gdb_ctx->params[0].val_ul;
+ pid = get_param(params, 0)->val_ul;
}
process = gdb_get_process(pid);
put_packet("OK");
}
-static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_thread_alive(GArray *params, void *user_ctx)
{
CPUState *cpu;
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
put_packet("E22");
return;
}
- if (gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) {
+ if (get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) {
put_packet("E22");
return;
}
- cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid,
- gdb_ctx->params[0].thread_id.tid);
+ cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid,
+ get_param(params, 0)->thread_id.tid);
if (!cpu) {
put_packet("E22");
return;
put_packet("OK");
}
-static void handle_continue(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_continue(GArray *params, void *user_ctx)
{
- if (gdb_ctx->num_params) {
- gdb_set_cpu_pc(gdb_ctx->params[0].val_ull);
+ if (params->len) {
+ gdb_set_cpu_pc(get_param(params, 0)->val_ull);
}
gdbserver_state.signal = 0;
gdb_continue();
}
-static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_cont_with_sig(GArray *params, void *user_ctx)
{
unsigned long signal = 0;
* Note: C sig;[addr] is currently unsupported and we simply
* omit the addr parameter
*/
- if (gdb_ctx->num_params) {
- signal = gdb_ctx->params[0].val_ul;
+ if (params->len) {
+ signal = get_param(params, 0)->val_ul;
}
gdbserver_state.signal = gdb_signal_to_target(signal);
gdb_continue();
}
-static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_set_thread(GArray *params, void *user_ctx)
{
CPUState *cpu;
- if (gdb_ctx->num_params != 2) {
+ if (params->len != 2) {
put_packet("E22");
return;
}
- if (gdb_ctx->params[1].thread_id.kind == GDB_READ_THREAD_ERR) {
+ if (get_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) {
put_packet("E22");
return;
}
- if (gdb_ctx->params[1].thread_id.kind != GDB_ONE_THREAD) {
+ if (get_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) {
put_packet("OK");
return;
}
- cpu = gdb_get_cpu(gdb_ctx->params[1].thread_id.pid,
- gdb_ctx->params[1].thread_id.tid);
+ cpu = gdb_get_cpu(get_param(params, 1)->thread_id.pid,
+ get_param(params, 1)->thread_id.tid);
if (!cpu) {
put_packet("E22");
return;
* Note: This command is deprecated and modern gdb's will be using the
* vCont command instead.
*/
- switch (gdb_ctx->params[0].opcode) {
+ switch (get_param(params, 0)->opcode) {
case 'c':
gdbserver_state.c_cpu = cpu;
put_packet("OK");
}
}
-static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_insert_bp(GArray *params, void *user_ctx)
{
int res;
- if (gdb_ctx->num_params != 3) {
+ if (params->len != 3) {
put_packet("E22");
return;
}
- res = gdb_breakpoint_insert(gdb_ctx->params[0].val_ul,
- gdb_ctx->params[1].val_ull,
- gdb_ctx->params[2].val_ull);
+ res = gdb_breakpoint_insert(get_param(params, 0)->val_ul,
+ get_param(params, 1)->val_ull,
+ get_param(params, 2)->val_ull);
if (res >= 0) {
put_packet("OK");
return;
put_packet("E22");
}
-static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_remove_bp(GArray *params, void *user_ctx)
{
int res;
- if (gdb_ctx->num_params != 3) {
+ if (params->len != 3) {
put_packet("E22");
return;
}
- res = gdb_breakpoint_remove(gdb_ctx->params[0].val_ul,
- gdb_ctx->params[1].val_ull,
- gdb_ctx->params[2].val_ull);
+ res = gdb_breakpoint_remove(get_param(params, 0)->val_ul,
+ get_param(params, 1)->val_ull,
+ get_param(params, 2)->val_ull);
if (res >= 0) {
put_packet("OK");
return;
* the remote gdb to fallback to older methods.
*/
-static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_set_reg(GArray *params, void *user_ctx)
{
int reg_size;
return;
}
- if (gdb_ctx->num_params != 2) {
+ if (params->len != 2) {
put_packet("E22");
return;
}
- reg_size = strlen(gdb_ctx->params[1].data) / 2;
- hextomem(gdbserver_state.mem_buf, gdb_ctx->params[1].data, reg_size);
+ reg_size = strlen(get_param(params, 1)->data) / 2;
+ hextomem(gdbserver_state.mem_buf, get_param(params, 1)->data, reg_size);
gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data,
- gdb_ctx->params[0].val_ull);
+ get_param(params, 0)->val_ull);
put_packet("OK");
}
-static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_get_reg(GArray *params, void *user_ctx)
{
int reg_size;
return;
}
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
put_packet("E14");
return;
}
reg_size = gdb_read_register(gdbserver_state.g_cpu,
gdbserver_state.mem_buf,
- gdb_ctx->params[0].val_ull);
+ get_param(params, 0)->val_ull);
if (!reg_size) {
put_packet("E14");
return;
put_strbuf();
}
-static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_write_mem(GArray *params, void *user_ctx)
{
- if (gdb_ctx->num_params != 3) {
+ if (params->len != 3) {
put_packet("E22");
return;
}
/* hextomem() reads 2*len bytes */
- if (gdb_ctx->params[1].val_ull > strlen(gdb_ctx->params[2].data) / 2) {
+ if (get_param(params, 1)->val_ull >
+ strlen(get_param(params, 2)->data) / 2) {
put_packet("E22");
return;
}
- hextomem(gdbserver_state.mem_buf, gdb_ctx->params[2].data,
- gdb_ctx->params[1].val_ull);
- if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull,
+ hextomem(gdbserver_state.mem_buf, get_param(params, 2)->data,
+ get_param(params, 1)->val_ull);
+ if (target_memory_rw_debug(gdbserver_state.g_cpu,
+ get_param(params, 0)->val_ull,
gdbserver_state.mem_buf->data,
gdbserver_state.mem_buf->len, true)) {
put_packet("E14");
put_packet("OK");
}
-static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_read_mem(GArray *params, void *user_ctx)
{
- if (gdb_ctx->num_params != 2) {
+ if (params->len != 2) {
put_packet("E22");
return;
}
/* memtohex() doubles the required space */
- if (gdb_ctx->params[1].val_ull > MAX_PACKET_LENGTH / 2) {
+ if (get_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) {
put_packet("E22");
return;
}
- g_byte_array_set_size(gdbserver_state.mem_buf, gdb_ctx->params[1].val_ull);
+ g_byte_array_set_size(gdbserver_state.mem_buf,
+ get_param(params, 1)->val_ull);
- if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull,
+ if (target_memory_rw_debug(gdbserver_state.g_cpu,
+ get_param(params, 0)->val_ull,
gdbserver_state.mem_buf->data,
gdbserver_state.mem_buf->len, false)) {
put_packet("E14");
put_strbuf();
}
-static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_write_all_regs(GArray *params, void *user_ctx)
{
target_ulong addr, len;
uint8_t *registers;
int reg_size;
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
return;
}
cpu_synchronize_state(gdbserver_state.g_cpu);
- len = strlen(gdb_ctx->params[0].data) / 2;
- hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len);
+ len = strlen(get_param(params, 0)->data) / 2;
+ hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
registers = gdbserver_state.mem_buf->data;
for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0;
addr++) {
put_packet("OK");
}
-static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_read_all_regs(GArray *params, void *user_ctx)
{
target_ulong addr, len;
put_strbuf();
}
-static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_file_io(GArray *params, void *user_ctx)
{
- if (gdb_ctx->num_params >= 1 && gdbserver_state.current_syscall_cb) {
+ if (params->len >= 1 && gdbserver_state.current_syscall_cb) {
target_ulong ret, err;
- ret = (target_ulong)gdb_ctx->params[0].val_ull;
- if (gdb_ctx->num_params >= 2) {
- err = (target_ulong)gdb_ctx->params[1].val_ull;
+ ret = (target_ulong)get_param(params, 0)->val_ull;
+ if (params->len >= 2) {
+ err = (target_ulong)get_param(params, 1)->val_ull;
} else {
err = 0;
}
gdbserver_state.current_syscall_cb = NULL;
}
- if (gdb_ctx->num_params >= 3 && gdb_ctx->params[2].opcode == (uint8_t)'C') {
+ if (params->len >= 3 && get_param(params, 2)->opcode == (uint8_t)'C') {
put_packet("T02");
return;
}
gdb_continue();
}
-static void handle_step(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_step(GArray *params, void *user_ctx)
{
- if (gdb_ctx->num_params) {
- gdb_set_cpu_pc((target_ulong)gdb_ctx->params[0].val_ull);
+ if (params->len) {
+ gdb_set_cpu_pc((target_ulong)get_param(params, 0)->val_ull);
}
- cpu_single_step(gdbserver_state.c_cpu, sstep_flags);
+ cpu_single_step(gdbserver_state.c_cpu, get_sstep_flags());
gdb_continue();
}
-static void handle_v_cont_query(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_backward(GArray *params, void *user_ctx)
+{
+ if (!stub_can_reverse()) {
+ put_packet("E22");
+ }
+ if (params->len == 1) {
+ switch (get_param(params, 0)->opcode) {
+ case 's':
+ if (replay_reverse_step()) {
+ gdb_continue();
+ } else {
+ put_packet("E14");
+ }
+ return;
+ case 'c':
+ if (replay_reverse_continue()) {
+ gdb_continue();
+ } else {
+ put_packet("E14");
+ }
+ return;
+ }
+ }
+
+ /* Default invalid command */
+ put_packet("");
+}
+
+static void handle_v_cont_query(GArray *params, void *user_ctx)
{
put_packet("vCont;c;C;s;S");
}
-static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_v_cont(GArray *params, void *user_ctx)
{
int res;
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
return;
}
- res = gdb_handle_vcont(gdb_ctx->params[0].data);
+ res = gdb_handle_vcont(get_param(params, 0)->data);
if ((res == -EINVAL) || (res == -ERANGE)) {
put_packet("E22");
} else if (res) {
}
}
-static void handle_v_attach(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_v_attach(GArray *params, void *user_ctx)
{
GDBProcess *process;
CPUState *cpu;
g_string_assign(gdbserver_state.str_buf, "E22");
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
goto cleanup;
}
- process = gdb_get_process(gdb_ctx->params[0].val_ul);
+ process = gdb_get_process(get_param(params, 0)->val_ul);
if (!process) {
goto cleanup;
}
put_strbuf();
}
-static void handle_v_kill(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_v_kill(GArray *params, void *user_ctx)
{
/* Kill the target */
put_packet("OK");
error_report("QEMU: Terminated via GDBstub");
+ gdb_exit(0);
exit(0);
}
-static GdbCmdParseEntry gdb_v_commands_table[] = {
+static const GdbCmdParseEntry gdb_v_commands_table[] = {
/* Order is important if has same prefix */
{
.handler = handle_v_cont_query,
},
};
-static void handle_v_commands(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_v_commands(GArray *params, void *user_ctx)
{
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
return;
}
- if (process_string_cmd(NULL, gdb_ctx->params[0].data,
+ if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_v_commands_table,
ARRAY_SIZE(gdb_v_commands_table))) {
put_packet("");
}
}
-static void handle_query_qemu_sstepbits(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_qemu_sstepbits(GArray *params, void *user_ctx)
{
g_string_printf(gdbserver_state.str_buf, "ENABLE=%x,NOIRQ=%x,NOTIMER=%x",
SSTEP_ENABLE, SSTEP_NOIRQ, SSTEP_NOTIMER);
put_strbuf();
}
-static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_set_qemu_sstep(GArray *params, void *user_ctx)
{
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
return;
}
- sstep_flags = gdb_ctx->params[0].val_ul;
+ sstep_flags = get_param(params, 0)->val_ul;
put_packet("OK");
}
-static void handle_query_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_qemu_sstep(GArray *params, void *user_ctx)
{
g_string_printf(gdbserver_state.str_buf, "0x%x", sstep_flags);
put_strbuf();
}
-static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_curr_tid(GArray *params, void *user_ctx)
{
CPUState *cpu;
GDBProcess *process;
put_strbuf();
}
-static void handle_query_threads(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_threads(GArray *params, void *user_ctx)
{
if (!gdbserver_state.query_cpu) {
put_packet("l");
gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu);
}
-static void handle_query_first_threads(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_first_threads(GArray *params, void *user_ctx)
{
gdbserver_state.query_cpu = gdb_first_attached_cpu();
- handle_query_threads(gdb_ctx, user_ctx);
+ handle_query_threads(params, user_ctx);
}
-static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_thread_extra(GArray *params, void *user_ctx)
{
g_autoptr(GString) rs = g_string_new(NULL);
CPUState *cpu;
- if (!gdb_ctx->num_params ||
- gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) {
+ if (!params->len ||
+ get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) {
put_packet("E22");
return;
}
- cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid,
- gdb_ctx->params[0].thread_id.tid);
+ cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid,
+ get_param(params, 0)->thread_id.tid);
if (!cpu) {
return;
}
/* Print the CPU model and name in multiprocess mode */
ObjectClass *oc = object_get_class(OBJECT(cpu));
const char *cpu_model = object_class_get_name(oc);
- g_autofree char *cpu_name;
- cpu_name = object_get_canonical_path_component(OBJECT(cpu));
+ const char *cpu_name =
+ object_get_canonical_path_component(OBJECT(cpu));
g_string_printf(rs, "%s %s [%s]", cpu_model, cpu_name,
cpu->halted ? "halted " : "running");
} else {
}
#ifdef CONFIG_USER_ONLY
-static void handle_query_offsets(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_offsets(GArray *params, void *user_ctx)
{
TaskState *ts;
put_strbuf();
}
#else
-static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_rcmd(GArray *params, void *user_ctx)
{
const guint8 zero = 0;
int len;
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
put_packet("E22");
return;
}
- len = strlen(gdb_ctx->params[0].data);
+ len = strlen(get_param(params, 0)->data);
if (len % 2) {
put_packet("E01");
return;
g_assert(gdbserver_state.mem_buf->len == 0);
len = len / 2;
- hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len);
+ hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
g_byte_array_append(gdbserver_state.mem_buf, &zero, 1);
qemu_chr_be_write(gdbserver_state.mon_chr, gdbserver_state.mem_buf->data,
gdbserver_state.mem_buf->len);
}
#endif
-static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_supported(GArray *params, void *user_ctx)
{
CPUClass *cc;
g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+");
}
- if (gdb_ctx->num_params &&
- strstr(gdb_ctx->params[0].data, "multiprocess+")) {
+ if (stub_can_reverse()) {
+ g_string_append(gdbserver_state.str_buf,
+ ";ReverseStep+;ReverseContinue+");
+ }
+
+#ifdef CONFIG_USER_ONLY
+ if (gdbserver_state.c_cpu->opaque) {
+ g_string_append(gdbserver_state.str_buf, ";qXfer:auxv:read+");
+ }
+#endif
+
+ if (params->len &&
+ strstr(get_param(params, 0)->data, "multiprocess+")) {
gdbserver_state.multiprocess = true;
}
put_strbuf();
}
-static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_xfer_features(GArray *params, void *user_ctx)
{
GDBProcess *process;
CPUClass *cc;
const char *xml;
const char *p;
- if (gdb_ctx->num_params < 3) {
+ if (params->len < 3) {
put_packet("E22");
return;
}
}
gdb_has_xml = true;
- p = gdb_ctx->params[0].data;
+ p = get_param(params, 0)->data;
xml = get_feature_xml(p, &p, process);
if (!xml) {
put_packet("E00");
return;
}
- addr = gdb_ctx->params[1].val_ul;
- len = gdb_ctx->params[2].val_ul;
+ addr = get_param(params, 1)->val_ul;
+ len = get_param(params, 2)->val_ul;
total_len = strlen(xml);
if (addr > total_len) {
put_packet("E00");
gdbserver_state.str_buf->len, true);
}
-static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx)
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER)
+static void handle_query_xfer_auxv(GArray *params, void *user_ctx)
+{
+ TaskState *ts;
+ unsigned long offset, len, saved_auxv, auxv_len;
+
+ if (params->len < 2) {
+ put_packet("E22");
+ return;
+ }
+
+ offset = get_param(params, 0)->val_ul;
+ len = get_param(params, 1)->val_ul;
+ ts = gdbserver_state.c_cpu->opaque;
+ saved_auxv = ts->info->saved_auxv;
+ auxv_len = ts->info->auxv_len;
+
+ if (offset >= auxv_len) {
+ put_packet("E00");
+ return;
+ }
+
+ if (len > (MAX_PACKET_LENGTH - 5) / 2) {
+ len = (MAX_PACKET_LENGTH - 5) / 2;
+ }
+
+ if (len < auxv_len - offset) {
+ g_string_assign(gdbserver_state.str_buf, "m");
+ } else {
+ g_string_assign(gdbserver_state.str_buf, "l");
+ len = auxv_len - offset;
+ }
+
+ g_byte_array_set_size(gdbserver_state.mem_buf, len);
+ if (target_memory_rw_debug(gdbserver_state.g_cpu, saved_auxv + offset,
+ gdbserver_state.mem_buf->data, len, false)) {
+ put_packet("E14");
+ return;
+ }
+
+ memtox(gdbserver_state.str_buf,
+ (const char *)gdbserver_state.mem_buf->data, len);
+ put_packet_binary(gdbserver_state.str_buf->str,
+ gdbserver_state.str_buf->len, true);
+}
+#endif
+
+static void handle_query_attached(GArray *params, void *user_ctx)
{
put_packet(GDB_ATTACHED);
}
-static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_qemu_supported(GArray *params, void *user_ctx)
{
g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep");
#ifndef CONFIG_USER_ONLY
}
#ifndef CONFIG_USER_ONLY
-static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx,
+static void handle_query_qemu_phy_mem_mode(GArray *params,
void *user_ctx)
{
g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode);
put_strbuf();
}
-static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_set_qemu_phy_mem_mode(GArray *params, void *user_ctx)
{
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
put_packet("E22");
return;
}
- if (!gdb_ctx->params[0].val_ul) {
+ if (!get_param(params, 0)->val_ul) {
phy_memory_mode = 0;
} else {
phy_memory_mode = 1;
}
#endif
-static GdbCmdParseEntry gdb_gen_query_set_common_table[] = {
+static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = {
/* Order is important if has same prefix */
{
.handler = handle_query_qemu_sstepbits,
},
};
-static GdbCmdParseEntry gdb_gen_query_table[] = {
+static const GdbCmdParseEntry gdb_gen_query_table[] = {
{
.handler = handle_query_curr_tid,
.cmd = "C",
.cmd_startswith = 1,
.schema = "s:l,l0"
},
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER)
+ {
+ .handler = handle_query_xfer_auxv,
+ .cmd = "Xfer:auxv:read::",
+ .cmd_startswith = 1,
+ .schema = "l,l0"
+ },
+#endif
{
.handler = handle_query_attached,
.cmd = "Attached:",
#endif
};
-static GdbCmdParseEntry gdb_gen_set_table[] = {
+static const GdbCmdParseEntry gdb_gen_set_table[] = {
/* Order is important if has same prefix */
{
.handler = handle_set_qemu_sstep,
#endif
};
-static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_gen_query(GArray *params, void *user_ctx)
{
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
return;
}
- if (!process_string_cmd(NULL, gdb_ctx->params[0].data,
+ if (!process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_query_set_common_table,
ARRAY_SIZE(gdb_gen_query_set_common_table))) {
return;
}
- if (process_string_cmd(NULL, gdb_ctx->params[0].data,
+ if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_query_table,
ARRAY_SIZE(gdb_gen_query_table))) {
put_packet("");
}
}
-static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_gen_set(GArray *params, void *user_ctx)
{
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
return;
}
- if (!process_string_cmd(NULL, gdb_ctx->params[0].data,
+ if (!process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_query_set_common_table,
ARRAY_SIZE(gdb_gen_query_set_common_table))) {
return;
}
- if (process_string_cmd(NULL, gdb_ctx->params[0].data,
+ if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_set_table,
ARRAY_SIZE(gdb_gen_set_table))) {
put_packet("");
}
}
-static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_target_halt(GArray *params, void *user_ctx)
{
g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP);
gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf);
case 'k':
/* Kill the target */
error_report("QEMU: Terminated via GDBstub");
+ gdb_exit(0);
exit(0);
case 'D':
{
cmd_parser = &step_cmd_desc;
}
break;
+ case 'b':
+ {
+ static const GdbCmdParseEntry backward_cmd_desc = {
+ .handler = handle_backward,
+ .cmd = "b",
+ .cmd_startswith = 1,
+ .schema = "o0"
+ };
+ cmd_parser = &backward_cmd_desc;
+ }
+ break;
case 'F':
{
static const GdbCmdParseEntry file_io_cmd_desc = {
}
#ifndef CONFIG_USER_ONLY
-static void gdb_vm_state_change(void *opaque, int running, RunState state)
+static void gdb_vm_state_change(void *opaque, bool running, RunState state)
{
CPUState *cpu = gdbserver_state.c_cpu;
g_autoptr(GString) buf = g_string_new(NULL);
}
/* Tell the remote gdb that the process has exited. */
-void gdb_exit(CPUArchState *env, int code)
+void gdb_exit(int code)
{
char buf[4];
return;
}
#ifdef CONFIG_USER_ONLY
- if (gdbserver_fd < 0 || gdbserver_state.fd < 0) {
+ if (gdbserver_state.socket_path) {
+ unlink(gdbserver_state.socket_path);
+ }
+ if (gdbserver_state.fd < 0) {
return;
}
#endif
char buf[256];
int n;
- if (gdbserver_fd < 0 || gdbserver_state.fd < 0) {
+ if (!gdbserver_state.init || gdbserver_state.fd < 0) {
return sig;
}
{
char buf[4];
- if (gdbserver_fd < 0 || gdbserver_state.fd < 0) {
+ if (!gdbserver_state.init || gdbserver_state.fd < 0) {
return;
}
put_packet(buf);
}
-static bool gdb_accept(void)
+static void gdb_accept_init(int fd)
{
- struct sockaddr_in sockaddr;
+ init_gdbserver_state();
+ create_default_process(&gdbserver_state);
+ gdbserver_state.processes[0].attached = true;
+ gdbserver_state.c_cpu = gdb_first_attached_cpu();
+ gdbserver_state.g_cpu = gdbserver_state.c_cpu;
+ gdbserver_state.fd = fd;
+ gdb_has_xml = false;
+}
+
+static bool gdb_accept_socket(int gdb_fd)
+{
+ int fd;
+
+ for(;;) {
+ fd = accept(gdb_fd, NULL, NULL);
+ if (fd < 0 && errno != EINTR) {
+ perror("accept socket");
+ return false;
+ } else if (fd >= 0) {
+ qemu_set_cloexec(fd);
+ break;
+ }
+ }
+
+ gdb_accept_init(fd);
+ return true;
+}
+
+static int gdbserver_open_socket(const char *path)
+{
+ struct sockaddr_un sockaddr = {};
+ int fd, ret;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("create socket");
+ return -1;
+ }
+
+ 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;
+}
+
+static bool gdb_accept_tcp(int gdb_fd)
+{
+ struct sockaddr_in sockaddr = {};
socklen_t len;
int fd;
for(;;) {
len = sizeof(sockaddr);
- fd = accept(gdbserver_fd, (struct sockaddr *)&sockaddr, &len);
+ fd = accept(gdb_fd, (struct sockaddr *)&sockaddr, &len);
if (fd < 0 && errno != EINTR) {
perror("accept");
return false;
return false;
}
- init_gdbserver_state();
- create_default_process(&gdbserver_state);
- gdbserver_state.processes[0].attached = true;
- gdbserver_state.c_cpu = gdb_first_attached_cpu();
- gdbserver_state.g_cpu = gdbserver_state.c_cpu;
- gdbserver_state.fd = fd;
- gdb_has_xml = false;
+ gdb_accept_init(fd);
return true;
}
-static int gdbserver_open(int port)
+static int gdbserver_open_port(int port)
{
struct sockaddr_in sockaddr;
int fd, ret;
close(fd);
return -1;
}
+
return fd;
}
-int gdbserver_start(int port)
+int gdbserver_start(const char *port_or_path)
{
- gdbserver_fd = gdbserver_open(port);
- if (gdbserver_fd < 0)
- return -1;
- /* accept connections */
- if (!gdb_accept()) {
- close(gdbserver_fd);
- gdbserver_fd = -1;
+ int port = g_ascii_strtoull(port_or_path, NULL, 10);
+ int gdb_fd;
+
+ if (port > 0) {
+ gdb_fd = gdbserver_open_port(port);
+ } else {
+ gdb_fd = gdbserver_open_socket(port_or_path);
+ }
+
+ if (gdb_fd < 0) {
return -1;
}
- return 0;
+
+ if (port > 0 && gdb_accept_tcp(gdb_fd)) {
+ return 0;
+ } else if (gdb_accept_socket(gdb_fd)) {
+ gdbserver_state.socket_path = g_strdup(port_or_path);
+ return 0;
+ }
+
+ /* gone wrong */
+ close(gdb_fd);
+ return -1;
}
/* Disable gdb stub for child processes. */
void gdbserver_fork(CPUState *cpu)
{
- if (gdbserver_fd < 0 || gdbserver_state.fd < 0) {
+ if (!gdbserver_state.init || gdbserver_state.fd < 0) {
return;
}
close(gdbserver_state.fd);
s->g_cpu = s->c_cpu;
vm_stop(RUN_STATE_PAUSED);
+ replay_gdb_attached();
gdb_has_xml = false;
break;
default:
if (strstart(device, "tcp:", NULL)) {
/* enforce required TCP attributes */
snprintf(gdbstub_device_name, sizeof(gdbstub_device_name),
- "%s,nowait,nodelay,server", device);
+ "%s,wait=off,nodelay=on,server=on", device);
device = gdbstub_device_name;
}
#ifndef _WIN32
return 0;
}
-void gdbserver_cleanup(void)
-{
- if (gdbserver_state.init) {
- put_packet("W00");
- }
-}
-
static void register_types(void)
{
type_register_static(&char_gdb_type_info);