Fix inferior memory reading in GDBServer for arm/aarch32
[deliverable/binutils-gdb.git] / gdb / gdbserver / linux-arm-low.c
index 0c788886159f464f411c2944889ab917fe930907..fc2b34870a14b00fb201103521e6204bf01e7815 100644 (file)
@@ -1,5 +1,5 @@
 /* GNU/Linux/ARM specific low level interface, for the remote server for GDB.
-   Copyright (C) 1995-2015 Free Software Foundation, Inc.
+   Copyright (C) 1995-2017 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -19,6 +19,8 @@
 #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>
@@ -29,8 +31,7 @@
 #endif
 #include "nat/gdb_ptrace.h"
 #include <signal.h>
-
-#include "arch/arm.h"
+#include <sys/syscall.h>
 
 /* Defined in auto-generated files.  */
 void init_registers_arm (void);
@@ -82,14 +83,6 @@ 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;
 
@@ -146,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)
 {
@@ -208,6 +222,13 @@ arm_fill_vfpregset (struct regcache *regcache, void *buf)
   arm_fill_vfpregset_num (regcache, buf, num);
 }
 
+/* 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)
 {
@@ -224,115 +245,33 @@ arm_store_vfpregset (struct regcache *regcache, const void *buf)
   arm_store_vfpregset_num (regcache, buf, num);
 }
 
-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)
-    debug_printf ("stop pc is %08lx\n", pc);
-  return pc;
-}
-
-static void
-arm_set_pc (struct regcache *regcache, CORE_ADDR pc)
-{
-  unsigned long newpc = pc;
-  supply_register_by_name (regcache, "pc", &newpc);
-}
-
-/* Correct in either endianness.  */
-#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.  */
-#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.  */
-
+/* Wrapper of arm_is_thumb_mode for get_next_pcs.  */
 static int
-arm_is_thumb_mode (void)
+get_next_pcs_is_thumb (struct arm_get_next_pcs *self)
 {
-  struct regcache *regcache = get_thread_regcache (current_thread, 1);
-  unsigned long cpsr;
-
-  collect_register_by_name (regcache, "cpsr", &cpsr);
-
-  if (cpsr & 0x20)
-    return 1;
-  else
-    return 0;
+  return arm_is_thumb_mode ();
 }
 
-/* Returns 1 if there is a software breakpoint at location.  */
-
-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)
 {
-  if (arm_is_thumb_mode ())
-    {
-      /* Thumb mode.  */
-      unsigned short insn;
+  ULONGEST res;
 
-      (*the_target->read_memory) (where, (unsigned char *) &insn, 2);
-      if (insn == thumb_breakpoint)
-       return 1;
+  res = 0;
+  target_read_memory (memaddr, (unsigned char *) &res, len);
 
-      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_abi_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_thread, 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)
@@ -813,6 +752,88 @@ 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)
@@ -902,6 +923,27 @@ arm_arch_setup (void)
     have_ptrace_getregset = 0;
 }
 
+/* Fetch the next possible PCs after the current instruction executes.  */
+
+static VEC (CORE_ADDR) *
+arm_gdbserver_get_next_pcs (struct regcache *regcache)
+{
+  struct arm_get_next_pcs next_pcs_ctx;
+  VEC (CORE_ADDR) *next_pcs = NULL;
+
+  arm_get_next_pcs_ctor (&next_pcs_ctx,
+                        &get_next_pcs_ops,
+                        /* Byte order is ignored assumed as host.  */
+                        0,
+                        0,
+                        1,
+                        regcache);
+
+  next_pcs = arm_get_next_pcs (&next_pcs_ctx);
+
+  return next_pcs;
+}
+
 /* Support for hardware single step.  */
 
 static int
@@ -910,6 +952,40 @@ 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[] = {
@@ -957,94 +1033,17 @@ arm_regs_info (void)
     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 = {
   arm_arch_setup,
   arm_regs_info,
   arm_cannot_fetch_register,
   arm_cannot_store_register,
   NULL, /* fetch_register */
-  arm_get_pc,
-  arm_set_pc,
+  linux_get_pc_32bit,
+  linux_set_pc_32bit,
   arm_breakpoint_kind_from_pc,
   arm_sw_breakpoint_from_kind,
-  arm_reinsert_addr,
+  arm_gdbserver_get_next_pcs,
   0,
   arm_breakpoint_at,
   arm_supports_z_point_type,
@@ -1067,7 +1066,8 @@ struct linux_target_ops the_low_target = {
   NULL, /* get_min_fast_tracepoint_insn_len */
   NULL, /* supports_range_stepping */
   arm_breakpoint_kind_from_current_state,
-  arm_supports_hardware_single_step
+  arm_supports_hardware_single_step,
+  arm_get_syscall_trapinfo,
 };
 
 void
This page took 0.028238 seconds and 4 git commands to generate.