diff --git a/.vscode/settings.json b/.vscode/settings.json index fbfe117..abb3706 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -56,6 +56,7 @@ "typeinfo": "cpp", "internals.h": "c", "stdint.h": "c", - "ctype.h": "c" + "ctype.h": "c", + "gdbstub.h": "c" } } \ No newline at end of file diff --git a/gdbstub/commands.h b/gdbstub/commands.h index 380f4ee..4a7eb87 100644 --- a/gdbstub/commands.h +++ b/gdbstub/commands.h @@ -101,10 +101,4 @@ void gdb_extend_set_table(GPtrArray *table); */ void gdb_extend_qsupported_features(char *qsupported_features); -/** - * Convert a hex string to bytes. Conversion is done per byte, so 2 hex digits - * are converted to 1 byte. Invalid hex digits are treated as 0 digits. - */ -void gdb_hextomem(GByteArray *mem, const char *buf, int len); - #endif /* GDBSTUB_COMMANDS_H */ diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 1230f6f..be26a05 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -23,27 +23,6 @@ * SPDX-License-Identifier: LGPL-2.0-or-later */ -// #include "qemu/osdep.h" -// #include "qemu/ctype.h" -// #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" -// #include "hw/cpu/cluster.h" -// #include "hw/boards.h" -// #include "hw/core/cpu.h" - -// #include "system/hw_accel.h" -// #include "system/runstate.h" -// #include "exec/replay-core.h" -// #include "exec/hwaddr.h" - -#include "gdbstub.h" - #include #include "commands.h" @@ -51,15 +30,6 @@ #include "enums.h" #include "internals.h" -// #include "trace.h" - -typedef struct GDBRegisterState { - int base_reg; - gdb_get_reg_cb get_reg; - gdb_set_reg_cb set_reg; - const GDBFeature *feature; -} GDBRegisterState; - GDBState gdbserver_state; void gdb_init_gdbserver_state(void) { @@ -101,43 +71,10 @@ void gdb_hextomem(GByteArray *mem, const char *buf, int len) { } } -static void hexdump(const char *buf, int len, void (*trace_fn)(size_t ofs, char const *text)) { - char line_buffer[3 * 16 + 4 + 16 + 1]; - - size_t i; - for (i = 0; i < len || (i & 0xF); ++i) { - size_t byte_ofs = i & 15; - - if (byte_ofs == 0) { - memset(line_buffer, ' ', 3 * 16 + 4 + 16); - line_buffer[3 * 16 + 4 + 16] = 0; - } - - size_t col_group = (i >> 2) & 3; - size_t hex_col = byte_ofs * 3 + col_group; - size_t txt_col = 3 * 16 + 4 + byte_ofs; - - if (i < len) { - char value = buf[i]; - - line_buffer[hex_col + 0] = tohex((value >> 4) & 0xF); - line_buffer[hex_col + 1] = tohex((value >> 0) & 0xF); - line_buffer[txt_col + 0] = (value >= ' ' && value < 127) ? value : '.'; - } - - // if (byte_ofs == 0xF) trace_fn(i & -16, line_buffer); - } -} - -/* return -1 if error, 0 if OK */ int gdb_put_packet_binary(const char *buf, int len, bool dump) { int csum, i; uint8_t footer[3]; - // if (dump && trace_event_get_state_backends(TRACE_GDBSTUB_IO_BINARYREPLY)) { - // hexdump(buf, len, trace_gdbstub_io_binaryreply); - // } - for (;;) { g_byte_array_set_size(gdbserver_state.last_packet, 0); g_byte_array_append(gdbserver_state.last_packet, (const uint8_t *)"$", 1); @@ -160,12 +97,7 @@ int gdb_put_packet_binary(const char *buf, int len, bool dump) { return 0; } -/* return -1 if error, 0 if OK */ -int gdb_put_packet(const char *buf) { - // trace_gdbstub_io_reply(buf); - - return gdb_put_packet_binary(buf, strlen(buf), false); -} +int gdb_put_packet(const char *buf) { return gdb_put_packet_binary(buf, strlen(buf), false); } void gdb_put_strbuf(void) { gdb_put_packet(gdbserver_state.str_buf->str); } @@ -190,15 +122,6 @@ void gdb_memtox(GString *buf, const char *mem, int len) { } } -static uint32_t gdb_get_cpu_pid(CPUState *cpu) { - if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) { - /* Return the default process' PID */ - int index = gdbserver_state.process_num - 1; - return gdbserver_state.processes[index].pid; - } - return cpu->cluster_index + 1; -} - GDBProcess *gdb_get_process(uint32_t pid) { int i; @@ -216,181 +139,6 @@ GDBProcess *gdb_get_process(uint32_t pid) { return NULL; } -static GDBProcess *gdb_get_cpu_process(CPUState *cpu) { return gdb_get_process(gdb_get_cpu_pid(cpu)); } - -static CPUState *find_cpu(uint32_t thread_id) { - CPUState *cpu; - - CPU_FOREACH(cpu) { - if (gdb_get_cpu_index(c) == thread_id) { - return cpu; - } - } - - return NULL; -} - -CPUState *gdb_get_first_cpu_in_process(GDBProcess *process) { - CPUState *cpu; - - CPU_FOREACH(cpu) { - if (gdb_get_cpu_pid(c) == process->pid) { - return c; - } - } - - return NULL; -} - -static CPUState *gdb_next_cpu_in_process(CPUState *cpu) { - uint32_t pid = gdb_get_cpu_pid(cpu); - cpu = cpu_next(cpu); - - while (cpu) { - if (gdb_get_cpu_pid(cpu) == pid) { - break; - } - - cpu = cpu_next(cpu); - } - - return cpu; -} - -/* Return the cpu following @cpu, while ignoring unattached processes. */ -static CPUState *gdb_next_attached_cpu(CPUState *cpu) { - cpu = cpu_next(cpu); - - while (cpu) { - if (gdb_get_cpu_process(cpu)->attached) { - break; - } - - cpu = cpu_next(cpu); - } - - return cpu; -} - -/* Return the first attached cpu */ -CPUState *gdb_first_attached_cpu(void) { - CPUState *cpu = get_cpu(); - GDBProcess *process = gdb_get_cpu_process(cpu); - - if (!process->attached) { - return gdb_next_attached_cpu(cpu); - } - - return cpu; -} - -static CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid) { - GDBProcess *process; - CPUState *cpu; - - if (!pid && !tid) { - /* 0 means any process/thread, we take the first attached one */ - return gdb_first_attached_cpu(); - } else if (pid && !tid) { - /* any thread in a specific process */ - process = gdb_get_process(pid); - - if (process == NULL) { - return NULL; - } - - if (!process->attached) { - return NULL; - } - - return gdb_get_first_cpu_in_process(process); - } else { - /* a specific thread */ - cpu = find_cpu(tid); - - if (cpu == NULL) { - return NULL; - } - - process = gdb_get_cpu_process(cpu); - - if (pid && process->pid != pid) { - return NULL; - } - - if (!process->attached) { - return NULL; - } - - return cpu; - } -} - -static const char *get_feature_xml(const char *p, const char **newp, GDBProcess *process) { - CPUState *cpu = gdb_get_first_cpu_in_process(process); - GDBRegisterState *r; - size_t len; - - /* - * qXfer:features:read:ANNEX:OFFSET,LENGTH' - * ^p ^newp - */ - const char *term = strchr(p, ':'); - *newp = term + 1; - len = term - p; - - /* Is it the main target xml? */ - if (strncmp(p, "target.xml", len) == 0) { - if (!process->target_xml) { - g_autoptr(GPtrArray) xml = g_ptr_array_new_with_free_func(g_free); - - g_ptr_array_add(xml, g_strdup("" - "" - "")); - - if (cpu->cc->gdb_arch_name) { - g_ptr_array_add(xml, g_markup_printf_escaped("%s", 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); - g_ptr_array_add(xml, g_markup_printf_escaped("", r->feature->xmlname)); - } - g_ptr_array_add(xml, g_strdup("")); - g_ptr_array_add(xml, NULL); - - process->target_xml = g_strjoinv(NULL, (gchar **)xml->pdata); - } - return process->target_xml; - } - /* Is it one of the features? */ - for (guint i = 0; i < cpu->gdb_regs->len; i++) { - r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); - if (strncmp(p, r->feature->xmlname, len) == 0) { - return r->feature->xml; - } - } - - /* failed */ - return NULL; -} - -void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature, const char *name, const char *xmlname, - int base_reg) { - char *header = g_markup_printf_escaped( - "" - "" - "", - name); - - builder->feature = feature; - builder->xml = g_ptr_array_new(); - g_ptr_array_add(builder->xml, header); - builder->regs = g_ptr_array_new(); - builder->base_reg = base_reg; - feature->xmlname = xmlname; - feature->name = name; -} - void gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder, const char *format, ...) { va_list ap; va_start(ap, format); @@ -642,11 +390,6 @@ static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, uin return GDB_ONE_THREAD; } -/** - * gdb_handle_vcont - Parses and handles a vCont packet. - * returns -ENOTSUP if a command is unsupported, -EINVAL or -ERANGE if there is - * a format error, 0 on success. - */ static int gdb_handle_vcont(const char *p) { int res, signal = 0; char cur_action; @@ -1085,17 +828,6 @@ static void handle_remove_bp(GArray *params, void *user_ctx) { gdb_put_packet("E22"); } -/* - * handle_set/get_reg - * - * Older gdb are really dumb, and don't use 'G/g' if 'P/p' is available. - * This works, but can be very slow. Anything new enough to understand - * XML also knows how to use this properly. However to use this we - * need to define a local XML file as well as be talking to a - * reasonably modern gdb. Responding with an empty packet will cause - * the remote gdb to fallback to older methods. - */ - static void handle_set_reg(GArray *params, void *user_ctx) { int reg_size; @@ -1230,18 +962,10 @@ static void handle_backward(GArray *params, void *user_ctx) { if (params->len == 1) { switch (gdb_get_cmd_param(params, 0)->opcode) { case 's': - // if (replay_reverse_step()) { - // gdb_continue(); - // } else { gdb_put_packet("E14"); - // } return; case 'c': - // if (replay_reverse_continue()) { - // gdb_continue(); - // } else { gdb_put_packet("E14"); - // } return; } } diff --git a/gdbstub/gdbstub.h b/gdbstub/gdbstub.h deleted file mode 100644 index ce79b0b..0000000 --- a/gdbstub/gdbstub.h +++ /dev/null @@ -1,157 +0,0 @@ -#ifndef GDBSTUB_H -#define GDBSTUB_H - -#include "cpu.h" - -typedef struct GDBFeature { - const char *xmlname; - const char *xml; - const char *name; - const char *const *regs; - int num_regs; -} GDBFeature; - -typedef struct GDBFeatureBuilder { - GDBFeature *feature; - GPtrArray *xml; - GPtrArray *regs; - int base_reg; -} GDBFeatureBuilder; - -/* Get or set a register. Returns the size of the register. */ -typedef int (*gdb_get_reg_cb)(CPUState *cpu, GByteArray *buf, int reg); -typedef int (*gdb_set_reg_cb)(CPUState *cpu, uint8_t *buf, int reg); - -/** - * gdb_init_cpu(): Initialize the CPU for gdbstub. - * @cpu: The CPU to be initialized. - */ -void gdb_init_cpu(CPUState *cpu); - -/** - * gdb_register_coprocessor() - register a supplemental set of registers - * @cpu - the CPU associated with registers - * @get_reg - get function (gdb reading) - * @set_reg - set function (gdb modifying) - * @num_regs - number of registers in set - * @xml - xml name of set - * @gpos - non-zero to append to "general" register set at @gpos - */ -void gdb_register_coprocessor(CPUState *cpu, gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, const GDBFeature *feature, - int g_pos); - -/** - * gdb_unregister_coprocessor_all() - unregisters supplemental set of registers - * @cpu - the CPU associated with registers - */ -void gdb_unregister_coprocessor_all(CPUState *cpu); - -/** - * gdbserver_start: start the gdb server - * @port_or_device: connection spec for gdb - * @errp: error handle - * - * For CONFIG_USER this is either a tcp port or a path to a fifo. For - * system emulation you can use a full chardev spec for your gdbserver - * port. - * - * The error handle should be either &error_fatal (for start-up) or - * &error_warn (for QMP/HMP initiated sessions). - * - * Returns true when server successfully started. - */ -bool gdbserver_start(const char *port_or_device, Error **errp); - -/** - * gdb_feature_builder_init() - Initialize GDBFeatureBuilder. - * @builder: The builder to be initialized. - * @feature: The feature to be filled. - * @name: The name of the feature. - * @xmlname: The name of the XML. - * @base_reg: The base number of the register ID. - */ -void gdb_feature_builder_init(GDBFeatureBuilder *builder, GDBFeature *feature, const char *name, const char *xmlname, - int base_reg); - -/** - * gdb_feature_builder_append_tag() - Append a tag. - * @builder: The builder. - * @format: The format of the tag. - * @...: The values to be formatted. - */ -void G_GNUC_PRINTF(2, 3) gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder, const char *format, ...); - -/** - * gdb_feature_builder_append_reg() - Append a register. - * @builder: The builder. - * @name: The register's name; it must be unique within a CPU. - * @bitsize: The register's size, in bits. - * @regnum: The offset of the register's number in the feature. - * @type: The type of the register. - * @group: The register group to which this register belongs; it can be NULL. - */ -void gdb_feature_builder_append_reg(const GDBFeatureBuilder *builder, const char *name, int bitsize, int regnum, - const char *type, const char *group); - -/** - * gdb_feature_builder_end() - End building GDBFeature. - * @builder: The builder. - */ -void gdb_feature_builder_end(const GDBFeatureBuilder *builder); - -/** - * gdb_find_static_feature() - Find a static feature. - * @xmlname: The name of the XML. - * - * Return: The static feature. - */ -const GDBFeature *gdb_find_static_feature(const char *xmlname); - -/** - * gdb_read_register() - Read a register associated with a CPU. - * @cpu: The CPU associated with the register. - * @buf: The buffer that the read register will be appended to. - * @reg: The register's number returned by gdb_find_feature_register(). - * - * Return: The number of read bytes. - */ -int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); - -/** - * gdb_write_register() - Write a register associated with a CPU. - * @cpu: The CPU associated with the register. - * @buf: The buffer that the register contents will be set to. - * @reg: The register's number returned by gdb_find_feature_register(). - * - * The size of @buf must be at least the size of the register being - * written. - * - * Return: The number of written bytes, or 0 if an error occurred (for - * example, an unknown register was provided). - */ -int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg); - -/** - * typedef GDBRegDesc - a register description from gdbstub - */ -typedef struct { - int gdb_reg; - const char *name; - const char *feature_name; -} GDBRegDesc; - -/** - * gdb_get_register_list() - Return list of all registers for CPU - * @cpu: The CPU being searched - * - * Returns a GArray of GDBRegDesc, caller frees array but not the - * const strings. - */ -GArray *gdb_get_register_list(CPUState *cpu); - -void gdb_set_stop_cpu(CPUState *cpu); - -/* in gdbstub-xml.c, generated by scripts/feature_to_c.py */ -extern const GDBFeature gdb_static_features[]; - -#endif /* GDBSTUB_H */ diff --git a/gdbstub/internals.h b/gdbstub/internals.h index 75d9631..670242d 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -9,35 +9,10 @@ #ifndef GDBSTUB_INTERNALS_H #define GDBSTUB_INTERNALS_H -// #include "exec/cpu-common.h" #include "cpu.h" -/* - * Most "large" transfers (e.g. memory reads, feature XML - * transfer) have mechanisms in the gdb protocol for splitting - * them. However, register values in particular cannot currently - * be split. This packet size must therefore be at least big enough - * for the worst-case register size. Currently that is Arm SME - * ZA storage with a 256x256 byte value. We also must account - * for the conversion from raw data to hex in gdb_memtohex(), - * which writes 2 * size bytes, and for other protocol overhead - * including command, register number and checksum which add - * another 4 bytes of overhead. However, to be consistent with - * the changes made in gdbserver to address this same requirement, - * we add a total of 32 bytes to account for protocol overhead - * (unclear why specifically 32 bytes), bringing the value of - * MAX_PACKET_LENGTH to 2 * 256 * 256 + 32 = 131104. - * - * The commit making this change for gdbserver can be found here: - * https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h= - * b816042e88583f280ad186ff124ab84d31fb592b - */ #define MAX_PACKET_LENGTH 131104 -/* - * Shared structures and definitions - */ - enum { GDB_SIGNAL_0 = 0, GDB_SIGNAL_INT = 2, @@ -67,6 +42,32 @@ enum RSState { RS_CHKSUM2, }; +typedef struct GDBFeature { + const char *xmlname; + const char *xml; + const char *name; + const char *const *regs; + int num_regs; +} GDBFeature; + +typedef struct GDBFeatureBuilder { + GDBFeature *feature; + GPtrArray *xml; + GPtrArray *regs; + int base_reg; +} GDBFeatureBuilder; + +/* Get or set a register. Returns the size of the register. */ +typedef int (*gdb_get_reg_cb)(CPUState *cpu, GByteArray *buf, int reg); +typedef int (*gdb_set_reg_cb)(CPUState *cpu, uint8_t *buf, int reg); + +typedef struct GDBRegisterState { + int base_reg; + gdb_get_reg_cb get_reg; + gdb_set_reg_cb set_reg; + const GDBFeature *feature; +} GDBRegisterState; + typedef struct GDBState { bool init; /* have we been initialised? */ CPUState *c_cpu; /* current CPU for step/continue ops */ @@ -125,11 +126,120 @@ static inline int tohex(int v) { */ void gdb_put_strbuf(void); -int gdb_put_packet_binary(const char *buf, int len, bool dump); -void gdb_memtohex(GString *buf, const uint8_t *mem, int len); -void gdb_memtox(GString *buf, const char *mem, int len); +void gdb_hextomem(GByteArray *mem, const char *buf, int len); void gdb_read_byte(uint8_t ch); +/** + * gdb_init_cpu(): Initialize the CPU for gdbstub. + * @cpu: The CPU to be initialized. + */ +void gdb_init_cpu(CPUState *cpu); + +/** + * gdb_register_coprocessor() - register a supplemental set of registers + * @cpu - the CPU associated with registers + * @get_reg - get function (gdb reading) + * @set_reg - set function (gdb modifying) + * @num_regs - number of registers in set + * @xml - xml name of set + * @gpos - non-zero to append to "general" register set at @gpos + */ +void gdb_register_coprocessor(CPUState *cpu, gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg, const GDBFeature *feature, + int g_pos); + +/** + * gdb_unregister_coprocessor_all() - unregisters supplemental set of registers + * @cpu - the CPU associated with registers + */ +void gdb_unregister_coprocessor_all(CPUState *cpu); + +/** + * gdbserver_start: start the gdb server + * @port_or_device: connection spec for gdb + * @errp: error handle + * + * For CONFIG_USER this is either a tcp port or a path to a fifo. For + * system emulation you can use a full chardev spec for your gdbserver + * port. + * + * The error handle should be either &error_fatal (for start-up) or + * &error_warn (for QMP/HMP initiated sessions). + * + * Returns true when server successfully started. + */ +bool gdbserver_start(const char *port_or_device, Error **errp); + +/** + * gdb_feature_builder_append_tag() - Append a tag. + * @builder: The builder. + * @format: The format of the tag. + * @...: The values to be formatted. + */ +void G_GNUC_PRINTF(2, 3) gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder, const char *format, ...); + +/** + * gdb_feature_builder_append_reg() - Append a register. + * @builder: The builder. + * @name: The register's name; it must be unique within a CPU. + * @bitsize: The register's size, in bits. + * @regnum: The offset of the register's number in the feature. + * @type: The type of the register. + * @group: The register group to which this register belongs; it can be NULL. + */ +void gdb_feature_builder_append_reg(const GDBFeatureBuilder *builder, const char *name, int bitsize, int regnum, + const char *type, const char *group); + +void gdb_feature_builder_end(const GDBFeatureBuilder *builder); + +const GDBFeature *gdb_find_static_feature(const char *xmlname); + +/** + * gdb_read_register() - Read a register associated with a CPU. + * @cpu: The CPU associated with the register. + * @buf: The buffer that the read register will be appended to. + * @reg: The register's number returned by gdb_find_feature_register(). + * + * Return: The number of read bytes. + */ +int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); + +/** + * gdb_write_register() - Write a register associated with a CPU. + * @cpu: The CPU associated with the register. + * @buf: The buffer that the register contents will be set to. + * @reg: The register's number returned by gdb_find_feature_register(). + * + * The size of @buf must be at least the size of the register being + * written. + * + * Return: The number of written bytes, or 0 if an error occurred (for + * example, an unknown register was provided). + */ +int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg); + +/** + * typedef GDBRegDesc - a register description from gdbstub + */ +typedef struct { + int gdb_reg; + const char *name; + const char *feature_name; +} GDBRegDesc; + +/** + * gdb_get_register_list() - Return list of all registers for CPU + * @cpu: The CPU being searched + * + * Returns a GArray of GDBRegDesc, caller frees array but not the + * const strings. + */ +GArray *gdb_get_register_list(CPUState *cpu); + +void gdb_set_stop_cpu(CPUState *cpu); + +/* in gdbstub-xml.c, generated by scripts/feature_to_c.py */ +extern const GDBFeature gdb_static_features[]; + /* * Packet acknowledgement - we handle this slightly differently * between user and system mode, mainly to deal with the differences @@ -163,33 +273,13 @@ int gdb_signal_to_target(int sig); int gdb_get_char(void); /* user only */ -/** - * gdb_continue() - handle continue in mode specific way. - */ void gdb_continue(void); -/** - * gdb_continue_partial() - handle partial continue in mode specific way. - */ int gdb_continue_partial(char *newstates); -/* - * Command handlers - either specialised or system or user only - */ void gdb_init_gdbserver_state(void); void gdb_handle_query_rcmd(GArray *params, void *ctx); /* system */ -// void gdb_handle_query_xfer_auxv(GArray *params, void *user_ctx); /*user */ -// void gdb_handle_query_xfer_siginfo(GArray *params, void *user_ctx); /*user */ -// void gdb_handle_v_file_open(GArray *params, void *user_ctx); /* user */ -// void gdb_handle_v_file_close(GArray *params, void *user_ctx); /* user */ -// void gdb_handle_v_file_pread(GArray *params, void *user_ctx); /* user */ -// void gdb_handle_v_file_readlink(GArray *params, void *user_ctx); /* user */ -// void gdb_handle_query_xfer_exec_file(GArray *params, void *user_ctx); /* user */ -// void gdb_handle_set_catch_syscalls(GArray *params, void *user_ctx); /* user */ -// void gdb_handle_query_supported_user(const char *gdb_supported); /* user */ -// bool gdb_handle_set_thread_user(uint32_t pid, uint32_t tid); /* user */ -// bool gdb_handle_detach_user(uint32_t pid); /* user */ void gdb_handle_query_attached(GArray *params, void *ctx); /* both */ @@ -232,6 +322,8 @@ void vm_start(); bool vm_prepare_start(bool step_requested); void qemu_clock_enable(); +uint32_t gdb_get_cpu_pid(CPUState *cpu); + #define CPU_FOREACH(cpu) for (auto c = get_cpu(); false;) /** @@ -248,4 +340,13 @@ void qemu_clock_enable(); */ int gdb_target_memory_rw_debug(CPUState *cs, hwaddr addr, uint8_t *buf, int len, bool is_write); +GDBProcess *gdb_get_cpu_process(CPUState *cpu); +CPUState *find_cpu(uint32_t thread_id); +CPUState *gdb_get_first_cpu_in_process(GDBProcess *process); +CPUState *gdb_next_cpu_in_process(CPUState *cpu); +CPUState *gdb_next_attached_cpu(CPUState *cpu); +CPUState *gdb_first_attached_cpu(void); +CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid); +const char *get_feature_xml(const char *p, const char **newp, GDBProcess *process); + #endif /* GDBSTUB_INTERNALS_H */ diff --git a/gdbstub/riscv64-softmmu-gdbstub-xml.c b/gdbstub/riscv64-softmmu-gdbstub-xml.c index 06cc163..cd90274 100644 --- a/gdbstub/riscv64-softmmu-gdbstub-xml.c +++ b/gdbstub/riscv64-softmmu-gdbstub-xml.c @@ -1,4 +1,4 @@ -#include "gdbstub.h" +#include "internals.h" const GDBFeature gdb_static_features[] = { { @@ -47,73 +47,12 @@ const GDBFeature gdb_static_features[] = { " \n" "\n", "org.gnu.gdb.riscv.cpu", - (const char * const []) { - [0] = - "zero", - [1] = - "ra", - [2] = - "sp", - [3] = - "gp", - [4] = - "tp", - [5] = - "t0", - [6] = - "t1", - [7] = - "t2", - [8] = - "fp", - [9] = - "s1", - [10] = - "a0", - [11] = - "a1", - [12] = - "a2", - [13] = - "a3", - [14] = - "a4", - [15] = - "a5", - [16] = - "a6", - [17] = - "a7", - [18] = - "s2", - [19] = - "s3", - [20] = - "s4", - [21] = - "s5", - [22] = - "s6", - [23] = - "s7", - [24] = - "s8", - [25] = - "s9", - [26] = - "s10", - [27] = - "s11", - [28] = - "t3", - [29] = - "t4", - [30] = - "t5", - [31] = - "t6", - [32] = - "pc", + (const char* const[]){ + [0] = "zero", [1] = "ra", [2] = "sp", [3] = "gp", [4] = "tp", [5] = "t0", [6] = "t1", + [7] = "t2", [8] = "fp", [9] = "s1", [10] = "a0", [11] = "a1", [12] = "a2", [13] = "a3", + [14] = "a4", [15] = "a5", [16] = "a6", [17] = "a7", [18] = "s2", [19] = "s3", [20] = "s4", + [21] = "s5", [22] = "s6", [23] = "s7", [24] = "s8", [25] = "s9", [26] = "s10", [27] = "s11", + [28] = "t3", [29] = "t4", [30] = "t5", [31] = "t6", [32] = "pc", }, 33, }, @@ -162,71 +101,12 @@ const GDBFeature gdb_static_features[] = { " \n" "\n", "org.gnu.gdb.riscv.fpu", - (const char * const []) { - [0] = - "ft0", - [1] = - "ft1", - [2] = - "ft2", - [3] = - "ft3", - [4] = - "ft4", - [5] = - "ft5", - [6] = - "ft6", - [7] = - "ft7", - [8] = - "fs0", - [9] = - "fs1", - [10] = - "fa0", - [11] = - "fa1", - [12] = - "fa2", - [13] = - "fa3", - [14] = - "fa4", - [15] = - "fa5", - [16] = - "fa6", - [17] = - "fa7", - [18] = - "fs2", - [19] = - "fs3", - [20] = - "fs4", - [21] = - "fs5", - [22] = - "fs6", - [23] = - "fs7", - [24] = - "fs8", - [25] = - "fs9", - [26] = - "fs10", - [27] = - "fs11", - [28] = - "ft8", - [29] = - "ft9", - [30] = - "ft10", - [31] = - "ft11", + (const char* const[]){ + [0] = "ft0", [1] = "ft1", [2] = "ft2", [3] = "ft3", [4] = "ft4", [5] = "ft5", [6] = "ft6", + [7] = "ft7", [8] = "fs0", [9] = "fs1", [10] = "fa0", [11] = "fa1", [12] = "fa2", [13] = "fa3", + [14] = "fa4", [15] = "fa5", [16] = "fa6", [17] = "fa7", [18] = "fs2", [19] = "fs3", [20] = "fs4", + [21] = "fs5", [22] = "fs6", [23] = "fs7", [24] = "fs8", [25] = "fs9", [26] = "fs10", [27] = "fs11", + [28] = "ft8", [29] = "ft9", [30] = "ft10", [31] = "ft11", }, 32, }, @@ -281,71 +161,12 @@ const GDBFeature gdb_static_features[] = { " \n" "\n", "org.gnu.gdb.riscv.fpu", - (const char * const []) { - [0] = - "ft0", - [1] = - "ft1", - [2] = - "ft2", - [3] = - "ft3", - [4] = - "ft4", - [5] = - "ft5", - [6] = - "ft6", - [7] = - "ft7", - [8] = - "fs0", - [9] = - "fs1", - [10] = - "fa0", - [11] = - "fa1", - [12] = - "fa2", - [13] = - "fa3", - [14] = - "fa4", - [15] = - "fa5", - [16] = - "fa6", - [17] = - "fa7", - [18] = - "fs2", - [19] = - "fs3", - [20] = - "fs4", - [21] = - "fs5", - [22] = - "fs6", - [23] = - "fs7", - [24] = - "fs8", - [25] = - "fs9", - [26] = - "fs10", - [27] = - "fs11", - [28] = - "ft8", - [29] = - "ft9", - [30] = - "ft10", - [31] = - "ft11", + (const char* const[]){ + [0] = "ft0", [1] = "ft1", [2] = "ft2", [3] = "ft3", [4] = "ft4", [5] = "ft5", [6] = "ft6", + [7] = "ft7", [8] = "fs0", [9] = "fs1", [10] = "fa0", [11] = "fa1", [12] = "fa2", [13] = "fa3", + [14] = "fa4", [15] = "fa5", [16] = "fa6", [17] = "fa7", [18] = "fs2", [19] = "fs3", [20] = "fs4", + [21] = "fs5", [22] = "fs6", [23] = "fs7", [24] = "fs8", [25] = "fs9", [26] = "fs10", [27] = "fs11", + [28] = "ft8", [29] = "ft9", [30] = "ft10", [31] = "ft11", }, 32, }, @@ -363,9 +184,8 @@ const GDBFeature gdb_static_features[] = { " \n" "\n", "org.gnu.gdb.riscv.virtual", - (const char * const []) { - [0] = - "priv", + (const char* const[]){ + [0] = "priv", }, 1, }, @@ -415,73 +235,12 @@ const GDBFeature gdb_static_features[] = { " \n" "\n", "org.gnu.gdb.riscv.cpu", - (const char * const []) { - [0] = - "zero", - [1] = - "ra", - [2] = - "sp", - [3] = - "gp", - [4] = - "tp", - [5] = - "t0", - [6] = - "t1", - [7] = - "t2", - [8] = - "fp", - [9] = - "s1", - [10] = - "a0", - [11] = - "a1", - [12] = - "a2", - [13] = - "a3", - [14] = - "a4", - [15] = - "a5", - [16] = - "a6", - [17] = - "a7", - [18] = - "s2", - [19] = - "s3", - [20] = - "s4", - [21] = - "s5", - [22] = - "s6", - [23] = - "s7", - [24] = - "s8", - [25] = - "s9", - [26] = - "s10", - [27] = - "s11", - [28] = - "t3", - [29] = - "t4", - [30] = - "t5", - [31] = - "t6", - [32] = - "pc", + (const char* const[]){ + [0] = "zero", [1] = "ra", [2] = "sp", [3] = "gp", [4] = "tp", [5] = "t0", [6] = "t1", + [7] = "t2", [8] = "fp", [9] = "s1", [10] = "a0", [11] = "a1", [12] = "a2", [13] = "a3", + [14] = "a4", [15] = "a5", [16] = "a6", [17] = "a7", [18] = "s2", [19] = "s3", [20] = "s4", + [21] = "s5", [22] = "s6", [23] = "s7", [24] = "s8", [25] = "s9", [26] = "s10", [27] = "s11", + [28] = "t3", [29] = "t4", [30] = "t5", [31] = "t6", [32] = "pc", }, 33, }, @@ -499,11 +258,9 @@ const GDBFeature gdb_static_features[] = { " \n" "\n", "org.gnu.gdb.riscv.virtual", - (const char * const []) { - [0] = - "priv", + (const char* const[]){ + [0] = "priv", }, 1, }, - { NULL } -}; + {NULL}}; diff --git a/gdbstub/stub.cpp b/gdbstub/stub.cpp index f67bc8b..ebf8bb9 100644 --- a/gdbstub/stub.cpp +++ b/gdbstub/stub.cpp @@ -2,7 +2,7 @@ #include #include "conn.h" -#include "gdbstub.h" +#include "internals.h" int main(int argc, char *argv[]) { conn_t conn; diff --git a/gdbstub/system.c b/gdbstub/system.c index ab5e787..aecba3d 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -35,170 +35,6 @@ int gdb_get_cpu_index(CPUState *cpu) { return 0; } */ bool gdb_got_immediate_ack(void) { return true; } -/* - * GDB Connection management. For system emulation we do all of this - * via our existing Chardev infrastructure which allows us to support - * network and unix sockets. - */ - -// static void gdb_chr_event(void *opaque, QEMUChrEvent event) { -// int i; -// GDBState *s = (GDBState *)opaque; - -// switch (event) { -// case CHR_EVENT_OPENED: -// /* Start with first process attached, others detached */ -// for (i = 0; i < s->process_num; i++) { -// s->processes[i].attached = !i; -// } - -// s->c_cpu = gdb_first_attached_cpu(); -// s->g_cpu = s->c_cpu; - -// vm_stop(RUN_STATE_PAUSED); -// replay_gdb_attached(); -// break; -// default: -// break; -// } -// } - -// /* -// * In system-mode we stop the VM and wait to send the syscall packet -// * until notification that the CPU has stopped. This must be done -// * because if the packet is sent now the reply from the syscall -// * request could be received while the CPU is still in the running -// * state, which can cause packets to be dropped and state transition -// * 'T' packets to be sent while the syscall is still being processed. -// */ -// void gdb_syscall_handling(const char *syscall_packet) { -// vm_stop(RUN_STATE_DEBUG); -// qemu_cpu_kick(gdbserver_state.c_cpu); -// } - -// 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); -// g_autoptr(GString) tid = g_string_new(NULL); -// const char *type; -// int ret; - -// if (running || gdbserver_state.state == RS_INACTIVE) { -// return; -// } - -// /* Is there a GDB syscall waiting to be sent? */ -// if (gdb_handled_syscall()) { -// return; -// } - -// if (cpu == NULL) { -// /* No process attached */ -// return; -// } - -// if (!gdbserver_state.allow_stop_reply) { -// return; -// } - -// gdb_append_thread_id(cpu, tid); - -// switch (state) { -// case RUN_STATE_DEBUG: -// if (cpu->watchpoint_hit) { -// switch (cpu->watchpoint_hit->flags & BP_MEM_ACCESS) { -// case BP_MEM_READ: -// type = "r"; -// break; -// case BP_MEM_ACCESS: -// type = "a"; -// break; -// default: -// type = ""; -// break; -// } -// trace_gdbstub_hit_watchpoint(type, gdb_get_cpu_index(cpu), cpu->watchpoint_hit->vaddr); -// g_string_printf(buf, "T%02xthread:%s;%swatch:%" VADDR_PRIx ";", GDB_SIGNAL_TRAP, tid->str, type, -// cpu->watchpoint_hit->vaddr); -// cpu->watchpoint_hit = NULL; -// goto send_packet; -// } else { -// trace_gdbstub_hit_break(); -// } -// if (tcg_enabled()) { -// tb_flush(cpu); -// } -// ret = GDB_SIGNAL_TRAP; -// break; -// case RUN_STATE_PAUSED: -// trace_gdbstub_hit_paused(); -// ret = GDB_SIGNAL_INT; -// break; -// case RUN_STATE_SHUTDOWN: -// trace_gdbstub_hit_shutdown(); -// ret = GDB_SIGNAL_QUIT; -// break; -// case RUN_STATE_IO_ERROR: -// trace_gdbstub_hit_io_error(); -// ret = GDB_SIGNAL_STOP; -// break; -// case RUN_STATE_WATCHDOG: -// trace_gdbstub_hit_watchdog(); -// ret = GDB_SIGNAL_ALRM; -// break; -// case RUN_STATE_INTERNAL_ERROR: -// trace_gdbstub_hit_internal_error(); -// ret = GDB_SIGNAL_ABRT; -// break; -// case RUN_STATE_SAVE_VM: -// case RUN_STATE_RESTORE_VM: -// return; -// case RUN_STATE_FINISH_MIGRATE: -// ret = GDB_SIGNAL_XCPU; -// break; -// default: -// trace_gdbstub_hit_unknown(state); -// ret = GDB_SIGNAL_UNKNOWN; -// break; -// } -// gdb_set_stop_cpu(cpu); -// g_string_printf(buf, "T%02xthread:%s;", ret, tid->str); - -// send_packet: -// gdb_put_packet(buf->str); -// gdbserver_state.allow_stop_reply = false; - -// /* disable single step if it was enabled */ -// cpu_single_step(cpu, 0); -// } - -// static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len) { -// g_autoptr(GString) hex_buf = g_string_new("O"); -// gdb_memtohex(hex_buf, buf, len); -// gdb_put_packet(hex_buf->str); -// return len; -// } - -// static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend, bool *be_opened, Error **errp) { -// *be_opened = false; -// } - -// static void char_gdb_class_init(ObjectClass *oc, const void *data) { -// ChardevClass *cc = CHARDEV_CLASS(oc); - -// cc->internal = true; -// cc->open = gdb_monitor_open; -// cc->chr_write = gdb_monitor_write; -// } - -// #define TYPE_CHARDEV_GDB "chardev-gdb" - -// static const TypeInfo char_gdb_type_info = { -// .name = TYPE_CHARDEV_GDB, -// .parent = TYPE_CHARDEV, -// .class_init = char_gdb_class_init, -// }; - static int gdb_chr_can_receive(void *opaque) { /* * We can handle an arbitrarily large amount of data. @@ -215,32 +51,6 @@ static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) { } } -// static int find_cpu_clusters(Object *child, void *opaque) { -// if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) { -// GDBState *s = (GDBState *)opaque; -// CPUClusterState *cluster = CPU_CLUSTER(child); -// GDBProcess *process; - -// s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); - -// process = &s->processes[s->process_num - 1]; - -// /* -// * GDB process IDs -1 and 0 are reserved. To avoid subtle errors at -// * runtime, we enforce here that the machine does not use a cluster ID -// * that would lead to PID 0. -// */ -// assert(cluster->cluster_id != UINT32_MAX); -// process->pid = cluster->cluster_id + 1; -// process->attached = false; -// process->target_xml = NULL; - -// return 0; -// } - -// return object_child_foreach(child, find_cpu_clusters, opaque); -// } - static int pid_order(const void *a, const void *b) { GDBProcess *pa = (GDBProcess *)a; GDBProcess *pb = (GDBProcess *)b; @@ -254,116 +64,6 @@ static int pid_order(const void *a, const void *b) { } } -// static void create_processes(GDBState *s) { -// object_child_foreach(object_get_root(), find_cpu_clusters, s); - -// if (gdbserver_state.processes) { -// /* Sort by PID */ -// qsort(gdbserver_state.processes, gdbserver_state.process_num, sizeof(gdbserver_state.processes[0]), pid_order); -// } - -// gdb_create_default_process(s); -// } - -// bool gdbserver_start(const char *device, Error **errp) { -// Chardev *chr = NULL; -// Chardev *mon_chr; -// g_autoptr(GString) cs = g_string_new(device); - -// if (!get_cpu()) { -// error_setg(errp, -// "gdbstub: meaningless to attach gdb to a " -// "machine without any CPU."); -// return false; -// } - -// if (!gdb_supports_guest_debug()) { -// error_setg(errp, -// "gdbstub: current accelerator doesn't " -// "support guest debugging"); -// return false; -// } - -// if (cs->len == 0) { -// error_setg(errp, "gdbstub: missing connection string"); -// return false; -// } - -// trace_gdbstub_op_start(cs->str); - -// if (g_strcmp0(cs->str, "none") != 0) { -// if (g_str_has_prefix(cs->str, "tcp:")) { -// /* enforce required TCP attributes */ -// g_string_append_printf(cs, ",wait=off,nodelay=on,server=on"); -// } -// /* -// * FIXME: it's a bit weird to allow using a mux chardev here -// * and implicitly setup a monitor. We may want to break this. -// */ -// chr = qemu_chr_new_noreplay("gdb", cs->str, true, NULL); -// if (!chr) { -// error_setg(errp, "gdbstub: couldn't create chardev"); -// return false; -// } -// } - -// if (!gdbserver_state.init) { -// gdb_init_gdbserver_state(); - -// qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); - -// /* Initialize a monitor terminal for gdb */ -// mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB, NULL, NULL, &error_abort); -// monitor_init_hmp(mon_chr, false, &error_abort); -// } else { -// qemu_chr_fe_deinit(&gdbserver_system_state.chr, true); -// mon_chr = gdbserver_system_state.mon_chr; -// reset_gdbserver_state(); -// } - -// create_processes(&gdbserver_state); - -// if (chr) { -// qemu_chr_fe_init(&gdbserver_system_state.chr, chr, &error_abort); -// qemu_chr_fe_set_handlers(&gdbserver_system_state.chr, gdb_chr_can_receive, gdb_chr_receive, gdb_chr_event, NULL, -// &gdbserver_state, NULL, true); -// } -// gdbserver_state.state = chr ? RS_IDLE : RS_INACTIVE; -// gdbserver_system_state.mon_chr = mon_chr; -// gdb_syscall_reset(); - -// return true; -// } - -// static void register_types(void) { type_register_static(&char_gdb_type_info); } - -// type_init(register_types); - -/* Tell the remote gdb that the process has exited. */ -// void gdb_exit(int code) { -// char buf[4]; - -// if (!gdbserver_state.init) { -// return; -// } - -// trace_gdbstub_op_exiting((uint8_t)code); - -// if (gdbserver_state.allow_stop_reply) { -// snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); -// gdb_put_packet(buf); -// gdbserver_state.allow_stop_reply = false; -// } - -// qemu_chr_fe_deinit(&gdbserver_system_state.chr, true); -// } - -// void gdb_qemu_exit(int code) { qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_SHUTDOWN, code); } - -/* - * Memory access - */ - int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, uint8_t *buf, int len, bool is_write) { if (is_write) { cpu_physical_memory_write(addr, buf, len); @@ -373,18 +73,10 @@ int gdb_target_memory_rw_debug(CPUState *cpu, hwaddr addr, uint8_t *buf, int len return 0; } -/* - * cpu helpers - */ - unsigned int gdb_get_max_cpus(void) { return 1; } bool gdb_can_reverse(void) { return false; } -/* - * Softmmu specific command helpers - */ - void gdb_handle_query_qemu_phy_mem_mode(GArray *params, void *ctx) { g_string_printf(gdbserver_state.str_buf, "%d", 1); gdb_put_strbuf(); @@ -424,15 +116,10 @@ void gdb_handle_query_rcmd(GArray *params, void *ctx) { gdb_put_packet("OK"); } -/* - * Execution state helpers - */ - void gdb_handle_query_attached(GArray *params, void *ctx) { gdb_put_packet("1"); } void gdb_continue(void) { if (!runstate_needs_reset()) { - // trace_gdbstub_op_continue(); vm_start(); } } @@ -537,4 +224,167 @@ void qemu_clock_enable() {} void gdb_handle_file_io(GArray *params, void *user_ctx) {} void gdb_disable_syscalls(void) {} -void cpu_single_step(CPUState *cpu, int enabled) {} \ No newline at end of file +void cpu_single_step(CPUState *cpu, int enabled) {} + +uint32_t gdb_get_cpu_pid(CPUState *cpu) { + if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) { + /* Return the default process' PID */ + int index = gdbserver_state.process_num - 1; + return gdbserver_state.processes[index].pid; + } + return cpu->cluster_index + 1; +} + +GDBProcess *gdb_get_cpu_process(CPUState *cpu) { return gdb_get_process(gdb_get_cpu_pid(cpu)); } + +CPUState *find_cpu(uint32_t thread_id) { + CPUState *cpu; + + CPU_FOREACH(cpu) { + if (gdb_get_cpu_index(c) == thread_id) { + return cpu; + } + } + + return NULL; +} + +CPUState *gdb_get_first_cpu_in_process(GDBProcess *process) { + CPUState *cpu; + + CPU_FOREACH(cpu) { + if (gdb_get_cpu_pid(c) == process->pid) { + return c; + } + } + + return NULL; +} + +CPUState *gdb_next_cpu_in_process(CPUState *cpu) { + uint32_t pid = gdb_get_cpu_pid(cpu); + cpu = cpu_next(cpu); + + while (cpu) { + if (gdb_get_cpu_pid(cpu) == pid) { + break; + } + + cpu = cpu_next(cpu); + } + + return cpu; +} + +/* Return the cpu following @cpu, while ignoring unattached processes. */ +CPUState *gdb_next_attached_cpu(CPUState *cpu) { + cpu = cpu_next(cpu); + + while (cpu) { + if (gdb_get_cpu_process(cpu)->attached) { + break; + } + + cpu = cpu_next(cpu); + } + + return cpu; +} + +/* Return the first attached cpu */ +CPUState *gdb_first_attached_cpu(void) { + CPUState *cpu = get_cpu(); + GDBProcess *process = gdb_get_cpu_process(cpu); + + if (!process->attached) { + return gdb_next_attached_cpu(cpu); + } + + return cpu; +} + +CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid) { + GDBProcess *process; + CPUState *cpu; + + if (!pid && !tid) { + /* 0 means any process/thread, we take the first attached one */ + return gdb_first_attached_cpu(); + } else if (pid && !tid) { + /* any thread in a specific process */ + process = gdb_get_process(pid); + + if (process == NULL) { + return NULL; + } + + if (!process->attached) { + return NULL; + } + + return gdb_get_first_cpu_in_process(process); + } else { + /* a specific thread */ + cpu = find_cpu(tid); + + if (cpu == NULL) { + return NULL; + } + + process = gdb_get_cpu_process(cpu); + + if (pid && process->pid != pid) { + return NULL; + } + + if (!process->attached) { + return NULL; + } + + return cpu; + } +} + +const char *get_feature_xml(const char *p, const char **newp, GDBProcess *process) { + CPUState *cpu = gdb_get_first_cpu_in_process(process); + GDBRegisterState *r; + size_t len; + + const char *term = strchr(p, ':'); + *newp = term + 1; + len = term - p; + + /* Is it the main target xml? */ + if (strncmp(p, "target.xml", len) == 0) { + if (!process->target_xml) { + g_autoptr(GPtrArray) xml = g_ptr_array_new_with_free_func(g_free); + + g_ptr_array_add(xml, g_strdup("" + "" + "")); + + if (cpu->cc->gdb_arch_name) { + g_ptr_array_add(xml, g_markup_printf_escaped("%s", 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); + g_ptr_array_add(xml, g_markup_printf_escaped("", r->feature->xmlname)); + } + g_ptr_array_add(xml, g_strdup("")); + g_ptr_array_add(xml, NULL); + + process->target_xml = g_strjoinv(NULL, (gchar **)xml->pdata); + } + return process->target_xml; + } + /* Is it one of the features? */ + for (guint i = 0; i < cpu->gdb_regs->len; i++) { + r = &g_array_index(cpu->gdb_regs, GDBRegisterState, i); + if (strncmp(p, r->feature->xmlname, len) == 0) { + return r->feature->xml; + } + } + + /* failed */ + return NULL; +} diff --git a/gdbstub/trace.h b/gdbstub/trace.h deleted file mode 100644 index dee87b1..0000000 --- a/gdbstub/trace.h +++ /dev/null @@ -1 +0,0 @@ -#include "trace/trace-gdbstub.h"