Index: gdb/arm-linux-tdep.c |
diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c |
index ac4860c3dbd1c58dbc6809d0fabaa0bce4176f4c..f4eaa5cc422e84a8799f64bbf4ee94b044057481 100644 |
--- a/gdb/arm-linux-tdep.c |
+++ b/gdb/arm-linux-tdep.c |
@@ -43,6 +43,12 @@ |
#include "gdbthread.h" |
#include "symfile.h" |
+#include "cli/cli-utils.h" |
+#include "stap-probe.h" |
+#include "parser-defs.h" |
+#include "user-regs.h" |
+#include <ctype.h> |
+ |
#include "gdb_string.h" |
/* This is defined in <elf.h> on ARM GNU/Linux systems. */ |
@@ -937,6 +943,9 @@ arm_linux_copy_svc (struct gdbarch *gdbarch, struct regcache *regs, |
= set_momentary_breakpoint (gdbarch, sal, get_frame_id (frame), |
bp_step_resume); |
+ /* set_momentary_breakpoint invalidates FRAME. */ |
+ frame = NULL; |
+ |
/* We need to make sure we actually insert the momentary |
breakpoint set above. */ |
insert_breakpoints (); |
@@ -1053,6 +1062,122 @@ arm_linux_displaced_step_copy_insn (struct gdbarch *gdbarch, |
return dsc; |
} |
+static int |
+arm_stap_is_single_operand (struct gdbarch *gdbarch, const char *s) |
+{ |
+ return (*s == '#' /* Literal number. */ |
+ || *s == '[' /* Register indirection or |
+ displacement. */ |
+ || isalpha (*s)); /* Register value. */ |
+} |
+ |
+/* This routine is used to parse a special token in ARM's assembly. |
+ |
+ The special tokens parsed by it are: |
+ |
+ - Register displacement (e.g, [fp, #-8]) |
+ |
+ It returns one if the special token has been parsed successfully, |
+ or zero if the current token is not considered special. */ |
+ |
+static int |
+arm_stap_parse_special_token (struct gdbarch *gdbarch, |
+ struct stap_parse_info *p) |
+{ |
+ if (*p->arg == '[') |
+ { |
+ /* Temporary holder for lookahead. */ |
+ const char *tmp = p->arg; |
+ /* Used to save the register name. */ |
+ const char *start; |
+ char *regname; |
+ int len, offset; |
+ int got_minus = 0; |
+ long displacement; |
+ struct stoken str; |
+ |
+ ++tmp; |
+ start = tmp; |
+ |
+ /* Register name. */ |
+ while (isalnum (*tmp)) |
+ ++tmp; |
+ |
+ if (*tmp != ',') |
+ return 0; |
+ |
+ len = tmp - start; |
+ regname = alloca (len + 2); |
+ |
+ offset = 0; |
+ if (isdigit (*start)) |
+ { |
+ /* If we are dealing with a register whose name begins with a |
+ digit, it means we should prefix the name with the letter |
+ `r', because GDB expects this name pattern. Otherwise (e.g., |
+ we are dealing with the register `fp'), we don't need to |
+ add such a prefix. */ |
+ regname[0] = 'r'; |
+ offset = 1; |
+ } |
+ |
+ strncpy (regname + offset, start, len); |
+ len += offset; |
+ regname[len] = '\0'; |
+ |
+ if (user_reg_map_name_to_regnum (gdbarch, regname, len) == -1) |
+ error (_("Invalid register name `%s' on expression `%s'."), |
+ regname, p->saved_arg); |
+ |
+ ++tmp; |
+ tmp = skip_spaces_const (tmp); |
+ if (*tmp++ != '#') |
+ return 0; |
+ |
+ if (*tmp == '-') |
+ { |
+ ++tmp; |
+ got_minus = 1; |
+ } |
+ |
+ displacement = strtol (tmp, (char **) &tmp, 10); |
+ |
+ /* Skipping last `]'. */ |
+ if (*tmp++ != ']') |
+ return 0; |
+ |
+ /* The displacement. */ |
+ write_exp_elt_opcode (OP_LONG); |
+ write_exp_elt_type (builtin_type (gdbarch)->builtin_long); |
+ write_exp_elt_longcst (displacement); |
+ write_exp_elt_opcode (OP_LONG); |
+ if (got_minus) |
+ write_exp_elt_opcode (UNOP_NEG); |
+ |
+ /* The register name. */ |
+ write_exp_elt_opcode (OP_REGISTER); |
+ str.ptr = regname; |
+ str.length = len; |
+ write_exp_string (str); |
+ write_exp_elt_opcode (OP_REGISTER); |
+ |
+ write_exp_elt_opcode (BINOP_ADD); |
+ |
+ /* Casting to the expected type. */ |
+ write_exp_elt_opcode (UNOP_CAST); |
+ write_exp_elt_type (lookup_pointer_type (p->arg_type)); |
+ write_exp_elt_opcode (UNOP_CAST); |
+ |
+ write_exp_elt_opcode (UNOP_IND); |
+ |
+ p->arg = tmp; |
+ } |
+ else |
+ return 0; |
+ |
+ return 1; |
+} |
+ |
static void |
arm_linux_init_abi (struct gdbarch_info info, |
struct gdbarch *gdbarch) |
@@ -1152,8 +1277,23 @@ arm_linux_init_abi (struct gdbarch_info info, |
simple_displaced_step_free_closure); |
set_gdbarch_displaced_step_location (gdbarch, displaced_step_at_entry_point); |
+ /* Reversible debugging, process record. */ |
+ set_gdbarch_process_record (gdbarch, arm_process_record); |
+ |
+ /* SystemTap functions. */ |
+ set_gdbarch_stap_integer_prefix (gdbarch, "#"); |
+ set_gdbarch_stap_register_prefix (gdbarch, "r"); |
+ set_gdbarch_stap_register_indirection_prefix (gdbarch, "["); |
+ set_gdbarch_stap_register_indirection_suffix (gdbarch, "]"); |
+ set_gdbarch_stap_gdb_register_prefix (gdbarch, "r"); |
+ set_gdbarch_stap_is_single_operand (gdbarch, arm_stap_is_single_operand); |
+ set_gdbarch_stap_parse_special_token (gdbarch, |
+ arm_stap_parse_special_token); |
tdep->syscall_next_pc = arm_linux_syscall_next_pc; |
+ |
+ /* Syscall record. */ |
+ tdep->arm_swi_record = NULL; |
} |
/* Provide a prototype to silence -Wmissing-prototypes. */ |