packaging: unlimit stack size to fix argument list too long error
[platform/upstream/binutils.git] / gdb / aarch64-linux-tdep.c
index 8e66425..804e767 100644 (file)
@@ -1,6 +1,6 @@
 /* Target-dependent code for GNU/Linux AArch64.
 
-   Copyright (C) 2009-2013 Free Software Foundation, Inc.
+   Copyright (C) 2009-2014 Free Software Foundation, Inc.
    Contributed by ARM Ltd.
 
    This file is part of GDB.
 #include "regcache.h"
 #include "regset.h"
 
-/* The general-purpose regset consists of 31 X registers, plus SP, PC,
-   and PSTATE registers, as defined in the AArch64 port of the Linux
-   kernel.  */
-#define AARCH64_LINUX_SIZEOF_GREGSET  (34 * X_REGISTER_SIZE)
-
-/* The fp regset consists of 32 V registers, plus FPCR and FPSR which
-   are 4 bytes wide each, and the whole structure is padded to 128 bit
-   alignment.  */
-#define AARCH64_LINUX_SIZEOF_FPREGSET (33 * V_REGISTER_SIZE)
+#include "cli/cli-utils.h"
+#include "stap-probe.h"
+#include "parser-defs.h"
+#include "user-regs.h"
+#include <ctype.h>
 
 /* Signal frame handling.
 
-      +----------+  ^
-      | saved lr |  |
-   +->| saved fp |--+
-   |  |          |
-   |  |          |
-   |  +----------+
-   |  | saved lr |
-   +--| saved fp |
-   ^  |          |
-   |  |          |
-   |  +----------+
-   ^  |          |
-   |  | signal   |
-   |  |          |
-   |  | saved lr |-->interrupted_function_pc
-   +--| saved fp |
-   |  +----------+
-   |  | saved lr |--> default_restorer (movz x8, NR_sys_rt_sigreturn; svc 0)
-   +--| saved fp |<- FP
-      |          |
-      |          |<- SP
-      +----------+
+      +------------+  ^
+      | saved lr   |  |
+   +->| saved fp   |--+
+   |  |            |
+   |  |            |
+   |  +------------+
+   |  | saved lr   |
+   +--| saved fp   |
+   ^  |            |
+   |  |            |
+   |  +------------+
+   ^  |            |
+   |  | signal     |
+   |  |            |        SIGTRAMP_FRAME (struct rt_sigframe)
+   |  | saved regs |
+   +--| saved sp   |--> interrupted_sp
+   |  | saved pc   |--> interrupted_pc
+   |  |            |
+   |  +------------+
+   |  | saved lr   |--> default_restorer (movz x8, NR_sys_rt_sigreturn; svc 0)
+   +--| saved fp   |<- FP
+      |            |         NORMAL_FRAME
+      |            |<- SP
+      +------------+
 
   On signal delivery, the kernel will create a signal handler stack
   frame and setup the return address in LR to point at restorer stub.
   d28015a8        movz    x8, #0xad
   d4000001        svc     #0x0
 
+  This is a system call sys_rt_sigreturn.
+
   We detect signal frames by snooping the return code for the restorer
   instruction sequence.
 
@@ -140,7 +140,6 @@ aarch64_linux_sigframe_init (const struct tramp_frame *self,
 {
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
   CORE_ADDR sp = get_frame_register_unsigned (this_frame, AARCH64_SP_REGNUM);
-  CORE_ADDR fp = get_frame_register_unsigned (this_frame, AARCH64_FP_REGNUM);
   CORE_ADDR sigcontext_addr =
     sp
     + AARCH64_RT_SIGFRAME_UCONTEXT_OFFSET
@@ -154,12 +153,14 @@ aarch64_linux_sigframe_init (const struct tramp_frame *self,
                               sigcontext_addr + AARCH64_SIGCONTEXT_XO_OFFSET
                               + i * AARCH64_SIGCONTEXT_REG_SIZE);
     }
-
-  trad_frame_set_reg_addr (this_cache, AARCH64_FP_REGNUM, fp);
-  trad_frame_set_reg_addr (this_cache, AARCH64_LR_REGNUM, fp + 8);
-  trad_frame_set_reg_addr (this_cache, AARCH64_PC_REGNUM, fp + 8);
-
-  trad_frame_set_id (this_cache, frame_id_build (fp, func));
+  trad_frame_set_reg_addr (this_cache, AARCH64_SP_REGNUM,
+                          sigcontext_addr + AARCH64_SIGCONTEXT_XO_OFFSET
+                            + 31 * AARCH64_SIGCONTEXT_REG_SIZE);
+  trad_frame_set_reg_addr (this_cache, AARCH64_PC_REGNUM,
+                          sigcontext_addr + AARCH64_SIGCONTEXT_XO_OFFSET
+                            + 32 * AARCH64_SIGCONTEXT_REG_SIZE);
+
+  trad_frame_set_id (this_cache, frame_id_build (sp, func));
 }
 
 static const struct tramp_frame aarch64_linux_rt_sigframe =
@@ -179,97 +180,182 @@ static const struct tramp_frame aarch64_linux_rt_sigframe =
   aarch64_linux_sigframe_init
 };
 
-/* Fill GDB's register array with the general-purpose register values
-   in the buffer pointed by GREGS_BUF.  */
+/* Register maps.  */
 
