Update copyright year range in all GDB files.
[deliverable/binutils-gdb.git] / gdb / gdbserver / linux-arm-low.c
index 10370834fcf6b59d76f69557bfbe44920de98c1a..8cad5c5fd4a99b352cef8a53f223b1bd89f0ec34 100644 (file)
@@ -1,5 +1,5 @@
 /* GNU/Linux/ARM specific low level interface, for the remote server for GDB.
-   Copyright (C) 1995-1996, 1998-2012 Free Software Foundation, Inc.
+   Copyright (C) 1995-2019 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 
 #include "server.h"
 #include "linux-low.h"
+#include "arch/arm.h"
+#include "arch/arm-linux.h"
+#include "arch/arm-get-next-pcs.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
 #include <elf.h>
 #endif
-#include <sys/ptrace.h>
+#include "nat/gdb_ptrace.h"
 #include <signal.h>
+#include <sys/syscall.h>
 
 /* Defined in auto-generated files.  */
 void init_registers_arm (void);
+extern const struct target_desc *tdesc_arm;
+
 void init_registers_arm_with_iwmmxt (void);
+extern const struct target_desc *tdesc_arm_with_iwmmxt;
+
 void init_registers_arm_with_vfpv2 (void);
+extern const struct target_desc *tdesc_arm_with_vfpv2;
+
 void init_registers_arm_with_vfpv3 (void);
-void init_registers_arm_with_neon (void);
+extern const struct target_desc *tdesc_arm_with_vfpv3;
 
 #ifndef PTRACE_GET_THREAD_AREA
 #define PTRACE_GET_THREAD_AREA 22
@@ -107,8 +119,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
@@ -129,6 +139,27 @@ static int arm_regmap[] = {
   64
 };
 
+/* Forward declarations needed for get_next_pcs ops.  */
+static ULONGEST get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
+                                                          int len,
+                                                          int byte_order);
+
+static CORE_ADDR get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
+                                               CORE_ADDR val);
+
+static CORE_ADDR get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self);
+
+static int get_next_pcs_is_thumb (struct arm_get_next_pcs *self);
+
+/* get_next_pcs operations.  */
+static struct arm_get_next_pcs_ops get_next_pcs_ops = {
+  get_next_pcs_read_memory_unsigned_integer,
+  get_next_pcs_syscall_next_pc,
+  get_next_pcs_addr_bits_remove,
+  get_next_pcs_is_thumb,
+  arm_linux_get_next_pcs_fixup,
+};
+
 static int
 arm_cannot_store_register (int regno)
 {
@@ -141,36 +172,12 @@ arm_cannot_fetch_register (int regno)
   return (regno >= arm_num_regs);
 }
 
-static void
-arm_fill_gregset (struct regcache *regcache, void *buf)
-{
-  int i;
-
-  for (i = 0; i < arm_num_regs; i++)
-    if (arm_regmap[i] != -1)
-      collect_register (regcache, i, ((char *) buf) + arm_regmap[i]);
-}
-
-static void
-arm_store_gregset (struct regcache *regcache, const void *buf)
-{
-  int i;
-  char zerobuf[8];
-
-  memset (zerobuf, 0, 8);
-  for (i = 0; i < arm_num_regs; i++)
-    if (arm_regmap[i] != -1)
-      supply_register (regcache, i, ((char *) buf) + arm_regmap[i]);
-    else
-      supply_register (regcache, i, zerobuf);
-}
-
 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++)
@@ -187,7 +194,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++)
@@ -202,130 +209,69 @@ arm_store_wmmxregset (struct regcache *regcache, const void *buf)
 static void
 arm_fill_vfpregset (struct regcache *regcache, 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 ("d0");
-  for (i = 0; i < num; i++)
-    collect_register (regcache, base + i, (char *) buf + i * 8);
+  arm_fill_vfpregset_num (regcache, buf, num);
+}
 
