diff mbox

[rfc,v2,arm] Backtrace out of restart handler in vector page

Message ID 201103041642.p24Gg3Ti010819@d06av02.portsmouth.uk.ibm.com
State Accepted
Headers show

Commit Message

Ulrich Weigand March 4, 2011, 4:42 p.m. UTC
http://sourceware.org/ml/gdb-patches/2011-02/msg00735.html


ChangeLog:

	* 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.
diff mbox

Patch

diff -urNp gdb-orig/gdb/arm-linux-tdep.c gdb-head/gdb/arm-linux-tdep.c
--- gdb-orig/gdb/arm-linux-tdep.c	2011-02-02 16:28:10.000000000 +0100
+++ gdb-head/gdb/arm-linux-tdep.c	2011-02-24 16:11:58.000000000 +0100
@@ -239,6 +239,7 @@  static const char arm_linux_thumb2_le_br
    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 st
 				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_rest
   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)
@@ -1008,6 +1046,8 @@  arm_linux_init_abi (struct gdbarch_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,
diff -urNp gdb-orig/gdb/arm-tdep.c gdb-head/gdb/arm-tdep.c
--- gdb-orig/gdb/arm-tdep.c	2011-02-17 14:00:14.000000000 +0100
+++ gdb-head/gdb/arm-tdep.c	2011-02-24 15:49:28.000000000 +0100
@@ -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)
diff -urNp gdb-orig/gdb/arm-tdep.h gdb-head/gdb/arm-tdep.h
--- gdb-orig/gdb/arm-tdep.h	2011-02-09 15:28:26.000000000 +0100
+++ gdb-head/gdb/arm-tdep.h	2011-02-24 15:49:28.000000000 +0100
@@ -310,6 +310,9 @@  extern void arm_displaced_step_fixup (st
 				      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);