-void
-aarch64_linux_supply_gregset (struct regcache *regcache,
-                             const gdb_byte *gregs_buf)
-{
-  int regno;
+static const struct regcache_map_entry aarch64_linux_gregmap[] =
+  {
+    { 31, AARCH64_X0_REGNUM, 8 }, /* x0 ... x30 */
+    { 1, AARCH64_SP_REGNUM, 8 },
+    { 1, AARCH64_PC_REGNUM, 8 },
+    { 1, AARCH64_CPSR_REGNUM, 8 },
+    { 0 }
+  };
 
-  for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++)
-    regcache_raw_supply (regcache, regno,
-                        gregs_buf + X_REGISTER_SIZE
-                        * (regno - AARCH64_X0_REGNUM));
-}
+static const struct regcache_map_entry aarch64_linux_fpregmap[] =
+  {
+    { 32, AARCH64_V0_REGNUM, 16 }, /* v0 ... v31 */
+    { 1, AARCH64_FPSR_REGNUM, 4 },
+    { 1, AARCH64_FPCR_REGNUM, 4 },
+    { 0 }
+  };
+
+/* Register set definitions.  */
+
+const struct regset aarch64_linux_gregset =
+  {
+    aarch64_linux_gregmap,
+    regcache_supply_regset, regcache_collect_regset
+  };
+
+const struct regset aarch64_linux_fpregset =
+  {
+    aarch64_linux_fpregmap,
+    regcache_supply_regset, regcache_collect_regset
+  };
 
-/* The "supply_regset" function for the general-purpose register set.  */
+/* Implement the "regset_from_core_section" gdbarch method.  */
 
 static void
-supply_gregset_from_core (const struct regset *regset,
-                         struct regcache *regcache,
-                         int regnum, const void *regbuf, size_t len)
+aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
+                                           iterate_over_regset_sections_cb *cb,
+                                           void *cb_data,
+                                           const struct regcache *regcache)
 {
-  aarch64_linux_supply_gregset (regcache, (const gdb_byte *) regbuf);
+  cb (".reg", AARCH64_LINUX_SIZEOF_GREGSET, &aarch64_linux_gregset,
+      NULL, cb_data);
+  cb (".reg2", AARCH64_LINUX_SIZEOF_FPREGSET, &aarch64_linux_fpregset,
+      NULL, cb_data);
 }
 
-/* Fill GDB's register array with the floating-point register values
-   in the buffer pointed by FPREGS_BUF.  */
+/* Implementation of `gdbarch_stap_is_single_operand', as defined in
+   gdbarch.h.  */
 
-void
-aarch64_linux_supply_fpregset (struct regcache *regcache,
-                              const gdb_byte *fpregs_buf)
+static int
+aarch64_stap_is_single_operand (struct gdbarch *gdbarch, const char *s)
 {
-  int regno;
-
-  for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
-    regcache_raw_supply (regcache, regno,
-                        fpregs_buf + V_REGISTER_SIZE
-                        * (regno - AARCH64_V0_REGNUM));
-
-  regcache_raw_supply (regcache, AARCH64_FPSR_REGNUM,
-                      fpregs_buf + V_REGISTER_SIZE * 32);
-  regcache_raw_supply (regcache, AARCH64_FPCR_REGNUM,
-                      fpregs_buf + V_REGISTER_SIZE * 32 + 4);
+  return (*s == '#' || isdigit (*s) /* Literal number.  */
+         || *s == '[' /* Register indirection.  */
+         || isalpha (*s)); /* Register value.  */
 }
 