-  collect_register_by_name (regcache, "fpscr", (char *) buf + 32 * 8);
+/* Wrapper of UNMAKE_THUMB_ADDR for get_next_pcs.  */
+static CORE_ADDR
+get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self, CORE_ADDR val)
+{
+  return UNMAKE_THUMB_ADDR (val);
 }
 
 static void
 arm_store_vfpregset (struct regcache *regcache, const 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 ("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);
-}
-
-extern int debug_threads;
-
-static CORE_ADDR
-arm_get_pc (struct regcache *regcache)
-{
-  unsigned long pc;
-  collect_register_by_name (regcache, "pc", &pc);
-  if (debug_threads)
-    fprintf (stderr, "stop pc is %08lx\n", pc);
-  return pc;
+  arm_store_vfpregset_num (regcache, buf, num);
 }
 
-static void
-arm_set_pc (struct regcache *regcache, CORE_ADDR pc)
+/* Wrapper of arm_is_thumb_mode for get_next_pcs.  */
+static int
+get_next_pcs_is_thumb (struct arm_get_next_pcs *self)
 {
-  unsigned long newpc = pc;
-  supply_register_by_name (regcache, "pc", &newpc);
+  return arm_is_thumb_mode ();
 }
 
-/* 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 };
-
-/* 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;
-
-static int
-arm_breakpoint_at (CORE_ADDR where)
+/* Read memory from the inferiror.
+   BYTE_ORDER is ignored and there to keep compatiblity with GDB's
+   read_memory_unsigned_integer. */
+static ULONGEST
+get_next_pcs_read_memory_unsigned_integer (CORE_ADDR memaddr,
+                                          int len,
+                                          int byte_order)
 {
-  struct regcache *regcache = get_thread_regcache (current_inferior, 1);
-  unsigned long cpsr;
+  ULONGEST res;
 
-  collect_register_by_name (regcache, "cpsr", &cpsr);
+  res = 0;
+  target_read_memory (memaddr, (unsigned char *) &res, len);
 
-  if (cpsr & 0x20)
-    {
-      /* Thumb mode.  */
-      unsigned short insn;
-
-      (*the_target->read_memory) (where, (unsigned char *) &insn, 2);
-      if (insn == thumb_breakpoint)
-       return 1;
-
-      if (insn == thumb2_breakpoint[0])
-       {
-         (*the_target->read_memory) (where + 2, (unsigned char *) &insn, 2);
-         if (insn == thumb2_breakpoint[1])
-           return 1;
-       }
-    }
-  else
-    {
-      /* ARM mode.  */
-      unsigned long insn;
-
-      (*the_target->read_memory) (where, (unsigned char *) &insn, 4);
-      if (insn == arm_breakpoint)
-       return 1;
-
-      if (insn == arm_eabi_breakpoint)
-       return 1;
-    }
-
-  return 0;
-}
-
-/* We only place breakpoints in empty marker functions, and thread locking
-   is outside of the function.  So rather than importing software single-step,
-   we can just run until exit.  */
-static CORE_ADDR
-arm_reinsert_addr (void)
-{
-  struct regcache *regcache = get_thread_regcache (current_inferior, 1);
-  unsigned long pc;
-  collect_register_by_name (regcache, "lr", &pc);
-  return pc;
+  return res;
 }
 
 /* Fetch the thread-local storage pointer for libthread_db.  */
 
 ps_err_e
-ps_get_thread_area (const struct ps_prochandle *ph,
+ps_get_thread_area (struct ps_prochandle *ph,
                    lwpid_t lwpid, int idx, void **base)
 {
   if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
@@ -430,42 +376,39 @@ arm_linux_hw_breakpoint_equal (const struct arm_linux_hw_breakpoint *p1,
   return p1->address == p2->address && p1->control == p2->control;
 }
 
+/* Convert a raw breakpoint type to an enum arm_hwbp_type.  */
+
+static arm_hwbp_type
+raw_bkpt_type_to_arm_hwbp_type (enum raw_bkpt_type raw_type)
+{
+  switch (raw_type)
+    {
+    case raw_bkpt_type_hw:
+      return arm_hwbp_break;
+    case raw_bkpt_type_write_wp:
+      return arm_hwbp_store;
+    case raw_bkpt_type_read_wp:
+      return arm_hwbp_load;
+    case raw_bkpt_type_access_wp:
+      return arm_hwbp_access;
+    default:
+      gdb_assert_not_reached ("unhandled raw type");
+    }
+}
+
 /* Initialize the hardware breakpoint structure P for a breakpoint or
    watchpoint at ADDR to LEN.  The type of watchpoint is given in TYPE.
-   Returns -1 if TYPE is unsupported, 0 if TYPE represents a breakpoint,
-   and 1 if type represents a watchpoint.  */
+   Returns -1 if TYPE is unsupported, or -2 if the particular combination
+   of ADDR and LEN cannot be implemented.  Otherwise, returns 0 if TYPE
+   represents a breakpoint and 1 if type represents a watchpoint.  */
 static int
-arm_linux_hw_point_initialize (char type, CORE_ADDR addr, int len,
-                              struct arm_linux_hw_breakpoint *p)
+arm_linux_hw_point_initialize (enum raw_bkpt_type raw_type, CORE_ADDR addr,
+                              int len, struct arm_linux_hw_breakpoint *p)
 {
   arm_hwbp_type hwbp_type;
   unsigned mask;
 
-  /* Breakpoint/watchpoint types (GDB terminology):
-     0 = memory breakpoint for instructions
-     (not supported; done via memory write instead)
-     1 = hardware breakpoint for instructions (supported)
-     2 = write watchpoint (supported)
-     3 = read watchpoint (supported)
-     4 = access watchpoint (supported).  */
-  switch (type)
-    {
-    case '1':
-      hwbp_type = arm_hwbp_break;
-      break;
-    case '2':
-      hwbp_type = arm_hwbp_store;
-      break;
-    case '3':
-      hwbp_type = arm_hwbp_load;
-      break;
-    case '4':
-      hwbp_type = arm_hwbp_access;
-      break;
-    default:
-      /* Unsupported.  */
-      return -1;
-    }
+  hwbp_type = raw_bkpt_type_to_arm_hwbp_type (raw_type);
 
   if (hwbp_type == arm_hwbp_break)
     {
@@ -483,7 +426,7 @@ arm_linux_hw_point_initialize (char type, CORE_ADDR addr, int len,
          break;
        default:
          /* Unsupported. */
-         return -1;
+         return -2;
        }
     }
   else
@@ -493,17 +436,17 @@ arm_linux_hw_point_initialize (char type, CORE_ADDR addr, int len,
 
       /* Can not set watchpoints for zero or negative lengths.  */
       if (len <= 0)
-       return -1;
+       return -2;
       /* The current ptrace interface can only handle watchpoints that are a
         power of 2.  */
       if ((len & (len - 1)) != 0)
-       return -1;
+       return -2;
 
       /* Test that the range [ADDR, ADDR + LEN) fits into the largest address
         range covered by a watchpoint.  */
       aligned_addr = addr & ~(max_wp_length - 1);
       if (aligned_addr + max_wp_length < addr + len)
-       return -1;
+       return -2;
 
       mask = (1 << len) - 1;
     }
@@ -517,40 +460,45 @@ arm_linux_hw_point_initialize (char type, CORE_ADDR addr, int len,
 /* Callback to mark a watch-/breakpoint to be updated in all threads of
    the current process.  */
 
-struct update_registers_data
+static void
+update_registers_callback (thread_info *thread, int watch, int i)
 {
-  int watch;
-  int i;
-};
+  struct lwp_info *lwp = get_thread_lwp (thread);
+
+  /* The actual update is done later just before resuming the lwp,
+     we just mark that the registers need updating.  */
+  if (watch)
+    lwp->arch_private->wpts_changed[i] = 1;
+  else
+    lwp->arch_private->bpts_changed[i] = 1;
+
+  /* If the lwp isn't stopped, force it to momentarily pause, so
+     we can update its breakpoint registers.  */
+  if (!lwp->stopped)
+    linux_stop_lwp (lwp);
+}
 
 static int
-update_registers_callback (struct inferior_list_entry *entry, void *arg)
+arm_supports_z_point_type (char z_type)
 {
-  struct lwp_info *lwp = (struct lwp_info *) entry;
-  struct update_registers_data *data = (struct update_registers_data *) arg;
-
-  /* Only update the threads of the current process.  */
-  if (pid_of (lwp) == pid_of (get_thread_lwp (current_inferior)))
+  switch (z_type)
     {
-      /* The actual update is done later just before resuming the lwp,
-         we just mark that the registers need updating.  */
-      if (data->watch)
-       lwp->arch_private->wpts_changed[data->i] = 1;
-      else
-       lwp->arch_private->bpts_changed[data->i] = 1;
-
-      /* If the lwp isn't stopped, force it to momentarily pause, so
-         we can update its breakpoint registers.  */
-      if (!lwp->stopped)
-        linux_stop_lwp (lwp);
+    case Z_PACKET_SW_BP:
+    case Z_PACKET_HW_BP:
+    case Z_PACKET_WRITE_WP:
+    case Z_PACKET_READ_WP:
+    case Z_PACKET_ACCESS_WP:
+      return 1;
+    default:
+      /* Leave the handling of sw breakpoints with the gdb client.  */
+      return 0;
     }
-
-  return 0;
 }
 
 /* Insert hardware break-/watchpoint.  */
 static int
-arm_insert_point (char type, CORE_ADDR addr, int len)
+arm_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+                 int len, struct raw_breakpoint *bp)
 {
   struct process_info *proc = current_process ();
   struct arm_linux_hw_breakpoint p, *pts;
@@ -560,26 +508,31 @@ arm_insert_point (char type, CORE_ADDR addr, int len)
   if (watch < 0)
     {
       /* Unsupported.  */
-      return 1;
+      return watch == -1 ? 1 : -1;
     }
 
   if (watch)
     {
       count = arm_linux_get_hw_watchpoint_count ();
-      pts = proc->private->arch_private->wpts;
+      pts = proc->priv->arch_private->wpts;
     }
   else
     {
       count = arm_linux_get_hw_breakpoint_count ();
-      pts = proc->private->arch_private->bpts;
+      pts = proc->priv->arch_private->bpts;
     }
 
   for (i = 0; i < count; i++)
     if (!arm_hwbp_control_is_enabled (pts[i].control))
       {
-       struct update_registers_data data = { watch, i };
        pts[i] = p;
-       find_inferior (&all_lwps, update_registers_callback, &data);
+
+       /* Only update the threads of the current process.  */
+       for_each_thread (current_thread->id.pid (), [&] (thread_info *thread)
+         {
+           update_registers_callback (thread, watch, i);
+         });
+
        return 0;
       }
 
@@ -589,7 +542,8 @@ arm_insert_point (char type, CORE_ADDR addr, int len)
 
 /* Remove hardware break-/watchpoint.  */
 static int
-arm_remove_point (char type, CORE_ADDR addr, int len)
+arm_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+                 int len, struct raw_breakpoint *bp)
 {
   struct process_info *proc = current_process ();
   struct arm_linux_hw_breakpoint p, *pts;
@@ -605,20 +559,25 @@ arm_remove_point (char type, CORE_ADDR addr, int len)
   if (watch)
     {
       count = arm_linux_get_hw_watchpoint_count ();
-      pts = proc->private->arch_private->wpts;
+      pts = proc->priv->arch_private->wpts;
     }
   else
     {
       count = arm_linux_get_hw_breakpoint_count ();
-      pts = proc->private->arch_private->bpts;
+      pts = proc->priv->arch_private->bpts;
     }
 
   for (i = 0; i < count; i++)
     if (arm_linux_hw_breakpoint_equal (&p, pts + i))
       {
-       struct update_registers_data data = { watch, i };
        pts[i].control = arm_hwbp_control_disable (pts[i].control);
-       find_inferior (&all_lwps, update_registers_callback, &data);
+
+       /* Only update the threads of the current process.  */
+       for_each_thread (current_thread->id.pid (), [&] (thread_info *thread)
+         {
+           update_registers_callback (thread, watch, i);
+         });
+
        return 0;
       }
 
@@ -630,7 +589,7 @@ arm_remove_point (char type, CORE_ADDR addr, int len)
 static int
 arm_stopped_by_watchpoint (void)
 {
-  struct lwp_info *lwp = get_thread_lwp (current_inferior);
+  struct lwp_info *lwp = get_thread_lwp (current_thread);
   siginfo_t siginfo;
 
   /* We must be able to set hardware watchpoints.  */
@@ -639,7 +598,7 @@ arm_stopped_by_watchpoint (void)
 
   /* Retrieve siginfo.  */
   errno = 0;
-  ptrace (PTRACE_GETSIGINFO, lwpid_of (lwp), 0, &siginfo);
+  ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread), 0, &siginfo);
   if (errno != 0)
     return 0;
 
@@ -665,7 +624,7 @@ arm_stopped_by_watchpoint (void)
 static CORE_ADDR
 arm_stopped_data_address (void)
 {
-  struct lwp_info *lwp = get_thread_lwp (current_inferior);
+  struct lwp_info *lwp = get_thread_lwp (current_thread);
   return lwp->arch_private->stopped_data_address;
 }
 
@@ -673,15 +632,23 @@ 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;
 }
 
+/* Called when a process is being deleted.  */
+
+static void
+arm_delete_process (struct arch_process_info *info)
+{
+  xfree (info);
+}
+
 /* Called when a new thread is detected.  */
-static struct arch_lwp_info *
-arm_new_thread (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++)
@@ -689,7 +656,59 @@ arm_new_thread (void)
   for (i = 0; i < MAX_WPTS; i++)
     info->wpts_changed[i] = 1;
 
-  return info;
+  lwp->arch_private = info;
+}
+
+/* Function to call when a thread is being deleted.  */
+
+static void
+arm_delete_thread (struct arch_lwp_info *arch_lwp)
+{
+  xfree (arch_lwp);
+}
+
+static void
+arm_new_fork (struct process_info *parent, struct process_info *child)
+{
+  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;
+
+  /* These are allocated by linux_add_process.  */
+  gdb_assert (parent->priv != NULL
+             && parent->priv->arch_private != NULL);
+  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
+     on fork/vfork/clone.  Newer Linux kernels create such tasks with
+     zeroed debug registers.
+
+     GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  The debug registers mirror will become zeroed
+     in the end before detaching the forked off process, thus making
+     this compatible with older Linux kernels too.  */
+
+  *child_proc_info = *parent_proc_info;
+
+  /* Mark all the hardware breakpoints and watchpoints as changed to
+     make sure that the registers will be updated.  */
+  child_lwp = find_lwp_pid (ptid_t (child->pid));
+  child_lwp_info = child_lwp->arch_private;
+  for (i = 0; i < MAX_BPTS; i++)
+    child_lwp_info->bpts_changed[i] = 1;
+  for (i = 0; i < MAX_WPTS; i++)
+    child_lwp_info->wpts_changed[i] = 1;
 }
 
 /* Called when resuming a thread.
@@ -697,9 +716,10 @@ arm_new_thread (void)
 static void
 arm_prepare_to_resume (struct lwp_info *lwp)
 {
-  int pid = lwpid_of (lwp);
-  struct process_info *proc = find_process_pid (pid_of (lwp));
-  struct arch_process_info *proc_info = proc->private->arch_private;
+  struct thread_info *thread = get_lwp_thread (lwp);
+  int pid = lwpid_of (thread);
+  struct process_info *proc = find_process_pid (pid_of (thread));
+  struct arch_process_info *proc_info = proc->priv->arch_private;
   struct arch_lwp_info *lwp_info = lwp->arch_private;
   int i;
 
@@ -710,13 +730,13 @@ arm_prepare_to_resume (struct lwp_info *lwp)
 
        if (arm_hwbp_control_is_enabled (proc_info->bpts[i].control))
          if (ptrace (PTRACE_SETHBPREGS, pid,
-                     (PTRACE_ARG3_TYPE) ((i << 1) + 1),
+                     (PTRACE_TYPE_ARG3) ((i << 1) + 1),
                      &proc_info->bpts[i].address) < 0)
            perror_with_name ("Unexpected error setting breakpoint address");
 
        if (arm_hwbp_control_is_initialized (proc_info->bpts[i].control))
          if (ptrace (PTRACE_SETHBPREGS, pid,
-                     (PTRACE_ARG3_TYPE) ((i << 1) + 2),
+                     (PTRACE_TYPE_ARG3) ((i << 1) + 2),
                      &proc_info->bpts[i].control) < 0)
            perror_with_name ("Unexpected error setting breakpoint");
 
@@ -730,13 +750,13 @@ arm_prepare_to_resume (struct lwp_info *lwp)
 
        if (arm_hwbp_control_is_enabled (proc_info->wpts[i].control))
          if (ptrace (PTRACE_SETHBPREGS, pid,
-                     (PTRACE_ARG3_TYPE) -((i << 1) + 1),
+                     (PTRACE_TYPE_ARG3) -((i << 1) + 1),
                      &proc_info->wpts[i].address) < 0)
            perror_with_name ("Unexpected error setting watchpoint address");
 
        if (arm_hwbp_control_is_initialized (proc_info->wpts[i].control))
          if (ptrace (PTRACE_SETHBPREGS, pid,
-                     (PTRACE_ARG3_TYPE) -((i << 1) + 2),
+                     (PTRACE_TYPE_ARG3) -((i << 1) + 2),
                      &proc_info->wpts[i].control) < 0)
            perror_with_name ("Unexpected error setting watchpoint");
 
@@ -744,11 +764,93 @@ arm_prepare_to_resume (struct lwp_info *lwp)
       }
 }
 
+/* Find the next pc for a sigreturn or rt_sigreturn syscall.  In
+   addition, set IS_THUMB depending on whether we will return to ARM
+   or Thumb code.
+   See arm-linux.h for stack layout details.  */
+static CORE_ADDR
+arm_sigreturn_next_pc (struct regcache *regcache, int svc_number,
+                      int *is_thumb)
+{
+  unsigned long sp;
+  unsigned long sp_data;
+  /* Offset of PC register.  */
+  int pc_offset = 0;
+  CORE_ADDR next_pc = 0;
+  uint32_t cpsr;
+
+  gdb_assert (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn);
+
+  collect_register_by_name (regcache, "sp", &sp);
+  (*the_target->read_memory) (sp, (unsigned char *) &sp_data, 4);
+
+  pc_offset = arm_linux_sigreturn_next_pc_offset
+    (sp, sp_data, svc_number, __NR_sigreturn == svc_number ? 1 : 0);
+
+  (*the_target->read_memory) (sp + pc_offset, (unsigned char *) &next_pc, 4);
+
+  /* Set IS_THUMB according the CPSR saved on the stack.  */
+  (*the_target->read_memory) (sp + pc_offset + 4, (unsigned char *) &cpsr, 4);
+  *is_thumb = ((cpsr & CPSR_T) != 0);
+
+  return next_pc;
+}
+
+/* When PC is at a syscall instruction, return the PC of the next
+   instruction to be executed.  */
+static CORE_ADDR
+get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self)
+{
+  CORE_ADDR next_pc = 0;
+  CORE_ADDR pc = regcache_read_pc (self->regcache);
+  int is_thumb = arm_is_thumb_mode ();
+  ULONGEST svc_number = 0;
+  struct regcache *regcache = self->regcache;
+
+  if (is_thumb)
+    {
+      collect_register (regcache, 7, &svc_number);
+      next_pc = pc + 2;
+    }
+  else
+    {
+      unsigned long this_instr;
+      unsigned long svc_operand;
+
+      target_read_memory (pc, (unsigned char *) &this_instr, 4);
+      svc_operand = (0x00ffffff & this_instr);
+
+      if (svc_operand)  /* OABI.  */
+       {
+         svc_number = svc_operand - 0x900000;
+       }
+      else /* EABI.  */
+       {
+         collect_register (regcache, 7, &svc_number);
+       }
+
+      next_pc = pc + 4;
+    }
+
+  /* This is a sigreturn or sigreturn_rt syscall.  */
+  if (svc_number == __NR_sigreturn || svc_number == __NR_rt_sigreturn)
+    {
+      /* SIGRETURN or RT_SIGRETURN may affect the arm thumb mode, so
+        update IS_THUMB.   */
+      next_pc = arm_sigreturn_next_pc (regcache, svc_number, &is_thumb);
+    }
+
+  /* Addresses for calling Thumb functions have the bit 0 set.  */
+  if (is_thumb)
+    next_pc = MAKE_THUMB_ADDR (next_pc);
+
+  return next_pc;
+}
 
 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)
