aboutsummaryrefslogtreecommitdiff
path: root/gdbstub/gdbstub.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdbstub/gdbstub.c')
-rw-r--r--gdbstub/gdbstub.c224
1 files changed, 141 insertions, 83 deletions
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index b9ad0a0..def0b7e 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,6 +28,7 @@
#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"
@@ -41,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"
@@ -354,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;
@@ -377,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);
@@ -520,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++) {
@@ -538,11 +537,10 @@ int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg)
static 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++) {
@@ -568,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);
@@ -618,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);
@@ -1318,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();
@@ -1572,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();
@@ -1614,27 +1652,27 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx)
gdb_put_strbuf();
}
-static char *extended_qsupported_features;
-void gdb_extend_qsupported_features(char *qsupported_features)
-{
- /*
- * We don't support different sets of CPU gdb features on different CPUs yet
- * so assert the feature strings are the same on all CPUs, or is set only
- * once (1 CPU).
- */
- g_assert(extended_qsupported_features == NULL ||
- g_strcmp0(extended_qsupported_features, qsupported_features) == 0);
- extended_qsupported_features = qsupported_features;
+static char **extra_query_flags;
+
+void gdb_extend_qsupported_features(char *qflags)
+{
+ 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)
{
- CPUClass *cc;
-
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+");
}
@@ -1668,8 +1706,11 @@ static void handle_query_supported(GArray *params, void *user_ctx)
g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+");
- if (extended_qsupported_features) {
- g_string_append(gdbserver_state.str_buf, extended_qsupported_features);
+ 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();
@@ -1678,7 +1719,6 @@ static void handle_query_supported(GArray *params, void *user_ctx)
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;
@@ -1689,8 +1729,7 @@ 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;
}
@@ -1753,39 +1792,58 @@ static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = {
},
};
-/* Compares if a set of command parsers is equal to another set of parsers. */
-static bool cmp_cmds(GdbCmdParseEntry *c, GdbCmdParseEntry *d, int size)
+/**
+ * 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)
{
- for (int i = 0; i < size; i++) {
- if (!(c[i].handler == d[i].handler &&
- g_strcmp0(c[i].cmd, d[i].cmd) == 0 &&
- c[i].cmd_startswith == d[i].cmd_startswith &&
- g_strcmp0(c[i].schema, d[i].schema) == 0)) {
+ if (!table) {
+ table = g_ptr_array_new();
+ }
- /* Sets are different. */
- return false;
+ 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);
}
}
- /* Sets are equal, i.e. contain the same command parsers. */
- return true;
+ return table;
}
-static GdbCmdParseEntry *extended_query_table;
-static int extended_query_table_size;
-void gdb_extend_query_table(GdbCmdParseEntry *table, int size)
+/**
+ * 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)
{
- /*
- * We don't support different sets of CPU gdb features on different CPUs yet
- * so assert query table is the same on all CPUs, or is set only once
- * (1 CPU).
- */
- g_assert(extended_query_table == NULL ||
- (extended_query_table_size == size &&
- cmp_cmds(extended_query_table, table, size)));
+ 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;
+}
- extended_query_table = table;
- extended_query_table_size = size;
+
+/* 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[] = {
@@ -1798,6 +1856,10 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = {
.cmd = "sThreadInfo",
},
{
+ .handler = handle_query_gdb_server_version,
+ .cmd = "GDBServerVersion",
+ },
+ {
.handler = handle_query_first_threads,
.cmd = "fThreadInfo",
},
@@ -1880,20 +1942,12 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = {
#endif
};
-static GdbCmdParseEntry *extended_set_table;
-static int extended_set_table_size;
-void gdb_extend_set_table(GdbCmdParseEntry *table, int size)
-{
- /*
- * We don't support different sets of CPU gdb features on different CPUs yet
- * so assert set table is the same on all CPUs, or is set only once (1 CPU).
- */
- g_assert(extended_set_table == NULL ||
- (extended_set_table_size == size &&
- cmp_cmds(extended_set_table, table, size)));
+/* Ptr to GdbCmdParseEntry */
+static GPtrArray *extended_set_table;
- extended_set_table = table;
- extended_set_table_size = size;
+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[] = {
@@ -1924,26 +1978,28 @@ 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(gdb_get_cmd_param(params, 0)->data,
+ 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(gdb_get_cmd_param(params, 0)->data,
+ if (process_string_cmd(data,
gdb_gen_query_table,
ARRAY_SIZE(gdb_gen_query_table))) {
return;
}
if (extended_query_table &&
- process_string_cmd(gdb_get_cmd_param(params, 0)->data,
- extended_query_table,
- extended_query_table_size)) {
+ process_extended_table(extended_query_table, data)) {
return;
}
@@ -1953,26 +2009,28 @@ static void handle_gen_query(GArray *params, void *user_ctx)
static void handle_gen_set(GArray *params, void *user_ctx)
{
+ const char *data;
+
if (!params->len) {
return;
}
- if (process_string_cmd(gdb_get_cmd_param(params, 0)->data,
+ 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(gdb_get_cmd_param(params, 0)->data,
+ if (process_string_cmd(data,
gdb_gen_set_table,
ARRAY_SIZE(gdb_gen_set_table))) {
return;
}
if (extended_set_table &&
- process_string_cmd(gdb_get_cmd_param(params, 0)->data,
- extended_set_table,
- extended_set_table_size)) {
+ process_extended_table(extended_set_table, data)) {
return;
}