* arm-linux-tdep.c (ARM_LDR_PC_SP_4): Add define.
authorUlrich Weigand <uweigand@de.ibm.com>
Tue, 1 Mar 2011 18:20:23 +0000 (18:20 +0000)
committerUlrich Weigand <uweigand@de.ibm.com>
Tue, 1 Mar 2011 18:20:23 +0000 (18:20 +0000)
(arm_linux_restart_syscall_init): Handle both on-stack and in-kernel
versions of the trampoline.  Handle Thumb vs. ARM addresses.
(arm_kernel_linux_restart_syscall_tramp_frame): New global.
(arm_linux_init_abi): Install it.
* arm-tdep.c (arm_psr_thumb_bit): Make global.
* arm-tdep.c (arm_psr_thumb_bit): Add prototype.

gdb/ChangeLog
gdb/arm-linux-tdep.c
gdb/arm-tdep.c
gdb/arm-tdep.h

index 2507c9022021c305ff43b81bcb01281ac6d2659a..af94f8d30bdfd650a2eb87db9033938fc52bb1e7 100644 (file)
@@ -1,3 +1,13 @@
+2011-03-01  Ulrich Weigand  <ulrich.weigand@linaro.org>
+
+       * arm-linux-tdep.c (ARM_LDR_PC_SP_4): Add define.
+       (arm_linux_restart_syscall_init): Handle both on-stack and in-kernel
+       versions of the trampoline.  Handle Thumb vs. ARM addresses.
+       (arm_kernel_linux_restart_syscall_tramp_frame): New global.
+       (arm_linux_init_abi): Install it.
+       * arm-tdep.c (arm_psr_thumb_bit): Make global.
+       * arm-tdep.c (arm_psr_thumb_bit): Add prototype.
+
 2011-02-28  Michael Snyder  <msnyder@vmware.com>
 
        * ui-out.c (ui_out_field_core_addr): Make local char buffer
index ff649d60ddba4afd5de248d0028d78b666cdc197..f60ecc3347a1472659d6901f122d01313bc6f88f 100644 (file)
@@ -239,6 +239,7 @@ static const char arm_linux_thumb2_le_breakpoint[] = { 0xf0, 0xf7, 0x00, 0xa0 };
    whenever OABI support has been enabled in the kernel.  */
 #define ARM_OABI_SYSCALL_RESTART_SYSCALL 0xef900000
 #define ARM_LDR_PC_SP_12               0xe49df00c
+#define ARM_LDR_PC_SP_4                        0xe49df004
 
 static void
 arm_linux_sigtramp_cache (struct frame_info *this_frame,
@@ -355,10 +356,36 @@ arm_linux_restart_syscall_init (const struct tramp_frame *self,
                                struct trad_frame_cache *this_cache,
                                CORE_ADDR func)
 {
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
   CORE_ADDR sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
+  CORE_ADDR pc = get_frame_memory_unsigned (this_frame, sp, 4);
+  CORE_ADDR cpsr = get_frame_register_unsigned (this_frame, ARM_PS_REGNUM);
+  ULONGEST t_bit = arm_psr_thumb_bit (gdbarch);
+  int sp_offset;
+
+  /* There are two variants of this trampoline; with older kernels, the
+     stub is placed on the stack, while newer kernels use the stub from
+     the vector page.  They are identical except that the older version
+     increments SP by 12 (to skip stored PC and the stub itself), while
+     the newer version increments SP only by 4 (just the stored PC).  */
+  if (self->insn[1].bytes == ARM_LDR_PC_SP_4)
+    sp_offset = 4;
+  else
+    sp_offset = 12;
+
+  /* Update Thumb bit in CPSR.  */
+  if (pc & 1)
+    cpsr |= t_bit;
+  else
+    cpsr &= ~t_bit;
 
-  trad_frame_set_reg_addr (this_cache, ARM_PC_REGNUM, sp);
-  trad_frame_set_reg_value (this_cache, ARM_SP_REGNUM, sp + 12);
+  /* Remove Thumb bit from PC.  */
+  pc = gdbarch_addr_bits_remove (gdbarch, pc);
+
+  /* Save previous register values.  */
+  trad_frame_set_reg_value (this_cache, ARM_SP_REGNUM, sp + sp_offset);
+  trad_frame_set_reg_value (this_cache, ARM_PC_REGNUM, pc);
+  trad_frame_set_reg_value (this_cache, ARM_PS_REGNUM, cpsr);
 
   /* Save a frame ID.  */
   trad_frame_set_id (this_cache, frame_id_build (sp, func));
@@ -417,6 +444,17 @@ static struct tramp_frame arm_linux_restart_syscall_tramp_frame = {
   arm_linux_restart_syscall_init
 };
 
+static struct tramp_frame arm_kernel_linux_restart_syscall_tramp_frame = {
+  NORMAL_FRAME,
+  4,
+  {
+    { ARM_OABI_SYSCALL_RESTART_SYSCALL, -1 },
+    { ARM_LDR_PC_SP_4, -1 },
+    { TRAMP_SENTINEL_INSN }
+  },
+  arm_linux_restart_syscall_init
+};
+
 /* Core file and register set support.  */
 
 #define ARM_LINUX_SIZEOF_GREGSET (18 * INT_REGISTER_SIZE)
@@ -1000,6 +1038,8 @@ arm_linux_init_abi (struct gdbarch_info info,
                                &arm_eabi_linux_rt_sigreturn_tramp_frame);
   tramp_frame_prepend_unwinder (gdbarch,
                                &arm_linux_restart_syscall_tramp_frame);
+  tramp_frame_prepend_unwinder (gdbarch,
+                               &arm_kernel_linux_restart_syscall_tramp_frame);
 
   /* Core file support.  */
   set_gdbarch_regset_from_core_section (gdbarch,
index e5e9055083474edbb83ddd2a84dcf7577f0078a5..824841822866388f94339907c7492607d60c95d6 100644 (file)
@@ -262,7 +262,7 @@ int arm_apcs_32 = 1;
 
 /* Return the bit mask in ARM_PS_REGNUM that indicates Thumb mode.  */
 
-static int
+int
 arm_psr_thumb_bit (struct gdbarch *gdbarch)
 {
   if (gdbarch_tdep (gdbarch)->is_m)
index ef0200238f9be71c9b3447e829681660b03664f7..de3f6cca769bd95faa043e14e311bb40ab104144 100644 (file)
@@ -309,6 +309,9 @@ extern void arm_displaced_step_fixup (struct gdbarch *,
                                      struct displaced_step_closure *,
                                      CORE_ADDR, CORE_ADDR, struct regcache *);
 
+/* Return the bit mask in ARM_PS_REGNUM that indicates Thumb mode.  */
+extern int arm_psr_thumb_bit (struct gdbarch *);
+
 /* Is the instruction at the given memory address a Thumb or ARM
    instruction?  */
 extern int arm_pc_is_thumb (struct gdbarch *, CORE_ADDR);