-/* The "supply_regset" function for the floating-point register set.  */
+/* This routine is used to parse a special token in AArch64's assembly.
 
-static void
-supply_fpregset_from_core (const struct regset *regset,
-                          struct regcache *regcache,
-                          int regnum, const void *regbuf, size_t len)
-{
-  aarch64_linux_supply_fpregset (regcache, (const gdb_byte *) regbuf);
-}
+   The special tokens parsed by it are:
 
-/* Implement the "regset_from_core_section" gdbarch method.  */
+      - Register displacement (e.g, [fp, #-8])
 
-static const struct regset *
-aarch64_linux_regset_from_core_section (struct gdbarch *gdbarch,
-                                       const char *sect_name,
-                                       size_t sect_size)
-{
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+   It returns one if the special token has been parsed successfully,
+   or zero if the current token is not considered special.  */
 
-  if (strcmp (sect_name, ".reg") == 0
-      && sect_size == AARCH64_LINUX_SIZEOF_GREGSET)
+static int
+aarch64_stap_parse_special_token (struct gdbarch *gdbarch,
+                                 struct stap_parse_info *p)
+{
+  if (*p->arg == '[')
     {
-      if (tdep->gregset == NULL)
-       tdep->gregset = regset_alloc (gdbarch, supply_gregset_from_core,
-                                     NULL);
-      return tdep->gregset;
+      /* Temporary holder for lookahead.  */
+      const char *tmp = p->arg;
+      char *endp;
+      /* Used to save the register name.  */
+      const char *start;
+      char *regname;
+      int len;
+      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);
+
+      strncpy (regname, start, len);
+      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);
+      /* Now we expect a number.  It can begin with '#' or simply
+        a digit.  */
+      if (*tmp == '#')
+       ++tmp;
+
+      if (*tmp == '-')
+       {
+         ++tmp;
+         got_minus = 1;
+       }
+      else if (*tmp == '+')
+       ++tmp;
+
+      if (!isdigit (*tmp))
+       return 0;
+
+      displacement = strtol (tmp, &endp, 10);
+      tmp = endp;
+
+      /* Skipping last `]'.  */
+      if (*tmp++ != ']')
+       return 0;
+
+      /* The displacement.  */
+      write_exp_elt_opcode (&p->pstate, OP_LONG);
+      write_exp_elt_type (&p->pstate, builtin_type (gdbarch)->builtin_long);
+      write_exp_elt_longcst (&p->pstate, displacement);
+      write_exp_elt_opcode (&p->pstate, OP_LONG);
+      if (got_minus)
+       write_exp_elt_opcode (&p->pstate, UNOP_NEG);
+
+      /* The register name.  */
+      write_exp_elt_opcode (&p->pstate, OP_REGISTER);
+      str.ptr = regname;
+      str.length = len;
+      write_exp_string (&p->pstate, str);
+      write_exp_elt_opcode (&p->pstate, OP_REGISTER);
+
+      write_exp_elt_opcode (&p->pstate, BINOP_ADD);
+
+      /* Casting to the expected type.  */
+      write_exp_elt_opcode (&p->pstate, UNOP_CAST);
+      write_exp_elt_type (&p->pstate, lookup_pointer_type (p->arg_type));
+      write_exp_elt_opcode (&p->pstate, UNOP_CAST);
+
+      write_exp_elt_opcode (&p->pstate, UNOP_IND);
+
+      p->arg = tmp;
     }
+  else
+    return 0;
 
-  if (strcmp (sect_name, ".reg2") == 0
-      && sect_size == AARCH64_LINUX_SIZEOF_FPREGSET)
-    {
-      if (tdep->fpregset == NULL)
-       tdep->fpregset = regset_alloc (gdbarch, supply_fpregset_from_core,
-                                      NULL);
-      return tdep->fpregset;
-    }
-  return NULL;
+  return 1;
 }
 
 static void
 aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
+  static const char *const stap_integer_prefixes[] = { "#", "", NULL };
+  static const char *const stap_register_prefixes[] = { "", NULL };
+  static const char *const stap_register_indirection_prefixes[] = { "[",
+                                                                   NULL };
+  static const char *const stap_register_indirection_suffixes[] = { "]",
+                                                                   NULL };
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
   tdep->lowest_pc = 0x8000;
 
+  linux_init_abi (info, gdbarch);
+
   set_solib_svr4_fetch_link_map_offsets (gdbarch,
                                         svr4_lp64_fetch_link_map_offsets);
 
@@ -286,8 +372,19 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   /* Enable longjmp.  */
   tdep->jb_pc = 11;
 
-  set_gdbarch_regset_from_core_section (gdbarch,
-                                       aarch64_linux_regset_from_core_section);
+  set_gdbarch_iterate_over_regset_sections
+    (gdbarch, aarch64_linux_iterate_over_regset_sections);
+
+  /* SystemTap related.  */
+  set_gdbarch_stap_integer_prefixes (gdbarch, stap_integer_prefixes);
+  set_gdbarch_stap_register_prefixes (gdbarch, stap_register_prefixes);
+  set_gdbarch_stap_register_indirection_prefixes (gdbarch,
+                                           stap_register_indirection_prefixes);
+  set_gdbarch_stap_register_indirection_suffixes (gdbarch,
+                                           stap_register_indirection_suffixes);
+  set_gdbarch_stap_is_single_operand (gdbarch, aarch64_stap_is_single_operand);
+  set_gdbarch_stap_parse_special_token (gdbarch,
+                                       aarch64_stap_parse_special_token);
 }
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */