[ARM] Fixup PC in software single step
[binutils-gdb.git] / gdb / arm-linux-tdep.c
index 6050bdf9531de502f776f7374fd6e651244351c4..e416e28fefa3a3e9361ce66d6feaa14a14986d94 100644 (file)
@@ -265,12 +265,17 @@ static const gdb_byte arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa
 /* Syscall number for rt_sigreturn.  */
 #define ARM_RT_SIGRETURN 173
 
+static CORE_ADDR
+  arm_linux_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self,
+                                         CORE_ADDR pc);
+
 /* Operation function pointers for get_next_pcs.  */
 static struct arm_get_next_pcs_ops arm_linux_get_next_pcs_ops = {
   arm_get_next_pcs_read_memory_unsigned_integer,
-  arm_get_next_pcs_syscall_next_pc,
+  arm_linux_get_next_pcs_syscall_next_pc,
   arm_get_next_pcs_addr_bits_remove,
-  arm_get_next_pcs_is_thumb
+  arm_get_next_pcs_is_thumb,
+  arm_linux_get_next_pcs_fixup,
 };
 
 static void
@@ -778,10 +783,12 @@ arm_linux_sigreturn_return_addr (struct frame_info *frame,
 }
 
 /* Find the value of the next PC after a sigreturn or rt_sigreturn syscall
-   based on current processor state.  */
+   based on current processor state.  In addition, set IS_THUMB depending
+   on whether we will return to ARM or Thumb code.  */
+
 static CORE_ADDR
 arm_linux_sigreturn_next_pc (struct regcache *regcache,
-                            unsigned long svc_number)
+                            unsigned long svc_number, int *is_thumb)
 {
   ULONGEST sp;
   unsigned long sp_data;
@@ -790,6 +797,7 @@ arm_linux_sigreturn_next_pc (struct regcache *regcache,
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
   int pc_offset = 0;
   int is_sigreturn = 0;
+  CORE_ADDR cpsr;
 
   gdb_assert (svc_number == ARM_SIGRETURN
              || svc_number == ARM_RT_SIGRETURN);
@@ -803,6 +811,10 @@ arm_linux_sigreturn_next_pc (struct regcache *regcache,
 
   next_pc = read_memory_unsigned_integer (sp + pc_offset, 4, byte_order);
 
+  /* Set IS_THUMB according the CPSR saved on the stack.  */
+  cpsr = read_memory_unsigned_integer (sp + pc_offset + 4, 4, byte_order);
+  *is_thumb = ((cpsr & arm_psr_thumb_bit (gdbarch)) != 0);
+
   return next_pc;
 }
 
@@ -859,26 +871,23 @@ arm_linux_get_syscall_number (struct gdbarch *gdbarch,
   return svc_number;
 }
 
-/* When the processor is at a syscall instruction, return the PC of the
-   next instruction to be executed.  */
-
 static CORE_ADDR
-arm_linux_syscall_next_pc (struct regcache *regcache)
+arm_linux_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self,
+                                       CORE_ADDR pc)
 {
-  CORE_ADDR pc = regcache_read_pc (regcache);
   CORE_ADDR next_pc = 0;
-  int is_thumb = arm_is_thumb (regcache);
+  int is_thumb = arm_is_thumb (self->regcache);
   ULONGEST svc_number = 0;
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct gdbarch *gdbarch = get_regcache_arch (self->regcache);
 
   if (is_thumb)
     {
-      svc_number = regcache_raw_get_unsigned (regcache, 7);
+      svc_number = regcache_raw_get_unsigned (self->regcache, 7);
       next_pc = pc + 2;
     }
   else
     {
-      struct gdbarch *gdbarch = get_regcache_arch (regcache);
+      struct gdbarch *gdbarch = get_regcache_arch (self->regcache);
       enum bfd_endian byte_order_for_code = 
        gdbarch_byte_order_for_code (gdbarch);
       unsigned long this_instr = 
@@ -891,14 +900,19 @@ arm_linux_syscall_next_pc (struct regcache *regcache)
        }
       else /* EABI.  */
        {
-         svc_number = regcache_raw_get_unsigned (regcache, 7);
+         svc_number = regcache_raw_get_unsigned (self->regcache, 7);
        }
 
       next_pc = pc + 4;
     }
 
   if (svc_number == ARM_SIGRETURN || svc_number == ARM_RT_SIGRETURN)
-    next_pc = arm_linux_sigreturn_next_pc (regcache, svc_number);
+    {
+      /* SIGRETURN or RT_SIGRETURN may affect the arm thumb mode, so
+        update IS_THUMB.   */
+      next_pc = arm_linux_sigreturn_next_pc (self->regcache, svc_number,
+                                            &is_thumb);
+    }
 
   /* Addresses for calling Thumb functions have the bit 0 set.  */
   if (is_thumb)
@@ -934,21 +948,10 @@ arm_linux_software_single_step (struct frame_info *frame)
                         1,
                         regcache);
 
-  next_pcs = arm_get_next_pcs (&next_pcs_ctx, regcache_read_pc (regcache));
+  next_pcs = arm_get_next_pcs (&next_pcs_ctx);
 
   for (i = 0; VEC_iterate (CORE_ADDR, next_pcs, i, pc); i++)
-    {
-      /* The Linux kernel offers some user-mode helpers in a high page.  We can
-        not read this page (as of 2.6.23), and even if we could then we
-        couldn't set breakpoints in it, and even if we could then the atomic
-        operations would fail when interrupted.  They are all called as
-        functions and return to the address in LR, so step to there
-        instead.  */
-      if (pc > 0xffff0000)
-       pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
-
-      arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
-    }
+    arm_insert_single_step_breakpoint (gdbarch, aspace, pc);
 
   do_cleanups (old_chain);
 
@@ -1115,8 +1118,7 @@ arm_catch_kernel_helper_return (struct gdbarch *gdbarch, CORE_ADDR from,
 
 /* Linux-specific displaced step instruction copying function.  Detects when
    the program has stepped into a Linux kernel helper routine (which must be
-   handled as a special case), falling back to arm_displaced_step_copy_insn()
-   if it hasn't.  */
+   handled as a special case).  */
 
 static struct displaced_step_closure *
 arm_linux_displaced_step_copy_insn (struct gdbarch *gdbarch,
@@ -1485,8 +1487,6 @@ arm_linux_init_abi (struct gdbarch_info info,
   set_gdbarch_stap_parse_special_token (gdbarch,
                                        arm_stap_parse_special_token);
 
-  tdep->syscall_next_pc = arm_linux_syscall_next_pc;
-
   /* `catch syscall' */
   set_xml_syscall_file_name (gdbarch, "syscalls/arm-linux.xml");
   set_gdbarch_get_syscall_number (gdbarch, arm_linux_get_syscall_number);