|
@@ -75,8 +75,6 @@ void gdb_init_gdbserver_state(void)
|
|
|
gdbserver_state.sstep_flags &= gdbserver_state.supported_sstep_flags;
|
|
|
}
|
|
|
|
|
|
-bool gdb_has_xml;
|
|
|
-
|
|
|
/* writes 2*len+1 bytes in buf */
|
|
|
void gdb_memtohex(GString *buf, const uint8_t *mem, int len)
|
|
|
{
|
|
@@ -351,67 +349,75 @@ static CPUState *gdb_get_cpu(uint32_t pid, uint32_t tid)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+bool gdb_has_xml(void)
|
|
|
+{
|
|
|
+ return !!gdb_get_cpu_process(gdbserver_state.g_cpu)->target_xml;
|
|
|
+}
|
|
|
+
|
|
|
static const char *get_feature_xml(const char *p, const char **newp,
|
|
|
GDBProcess *process)
|
|
|
{
|
|
|
- size_t len;
|
|
|
- int i;
|
|
|
- const char *name;
|
|
|
CPUState *cpu = gdb_get_first_cpu_in_process(process);
|
|
|
CPUClass *cc = CPU_GET_CLASS(cpu);
|
|
|
+ size_t len;
|
|
|
|
|
|
- len = 0;
|
|
|
- while (p[len] && p[len] != ':')
|
|
|
- len++;
|
|
|
- *newp = p + len;
|
|
|
+ /*
|
|
|
+ * qXfer:features:read:ANNEX:OFFSET,LENGTH'
|
|
|
+ * ^p ^newp
|
|
|
+ */
|
|
|
+ char *term = strchr(p, ':');
|
|
|
+ *newp = term + 1;
|
|
|
+ len = term - p;
|
|
|
|
|
|
- name = NULL;
|
|
|
+ /* Is it the main target xml? */
|
|
|
if (strncmp(p, "target.xml", len) == 0) {
|
|
|
- char *buf = process->target_xml;
|
|
|
- const size_t buf_sz = sizeof(process->target_xml);
|
|
|
-
|
|
|
- /* Generate the XML description for this CPU. */
|
|
|
- if (!buf[0]) {
|
|
|
+ if (!process->target_xml) {
|
|
|
GDBRegisterState *r;
|
|
|
+ GString *xml = g_string_new("<?xml version=\"1.0\"?>");
|
|
|
+
|
|
|
+ g_string_append(xml,
|
|
|
+ "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"
|
|
|
+ "<target>");
|
|
|
|
|
|
- pstrcat(buf, buf_sz,
|
|
|
- "<?xml version=\"1.0\"?>"
|
|
|
- "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">"
|
|
|
- "<target>");
|
|
|
if (cc->gdb_arch_name) {
|
|
|
- gchar *arch = cc->gdb_arch_name(cpu);
|
|
|
- pstrcat(buf, buf_sz, "<architecture>");
|
|
|
- pstrcat(buf, buf_sz, arch);
|
|
|
- pstrcat(buf, buf_sz, "</architecture>");
|
|
|
- g_free(arch);
|
|
|
+ g_autofree gchar *arch = cc->gdb_arch_name(cpu);
|
|
|
+ g_string_append_printf(xml,
|
|
|
+ "<architecture>%s</architecture>",
|
|
|
+ arch);
|
|
|
}
|
|
|
- pstrcat(buf, buf_sz, "<xi:include href=\"");
|
|
|
- pstrcat(buf, buf_sz, cc->gdb_core_xml_file);
|
|
|
- pstrcat(buf, buf_sz, "\"/>");
|
|
|
+ g_string_append(xml, "<xi:include href=\"");
|
|
|
+ g_string_append(xml, cc->gdb_core_xml_file);
|
|
|
+ g_string_append(xml, "\"/>");
|
|
|
for (r = cpu->gdb_regs; r; r = r->next) {
|
|
|
- pstrcat(buf, buf_sz, "<xi:include href=\"");
|
|
|
- pstrcat(buf, buf_sz, r->xml);
|
|
|
- pstrcat(buf, buf_sz, "\"/>");
|
|
|
+ g_string_append(xml, "<xi:include href=\"");
|
|
|
+ g_string_append(xml, r->xml);
|
|
|
+ g_string_append(xml, "\"/>");
|
|
|
}
|
|
|
- pstrcat(buf, buf_sz, "</target>");
|
|
|
+ g_string_append(xml, "</target>");
|
|
|
+
|
|
|
+ process->target_xml = g_string_free(xml, false);
|
|
|
+ return process->target_xml;
|
|
|
}
|
|
|
- return buf;
|
|
|
}
|
|
|
+ /* Is it dynamically generated by the target? */
|
|
|
if (cc->gdb_get_dynamic_xml) {
|
|
|
- char *xmlname = g_strndup(p, len);
|
|
|
+ g_autofree char *xmlname = g_strndup(p, len);
|
|
|
const char *xml = cc->gdb_get_dynamic_xml(cpu, xmlname);
|
|
|
-
|
|
|
- g_free(xmlname);
|
|
|
if (xml) {
|
|
|
return xml;
|
|
|
}
|
|
|
}
|
|
|
- for (i = 0; ; i++) {
|
|
|
- name = xml_builtin[i][0];
|
|
|
- if (!name || (strncmp(name, p, len) == 0 && strlen(name) == len))
|
|
|
- break;
|
|
|
+ /* Is it one of the encoded gdb-xml/ files? */
|
|
|
+ for (int i = 0; xml_builtin[i][0]; i++) {
|
|
|
+ const char *name = xml_builtin[i][0];
|
|
|
+ if ((strncmp(name, p, len) == 0) &&
|
|
|
+ strlen(name) == len) {
|
|
|
+ return xml_builtin[i][1];
|
|
|
+ }
|
|
|
}
|
|
|
- return name ? xml_builtin[i][1] : NULL;
|
|
|
+
|
|
|
+ /* failed */
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
static int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg)
|
|
@@ -450,12 +456,6 @@ static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-/* Register a supplemental set of CPU registers. If g_pos is nonzero it
|
|
|
- specifies the first register number and these registers are included in
|
|
|
- a standard "g" packet. Direction is relative to gdb, i.e. get_reg is
|
|
|
- gdb reading a CPU register, and set_reg is gdb modifying a CPU register.
|
|
|
- */
|
|
|
-
|
|
|
void gdb_register_coprocessor(CPUState *cpu,
|
|
|
gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
|
|
|
int num_regs, const char *xml, int g_pos)
|
|
@@ -597,6 +597,15 @@ static int gdb_handle_vcont(const char *p)
|
|
|
* or incorrect parameters passed.
|
|
|
*/
|
|
|
res = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * target_count and last_target keep track of how many CPUs we are going to
|
|
|
+ * step or resume, and a pointer to the state structure of one of them,
|
|
|
+ * respectivelly
|
|
|
+ */
|
|
|
+ int target_count = 0;
|
|
|
+ CPUState *last_target = NULL;
|
|
|
+
|
|
|
while (*p) {
|
|
|
if (*p++ != ';') {
|
|
|
return -ENOTSUP;
|
|
@@ -637,6 +646,9 @@ static int gdb_handle_vcont(const char *p)
|
|
|
while (cpu) {
|
|
|
if (newstates[cpu->cpu_index] == 1) {
|
|
|
newstates[cpu->cpu_index] = cur_action;
|
|
|
+
|
|
|
+ target_count++;
|
|
|
+ last_target = cpu;
|
|
|
}
|
|
|
|
|
|
cpu = gdb_next_attached_cpu(cpu);
|
|
@@ -654,6 +666,9 @@ static int gdb_handle_vcont(const char *p)
|
|
|
while (cpu) {
|
|
|
if (newstates[cpu->cpu_index] == 1) {
|
|
|
newstates[cpu->cpu_index] = cur_action;
|
|
|
+
|
|
|
+ target_count++;
|
|
|
+ last_target = cpu;
|
|
|
}
|
|
|
|
|
|
cpu = gdb_next_cpu_in_process(cpu);
|
|
@@ -671,11 +686,25 @@ static int gdb_handle_vcont(const char *p)
|
|
|
/* only use if no previous match occourred */
|
|
|
if (newstates[cpu->cpu_index] == 1) {
|
|
|
newstates[cpu->cpu_index] = cur_action;
|
|
|
+
|
|
|
+ target_count++;
|
|
|
+ last_target = cpu;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * if we're about to resume a specific set of CPUs/threads, make it so that
|
|
|
+ * in case execution gets interrupted, we can send GDB a stop reply with a
|
|
|
+ * correct value. it doesn't really matter which CPU we tell GDB the signal
|
|
|
+ * happened in (VM pauses stop all of them anyway), so long as it is one of
|
|
|
+ * the ones we resumed/single stepped here.
|
|
|
+ */
|
|
|
+ if (target_count > 0) {
|
|
|
+ gdbserver_state.c_cpu = last_target;
|
|
|
+ }
|
|
|
+
|
|
|
gdbserver_state.signal = signal;
|
|
|
gdb_continue_partial(newstates);
|
|
|
return res;
|
|
@@ -807,7 +836,7 @@ static inline int startswith(const char *string, const char *pattern)
|
|
|
return !strncmp(string, pattern, strlen(pattern));
|
|
|
}
|
|
|
|
|
|
-static int process_string_cmd(void *user_ctx, const char *data,
|
|
|
+static int process_string_cmd(const char *data,
|
|
|
const GdbCmdParseEntry *cmds, int num_cmds)
|
|
|
{
|
|
|
int i;
|
|
@@ -834,7 +863,7 @@ static int process_string_cmd(void *user_ctx, const char *data,
|
|
|
}
|
|
|
|
|
|
gdbserver_state.allow_stop_reply = cmd->allow_stop_reply;
|
|
|
- cmd->handler(params, user_ctx);
|
|
|
+ cmd->handler(params, NULL);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -852,7 +881,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(NULL, data, cmd, 1)) {
|
|
|
+ if (process_string_cmd(data, cmd, 1)) {
|
|
|
gdb_put_packet("");
|
|
|
}
|
|
|
}
|
|
@@ -1052,7 +1081,7 @@ static void handle_set_reg(GArray *params, void *user_ctx)
|
|
|
{
|
|
|
int reg_size;
|
|
|
|
|
|
- if (!gdb_has_xml) {
|
|
|
+ if (!gdb_get_cpu_process(gdbserver_state.g_cpu)->target_xml) {
|
|
|
gdb_put_packet("");
|
|
|
return;
|
|
|
}
|
|
@@ -1073,7 +1102,7 @@ static void handle_get_reg(GArray *params, void *user_ctx)
|
|
|
{
|
|
|
int reg_size;
|
|
|
|
|
|
- if (!gdb_has_xml) {
|
|
|
+ if (!gdb_get_cpu_process(gdbserver_state.g_cpu)->target_xml) {
|
|
|
gdb_put_packet("");
|
|
|
return;
|
|
|
}
|
|
@@ -1365,7 +1394,7 @@ static void handle_v_commands(GArray *params, void *user_ctx)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (process_string_cmd(NULL, get_param(params, 0)->data,
|
|
|
+ if (process_string_cmd(get_param(params, 0)->data,
|
|
|
gdb_v_commands_table,
|
|
|
ARRAY_SIZE(gdb_v_commands_table))) {
|
|
|
gdb_put_packet("");
|
|
@@ -1540,7 +1569,6 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- gdb_has_xml = true;
|
|
|
p = get_param(params, 0)->data;
|
|
|
xml = get_feature_xml(p, &p, process);
|
|
|
if (!xml) {
|
|
@@ -1709,13 +1737,13 @@ static void handle_gen_query(GArray *params, void *user_ctx)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (!process_string_cmd(NULL, get_param(params, 0)->data,
|
|
|
+ if (!process_string_cmd(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, get_param(params, 0)->data,
|
|
|
+ if (process_string_cmd(get_param(params, 0)->data,
|
|
|
gdb_gen_query_table,
|
|
|
ARRAY_SIZE(gdb_gen_query_table))) {
|
|
|
gdb_put_packet("");
|
|
@@ -1728,13 +1756,13 @@ static void handle_gen_set(GArray *params, void *user_ctx)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (!process_string_cmd(NULL, get_param(params, 0)->data,
|
|
|
+ if (!process_string_cmd(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, get_param(params, 0)->data,
|
|
|
+ if (process_string_cmd(get_param(params, 0)->data,
|
|
|
gdb_gen_set_table,
|
|
|
ARRAY_SIZE(gdb_gen_set_table))) {
|
|
|
gdb_put_packet("");
|
|
@@ -2216,6 +2244,6 @@ void gdb_create_default_process(GDBState *s)
|
|
|
process = &s->processes[s->process_num - 1];
|
|
|
process->pid = pid;
|
|
|
process->attached = false;
|
|
|
- process->target_xml[0] = '\0';
|
|
|
+ process->target_xml = NULL;
|
|
|
}
|
|
|
|