@@ -767,61 +869,135 @@ arm_get_hwcap (unsigned long *valp)
   return 0;
 }
 
-static void
-arm_arch_setup (void)
+static const struct target_desc *
+arm_read_description (void)
 {
-  int pid = lwpid_of (get_thread_lwp (current_inferior));
+  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)
-    {
-      init_registers_arm ();
-      return;
-    }
+    return tdesc_arm;
 
   if (arm_hwcap & HWCAP_IWMMXT)
-    {
-      init_registers_arm_with_iwmmxt ();
-      return;
-    }
+    return tdesc_arm_with_iwmmxt;
 
   if (arm_hwcap & HWCAP_VFP)
     {
+      const struct target_desc *result;
       char *buf;
 
       /* NEON implies either no VFP, or VFPv3-D32.  We only support
         it with VFP.  */
       if (arm_hwcap & HWCAP_NEON)
-       init_registers_arm_with_neon ();
+       result = tdesc_arm_with_neon;
       else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3)
-       init_registers_arm_with_vfpv3 ();
+       result = tdesc_arm_with_vfpv3;
       else
-       init_registers_arm_with_vfpv2 ();
+       result = tdesc_arm_with_vfpv2;
 
       /* 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;
-         init_registers_arm ();
-       }
+       result = tdesc_arm;
+
       free (buf);
 
-      return;
+      return result;
     }
 
   /* The default configuration uses legacy FPA registers, probably
      simulated.  */
