Refactor queries for hardware and software single stepping support in GDBServer.
[binutils-gdb.git] / gdb / gdbserver / linux-arm-low.c
index 0a343798a8fbfd4a3e7e8402ff83e4171fb49d8c..0c788886159f464f411c2944889ab917fe930907 100644 (file)
@@ -19,7 +19,9 @@
 #include "server.h"
 #include "linux-low.h"
 #include "arch/arm.h"
+#include "linux-aarch32-low.h"
 
+#include <sys/uio.h>
 /* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h.
    On Bionic elf.h and linux/elf.h have conflicting definitions.  */
 #ifndef ELFMAG0
@@ -28,6 +30,8 @@
 #include "nat/gdb_ptrace.h"
 #include <signal.h>
 
+#include "arch/arm.h"
+
 /* Defined in auto-generated files.  */
 void init_registers_arm (void);
 extern const struct target_desc *tdesc_arm;
@@ -41,9 +45,6 @@ extern const struct target_desc *tdesc_arm_with_vfpv2;
 void init_registers_arm_with_vfpv3 (void);
 extern const struct target_desc *tdesc_arm_with_vfpv3;
 
-void init_registers_arm_with_neon (void);
-extern const struct target_desc *tdesc_arm_with_neon;
-
 #ifndef PTRACE_GET_THREAD_AREA
 #define PTRACE_GET_THREAD_AREA 22
 #endif
@@ -81,6 +82,14 @@ typedef enum
   arm_hwbp_access = 3
 } arm_hwbp_type;
 
+/* Enum describing the different kinds of breakpoints.  */
+enum arm_breakpoint_kinds
+{
+   ARM_BP_KIND_THUMB = 2,
+   ARM_BP_KIND_THUMB2 = 3,
+   ARM_BP_KIND_ARM = 4,
+};
+
 /* Type describing an ARM Hardware Breakpoint Control register value.  */
 typedef unsigned int arm_hwbp_control_t;
 
@@ -117,8 +126,6 @@ struct arch_lwp_info
   CORE_ADDR stopped_data_address;
 };
 
-static unsigned long arm_hwcap;
-
 /* These are in <asm/elf.h> in current kernels.  */
 #define HWCAP_VFP       64
 #define HWCAP_IWMMXT    512
@@ -151,41 +158,12 @@ arm_cannot_fetch_register (int regno)
   return (regno >= arm_num_regs);
 }
 
-static void
-arm_fill_gregset (struct regcache *regcache, void *buf)
-{
-  int i;
-  uint32_t *regs = buf;
-
-  for (i = ARM_A1_REGNUM; i <= ARM_PC_REGNUM; i++)
-    collect_register (regcache, i, &regs[i]);
-
-  collect_register (regcache, ARM_PS_REGNUM, &regs[16]);
-}
-
-static void
-arm_store_gregset (struct regcache *regcache, const void *buf)
-{
-  int i;
-  char zerobuf[8];
-  const uint32_t *regs = buf;
-
-  memset (zerobuf, 0, 8);
-  for (i = ARM_A1_REGNUM; i <= ARM_PC_REGNUM; i++)
-    supply_register (regcache, i, &regs[i]);
-
-  for (; i < ARM_PS_REGNUM; i++)
-    supply_register (regcache, i, zerobuf);
-
-  supply_register (regcache, ARM_PS_REGNUM, &regs[16]);
-}
-
 static void
 arm_fill_wmmxregset (struct regcache *regcache, void *buf)
 {
   int i;
 
-  if (!(arm_hwcap & HWCAP_IWMMXT))
+  if (regcache->tdesc != tdesc_arm_with_iwmmxt)
     return;
 
   for (i = 0; i < 16; i++)
@@ -202,7 +180,7 @@ arm_store_wmmxregset (struct regcache *regcache, const void *buf)
 {
   int i;
 
-  if (!(arm_hwcap & HWCAP_IWMMXT))
+  if (regcache->tdesc != tdesc_arm_with_iwmmxt)
     return;
 
   for (i = 0; i < 16; i++)
@@ -217,41 +195,33 @@ arm_store_wmmxregset (struct regcache *regcache, const void *buf)
 static void
 arm_fill_vfpregset (struct regcache *regcache, void *buf)
 {
-  int i, num, base;
+  int num;
 
-  if (!(arm_hwcap & HWCAP_VFP))
-    return;
-
-  if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
+  if (regcache->tdesc == tdesc_arm_with_neon
+      || regcache->tdesc == tdesc_arm_with_vfpv3)
     num = 32;
-  else
+  else if (regcache->tdesc == tdesc_arm_with_vfpv2)
     num = 16;
+  else
+    return;
 
-  base = find_regno (regcache->tdesc, "d0");
-  for (i = 0; i < num; i++)
-    collect_register (regcache, base + i, (char *) buf + i * 8);
-
-  collect_register_by_name (regcache, "fpscr", (char *) buf + 32 * 8);
+  arm_fill_vfpregset_num (regcache, buf, num);
 }
 
 static void
 arm_store_vfpregset (struct regcache *regcache, const void *buf)
 {
-  int i, num, base;
-
-  if (!(arm_hwcap & HWCAP_VFP))
-    return;
+  int num;
 
-  if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
+  if (regcache->tdesc == tdesc_arm_with_neon
+      || regcache->tdesc == tdesc_arm_with_vfpv3)
     num = 32;
-  else
+  else if (regcache->tdesc == tdesc_arm_with_vfpv2)
     num = 16;
+  else
+    return;
 
-  base = find_regno (regcache->tdesc, "d0");
-  for (i = 0; i < num; i++)
-    supply_register (regcache, base + i, (char *) buf + i * 8);
-
-  supply_register_by_name (regcache, "fpscr", (char *) buf + 32 * 8);
+  arm_store_vfpregset_num (regcache, buf, num);
 }
 
 extern int debug_threads;
@@ -274,19 +244,30 @@ arm_set_pc (struct regcache *regcache, CORE_ADDR pc)
 }
 
 /* Correct in either endianness.  */
-static const unsigned long arm_breakpoint = 0xef9f0001;
-#define arm_breakpoint_len 4
-static const unsigned short thumb_breakpoint = 0xde01;
-static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
+#define arm_abi_breakpoint 0xef9f0001UL
 
 /* For new EABI binaries.  We recognize it regardless of which ABI
    is used for gdbserver, so single threaded debugging should work
    OK, but for multi-threaded debugging we only insert the current
    ABI's breakpoint instruction.  For now at least.  */
-static const unsigned long arm_eabi_breakpoint = 0xe7f001f0;
+#define arm_eabi_breakpoint 0xe7f001f0UL
+
+#ifndef __ARM_EABI__
+static const unsigned long arm_breakpoint = arm_abi_breakpoint;
+#else
+static const unsigned long arm_breakpoint = arm_eabi_breakpoint;
+#endif
+
+#define arm_breakpoint_len 4
+static const unsigned short thumb_breakpoint = 0xde01;
+#define thumb_breakpoint_len 2
+static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 };
+#define thumb2_breakpoint_len 4
+
+/* Returns 1 if the current instruction set is thumb, 0 otherwise.  */
 
 static int