-  init_registers_arm ();
+  return tdesc_arm;
+}
+
+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;
+}
+
+/* Fetch the next possible PCs after the current instruction executes.  */
+
+static std::vector<CORE_ADDR>
+arm_gdbserver_get_next_pcs (struct regcache *regcache)
+{
+  struct arm_get_next_pcs next_pcs_ctx;
+
+  arm_get_next_pcs_ctor (&next_pcs_ctx,
+                        &get_next_pcs_ops,
+                        /* Byte order is ignored assumed as host.  */
+                        0,
+                        0,
+                        1,
+                        regcache);
+
+  return arm_get_next_pcs (&next_pcs_ctx);
 }
 
-struct regset_info target_regsets[] = {
+/* Support for hardware single step.  */
+
+static int
+arm_supports_hardware_single_step (void)
+{
+  return 0;
+}
+
+/* Implementation of linux_target_ops method "get_syscall_trapinfo".  */
+
+static void
+arm_get_syscall_trapinfo (struct regcache *regcache, int *sysno)
+{
+  if (arm_is_thumb_mode ())
+    collect_register_by_name (regcache, "r7", sysno);
+  else
+    {
+      unsigned long pc;
+      unsigned long insn;
+
+      collect_register_by_name (regcache, "pc", &pc);
+
+      if ((*the_target->read_memory) (pc - 4, (unsigned char *) &insn, 4))
+       *sysno = UNKNOWN_SYSCALL;
+      else
+       {
+         unsigned long svc_operand = (0x00ffffff & insn);
+
+         if (svc_operand)
+           {
+             /* OABI */
+             *sysno = svc_operand - 0x900000;
+           }
+         else
+           {
+             /* EABI */
+             collect_register_by_name (regcache, "r7", sysno);
+           }
+       }
+    }
+}
+
+/* Register sets without using PTRACE_GETREGSET.  */
+
+static struct regset_info arm_regsets[] = {
   { PTRACE_GETREGS, PTRACE_SETREGS, 0, 18 * 4,
     GENERAL_REGS,
     arm_fill_gregset, arm_store_gregset },
@@ -831,34 +1007,55 @@ struct regset_info target_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 =
+  {
+    arm_regsets, /* regsets */
+    0, /* num_regsets */
+    NULL, /* disabled_regsets */
+  };
+
+static struct usrregs_info arm_usrregs_info =
+  {
+    arm_num_regs,
+    arm_regmap,
+  };
+
+static struct regs_info regs_info_arm =
+  {
+    NULL, /* regset_bitmap */
+    &arm_usrregs_info,
+    &arm_regsets_info
+  };
+
+static const struct regs_info *
+arm_regs_info (void)
+{
+  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;
+}
+
 struct linux_target_ops the_low_target = {
   arm_arch_setup,
-  arm_num_regs,
-  arm_regmap,
-  NULL,
+  arm_regs_info,
   arm_cannot_fetch_register,
   arm_cannot_store_register,
   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_reinsert_addr,
+  linux_get_pc_32bit,
+  linux_set_pc_32bit,
+  arm_breakpoint_kind_from_pc,
+  arm_sw_breakpoint_from_kind,
+  arm_gdbserver_get_next_pcs,
   0,
   arm_breakpoint_at,
+  arm_supports_z_point_type,
   arm_insert_point,
   arm_remove_point,
   arm_stopped_by_watchpoint,
@@ -867,6 +1064,33 @@ struct linux_target_ops the_low_target = {
   NULL, /* supply_ptrace_register */
   NULL, /* siginfo_fixup */
   arm_new_process,
+  arm_delete_process,
   arm_new_thread,
+  arm_delete_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,
+  arm_get_syscall_trapinfo,
 };
+
+void
+initialize_low_arch (void)
+{
+  /* Initialize the Linux target descriptions.  */
+  init_registers_arm ();
+  init_registers_arm_with_iwmmxt ();
+  init_registers_arm_with_vfpv2 ();
+  init_registers_arm_with_vfpv3 ();
+
+  initialize_low_arch_aarch32 ();
+
+  initialize_regsets_info (&arm_regsets_info);
+}
This page took 0.035202 seconds and 4 git commands to generate.