-arm_breakpoint_at (CORE_ADDR where)
+arm_is_thumb_mode (void)
 {
   struct regcache *regcache = get_thread_regcache (current_thread, 1);
   unsigned long cpsr;
@@ -294,6 +275,17 @@ arm_breakpoint_at (CORE_ADDR where)
   collect_register_by_name (regcache, "cpsr", &cpsr);
 
   if (cpsr & 0x20)
+    return 1;
+  else
+    return 0;
+}
+
+/* Returns 1 if there is a software breakpoint at location.  */
+
+static int
+arm_breakpoint_at (CORE_ADDR where)
+{
+  if (arm_is_thumb_mode ())
     {
       /* Thumb mode.  */
       unsigned short insn;
@@ -315,7 +307,7 @@ arm_breakpoint_at (CORE_ADDR where)
       unsigned long insn;
 
       (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
-      if (insn == arm_breakpoint)
+      if (insn == arm_abi_breakpoint)
        return 1;
 
       if (insn == arm_eabi_breakpoint)
@@ -447,7 +439,7 @@ arm_linux_hw_breakpoint_equal (const struct arm_linux_hw_breakpoint *p1,
 
 /* Convert a raw breakpoint type to an enum arm_hwbp_type.  */
 
-static int
+static arm_hwbp_type
 raw_bkpt_type_to_arm_hwbp_type (enum raw_bkpt_type raw_type)
 {
   switch (raw_type)
@@ -566,6 +558,7 @@ arm_supports_z_point_type (char z_type)
 {
   switch (z_type)
     {
+    case Z_PACKET_SW_BP:
     case Z_PACKET_HW_BP:
     case Z_PACKET_WRITE_WP:
     case Z_PACKET_READ_WP:
@@ -704,7 +697,7 @@ arm_stopped_data_address (void)
 static struct arch_process_info *
 arm_new_process (void)
 {
-  struct arch_process_info *info = xcalloc (1, sizeof (*info));
+  struct arch_process_info *info = XCNEW (struct arch_process_info);
   return info;
 }
 
@@ -712,7 +705,7 @@ arm_new_process (void)
 static void
 arm_new_thread (struct lwp_info *lwp)
 {
-  struct arch_lwp_info *info = xcalloc (1, sizeof (*info));
+  struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
   int i;
 
   for (i = 0; i < MAX_BPTS; i++)
@@ -726,8 +719,8 @@ arm_new_thread (struct lwp_info *lwp)
 static void
 arm_new_fork (struct process_info *parent, struct process_info *child)
 {
-  struct arch_process_info *parent_proc_info = parent->priv->arch_private;
-  struct arch_process_info *child_proc_info = child->priv->arch_private;
+  struct arch_process_info *parent_proc_info;
+  struct arch_process_info *child_proc_info;
   struct lwp_info *child_lwp;
   struct arch_lwp_info *child_lwp_info;
   int i;
@@ -738,6 +731,9 @@ arm_new_fork (struct process_info *parent, struct process_info *child)
   gdb_assert (child->priv != NULL
              && child->priv->arch_private != NULL);
 
+  parent_proc_info = parent->priv->arch_private;
+  child_proc_info = child->priv->arch_private;
+
   /* Linux kernel before 2.6.33 commit
      72f674d203cd230426437cdcf7dd6f681dad8b0d
      will inherit hardware debug registers from parent
@@ -821,7 +817,7 @@ arm_prepare_to_resume (struct lwp_info *lwp)
 static int
 arm_get_hwcap (unsigned long *valp)
 {
-  unsigned char *data = alloca (8);
+  unsigned char *data = (unsigned char *) alloca (8);
   int offset = 0;
 
   while ((*the_target->read_auxv) (offset, data, 8) == 8)
@@ -844,11 +840,11 @@ static const struct target_desc *
 arm_read_description (void)
 {
   int pid = lwpid_of (current_thread);
+  unsigned long arm_hwcap = 0;
 
   /* Query hardware watchpoint/breakpoint capabilities.  */
   arm_linux_init_hwbp_cap (pid);
 
-  arm_hwcap = 0;
   if (arm_get_hwcap (&arm_hwcap) == 0)
     return tdesc_arm;
 
@@ -872,13 +868,11 @@ arm_read_description (void)
       /* Now make sure that the kernel supports reading these
         registers.  Support was added in 2.6.30.  */
       errno = 0;
-      buf = xmalloc (32 * 8 + 4);
+      buf = (char *) xmalloc (32 * 8 + 4);
       if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0
          && errno == EIO)
-       {
-         arm_hwcap = 0;
-         result = tdesc_arm;
-       }
+       result = tdesc_arm;
+
       free (buf);
 
       return result;
@@ -892,9 +886,32 @@ arm_read_description (void)
 static void
 arm_arch_setup (void)
 {
+  int tid = lwpid_of (current_thread);
+  int gpregs[18];
+  struct iovec iov;
+
   current_process ()->tdesc = arm_read_description ();
+
+  iov.iov_base = gpregs;
+  iov.iov_len = sizeof (gpregs);
+
+  /* Check if PTRACE_GETREGSET works.  */
+  if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov) == 0)
+    have_ptrace_getregset = 1;
+  else
+    have_ptrace_getregset = 0;
 }
 
+/* Support for hardware single step.  */
+
+static int
+arm_supports_hardware_single_step (void)
+{
+  return 0;
+}
+
+/* Register sets without using PTRACE_GETREGSET.  */
+
 static struct regset_info arm_regsets[] = {
   { PTRACE_GETREGS, PTRACE_SETREGS, 0, 18 * 4,
     GENERAL_REGS,
@@ -905,7 +922,7 @@ static struct regset_info arm_regsets[] = {
   { PTRACE_GETVFPREGS, PTRACE_SETVFPREGS, 0, 32 * 8 + 4,
     EXTENDED_REGS,
     arm_fill_vfpregset, arm_store_vfpregset },
-  { 0, 0, 0, -1, -1, NULL, NULL }
+  NULL_REGSET
 };
 
 static struct regsets_info arm_regsets_info =
@@ -921,7 +938,7 @@ static struct usrregs_info arm_usrregs_info =
     arm_regmap,
   };
 
-static struct regs_info regs_info =
+static struct regs_info regs_info_arm =
   {
     NULL, /* regset_bitmap */
     &arm_usrregs_info,
@@ -931,7 +948,90 @@ static struct regs_info regs_info =
 static const struct regs_info *
 arm_regs_info (void)
 {
-  return &regs_info;
+  const struct target_desc *tdesc = current_process ()->tdesc;
+
+  if (have_ptrace_getregset == 1
+      && (tdesc == tdesc_arm_with_neon || tdesc == tdesc_arm_with_vfpv3))
+    return &regs_info_aarch32;
+  else
+    return &regs_info_arm;
+}
+
+/* Implementation of linux_target_ops method "breakpoint_kind_from_pc".
+
+   Determine the type and size of breakpoint to insert at PCPTR.  Uses the
+   program counter value to determine whether a 16-bit or 32-bit breakpoint
+   should be used.  It returns the breakpoint's kind, and adjusts the program
+   counter (if necessary) to point to the actual memory location where the
+   breakpoint should be inserted.  */
+
+static int
+arm_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
+{
+  if (IS_THUMB_ADDR (*pcptr))
+    {
+      gdb_byte buf[2];
+
+      *pcptr = UNMAKE_THUMB_ADDR (*pcptr);
+
+      /* Check whether we are replacing a thumb2 32-bit instruction.  */
+      if ((*the_target->read_memory) (*pcptr, buf, 2) == 0)
+       {
+         unsigned short inst1 = 0;
+
+         (*the_target->read_memory) (*pcptr, (gdb_byte *) &inst1, 2);
+         if (thumb_insn_size (inst1) == 4)
+           return ARM_BP_KIND_THUMB2;
+       }
+      return ARM_BP_KIND_THUMB;
+    }
+  else
+    return ARM_BP_KIND_ARM;
+}
+
+/*  Implementation of the linux_target_ops method "sw_breakpoint_from_kind".  */
+
+static const gdb_byte *
+arm_sw_breakpoint_from_kind (int kind , int *size)
+{
+  *size = arm_breakpoint_len;
+  /* Define an ARM-mode breakpoint; we only set breakpoints in the C
+     library, which is most likely to be ARM.  If the kernel supports
+     clone events, we will never insert a breakpoint, so even a Thumb
+     C library will work; so will mixing EABI/non-EABI gdbserver and
+     application.  */
+  switch (kind)
+    {
+      case ARM_BP_KIND_THUMB:
+       *size = thumb_breakpoint_len;
+       return (gdb_byte *) &thumb_breakpoint;
+      case ARM_BP_KIND_THUMB2:
+       *size = thumb2_breakpoint_len;
+       return (gdb_byte *) &thumb2_breakpoint;
+      case ARM_BP_KIND_ARM:
+       *size = arm_breakpoint_len;
+       return (const gdb_byte *) &arm_breakpoint;
+      default:
+       return NULL;
+    }
+  return NULL;
+}
+
+/* Implementation of the linux_target_ops method
+   "breakpoint_kind_from_current_state".  */
+
+static int
+arm_breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
+{
+  if (arm_is_thumb_mode ())
+    {
+      *pcptr = MAKE_THUMB_ADDR (*pcptr);
+      return arm_breakpoint_kind_from_pc (pcptr);
+    }
+  else
+    {
+      return arm_breakpoint_kind_from_pc (pcptr);
+    }
 }
 
 struct linux_target_ops the_low_target = {
@@ -942,18 +1042,8 @@ struct linux_target_ops the_low_target = {
   NULL, /* fetch_register */
   arm_get_pc,
   arm_set_pc,
-
-  /* Define an ARM-mode breakpoint; we only set breakpoints in the C
-     library, which is most likely to be ARM.  If the kernel supports
-     clone events, we will never insert a breakpoint, so even a Thumb
-     C library will work; so will mixing EABI/non-EABI gdbserver and
-     application.  */
-#ifndef __ARM_EABI__
-  (const unsigned char *) &arm_breakpoint,
-#else
-  (const unsigned char *) &arm_eabi_breakpoint,
-#endif
-  arm_breakpoint_len,
+  arm_breakpoint_kind_from_pc,
+  arm_sw_breakpoint_from_kind,
   arm_reinsert_addr,
   0,
   arm_breakpoint_at,
@@ -969,6 +1059,15 @@ struct linux_target_ops the_low_target = {
   arm_new_thread,
   arm_new_fork,
   arm_prepare_to_resume,
+  NULL, /* process_qsupported */
+  NULL, /* supports_tracepoints */
+  NULL, /* get_thread_area */
+  NULL, /* install_fast_tracepoint_jump_pad */
+  NULL, /* emit_ops */
+  NULL, /* get_min_fast_tracepoint_insn_len */
+  NULL, /* supports_range_stepping */
+  arm_breakpoint_kind_from_current_state,
+  arm_supports_hardware_single_step
 };
 
 void
@@ -979,7 +1078,8 @@ initialize_low_arch (void)
   init_registers_arm_with_iwmmxt ();
   init_registers_arm_with_vfpv2 ();
   init_registers_arm_with_vfpv3 ();
-  init_registers_arm_with_neon ();
+
+  initialize_low_arch_aarch32 ();
 
   initialize_regsets_info (&arm_regsets_info);
 }