[rfc] Unwind the ARM CPSR

Daniel Jacobowitz drow@false.org
Thu May 1 18:31:00 GMT 2008


On Thu, Oct 11, 2007 at 10:51:37AM -0400, Daniel Jacobowitz wrote:
> This patch is based on a suggestion that Mark made around March 2006,
> and another related problem that Paul Brook and Vladimir Prus
> discovered.  I would love comments on it; it works, but there is
> one place that I'm unhappy with (in frame unwinding and the DWARF-2
> unwinder).

That place is now gone.  I've tested this version on arm-none-eabi
(where it fixes one failure with -mthumb, gdb.base/ending-run.exp:
step to end of run) and on arm-none-symbianelf.

Checked in.

-- 
Daniel Jacobowitz
CodeSourcery

2008-05-01  Daniel Jacobowitz  <dan@codesourcery.com>

	* arm-linux-tdep.h (ARM_CPSR_REGNUM): Delete definition.
	* arm-tdep.c (arm_frame_is_thumb): New.
	(arm_pc_is_thumb): Clarify comment.
	(thumb_analyze_prologue): Remove PC special case.
	(thumb_scan_prologue): Take a block_addr argument.  Use it for
	find_pc_partial_function.  Remove unused variables.
	(arm_scan_prologue): Use arm_frame_is_thumb.  Use the block address
	for find_pc_partial_function.  Remove PC special case.
	(arm_prologue_prev_register): Add special handling for PC and CPSR.
	(arm_dwarf2_prev_register, arm_dwarf2_frame_init_reg): New.
	(arm_get_next_pc): Use arm_frame_is_thumb.
	(arm_write_pc): Use CPSR_T instead of 0x20.
	(arm_gdbarch_init): Call dwarf2_frame_set_init_reg.
	* arm-tdep.h (enum gdb_regnum): Add ARM_CPSR_REGNUM.
	(CPSR_T): Define.
	* dwarf2-frame.c (dwarf2_frame_prev_register): Handle
	DWARF2_FRAME_REG_FN.
	* dwarf2-frame.h (enum dwarf2_frame_reg_rule): Add
	DWARF2_FRAME_REG_FN.
	(struct dwarf2_frame_state_reg): Add FN to loc union.

2007-10-11  Daniel Jacobowitz  <dan@codesourcery.com>

	* gdb.arch/thumb-prologue.exp: Do not expect a saved PC.

---
 gdb/arm-linux-tdep.h                      |    2 
 gdb/arm-tdep.c                            |  147 +++++++++++++++++++++++-------
 gdb/arm-tdep.h                            |    3 
 gdb/dwarf2-frame.c                        |    3 
 gdb/dwarf2-frame.h                        |    3 
 gdb/testsuite/gdb.arch/thumb-prologue.exp |    2 
 6 files changed, 126 insertions(+), 34 deletions(-)

Index: src/gdb/arm-linux-tdep.h
===================================================================
--- src.orig/gdb/arm-linux-tdep.h	2008-01-01 17:10:01.000000000 -0500
+++ src/gdb/arm-linux-tdep.h	2008-05-01 10:00:54.000000000 -0400
@@ -20,8 +20,6 @@
 struct regset;
 struct regcache;
 
-#define		ARM_CPSR_REGNUM		16
-
 #define ARM_LINUX_SIZEOF_NWFPE (8 * FP_REGISTER_SIZE \
 				+ 2 * INT_REGISTER_SIZE \
 				+ 8 + INT_REGISTER_SIZE)
Index: src/gdb/arm-tdep.c
===================================================================
--- src.orig/gdb/arm-tdep.c	2008-04-30 17:23:09.000000000 -0400
+++ src/gdb/arm-tdep.c	2008-05-01 13:56:01.000000000 -0400
@@ -211,8 +211,25 @@ struct arm_prologue_cache
 
 int arm_apcs_32 = 1;
 
+/* Determine if FRAME is executing in Thumb mode.  */
+
+static int
+arm_frame_is_thumb (struct frame_info *frame)
+{
+  CORE_ADDR cpsr;
+
+  /* Every ARM frame unwinder can unwind the T bit of the CPSR, either
+     directly (from a signal frame or dummy frame) or by interpreting
+     the saved LR (from a prologue or DWARF frame).  So consult it and
+     trust the unwinders.  */
+  cpsr = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
+
+  return (cpsr & CPSR_T) != 0;
+}
+
 /* Determine if the program counter specified in MEMADDR is in a Thumb
-   function.  */
+   function.  This function should be called for addresses unrelated to
+   any executing frame; otherwise, prefer arm_frame_is_thumb.  */
 
 static int
 arm_pc_is_thumb (CORE_ADDR memaddr)
@@ -273,14 +290,6 @@ thumb_analyze_prologue (struct gdbarch *
   stack = make_pv_area (ARM_SP_REGNUM);
   back_to = make_cleanup_free_pv_area (stack);
 
-  /* The call instruction saved PC in LR, and the current PC is not
-     interesting.  Due to this file's conventions, we want the value
-     of LR at this function's entry, not at the call site, so we do
-     not record the save of the PC - when the ARM prologue analyzer
-     has also been converted to the pv mechanism, we could record the
-     save here and remove the hack in prev_register.  */
-  regs[ARM_PC_REGNUM] = pv_unknown ();
-
   while (start < limit)
     {
       unsigned short insn;
@@ -535,22 +544,14 @@ arm_skip_prologue (struct gdbarch *gdbar
 
 static void
 thumb_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR prev_pc,
-		     struct arm_prologue_cache *cache)
+		     CORE_ADDR block_addr, struct arm_prologue_cache *cache)
 {
   CORE_ADDR prologue_start;
   CORE_ADDR prologue_end;
   CORE_ADDR current_pc;
-  /* Which register has been copied to register n?  */
-  int saved_reg[16];
-  /* findmask:
-     bit 0 - push { rlist }
-     bit 1 - mov r7, sp  OR  add r7, sp, #imm  (setting of r7)
-     bit 2 - sub sp, #simm  OR  add sp, #simm  (adjusting of sp)
-  */
-  int findmask = 0;
-  int i;
 
-  if (find_pc_partial_function (prev_pc, NULL, &prologue_start, &prologue_end))
+  if (find_pc_partial_function (block_addr, NULL, &prologue_start,
+				&prologue_end))
     {
       struct symtab_and_line sal = find_pc_line (prologue_start, 0);
 
@@ -644,6 +645,7 @@ arm_scan_prologue (struct frame_info *th
   int regno;
   CORE_ADDR prologue_start, prologue_end, current_pc;
   CORE_ADDR prev_pc = get_frame_pc (this_frame);
+  CORE_ADDR block_addr = get_frame_address_in_block (this_frame);
   pv_t regs[ARM_FPS_REGNUM];
   struct pv_area *stack;
   struct cleanup *back_to;
@@ -654,15 +656,16 @@ arm_scan_prologue (struct frame_info *th
   cache->framesize = 0;
 
   /* Check for Thumb prologue.  */
-  if (arm_pc_is_thumb (prev_pc))
+  if (arm_frame_is_thumb (this_frame))
     {
-      thumb_scan_prologue (gdbarch, prev_pc, cache);
+      thumb_scan_prologue (gdbarch, prev_pc, block_addr, cache);
       return;
     }
 
   /* Find the function prologue.  If we can't find the function in
      the symbol table, peek in the stack frame to find the PC.  */
-  if (find_pc_partial_function (prev_pc, NULL, &prologue_start, &prologue_end))
+  if (find_pc_partial_function (block_addr, NULL, &prologue_start,
+				&prologue_end))
     {
       /* One way to find the end of the prologue (which works well
          for unoptimized code) is to do the following:
@@ -751,8 +754,6 @@ arm_scan_prologue (struct frame_info *th
   stack = make_pv_area (ARM_SP_REGNUM);
   back_to = make_cleanup_free_pv_area (stack);
 
-  regs[ARM_PC_REGNUM] = pv_unknown ();
-
   for (current_pc = prologue_start;
        current_pc < prologue_end;
        current_pc += 4)
@@ -985,10 +986,18 @@ arm_prologue_prev_register (struct frame
   cache = *this_cache;
 
   /* If we are asked to unwind the PC, then we need to return the LR
-     instead.  The saved value of PC points into this frame's
-     prologue, not the next frame's resume location.  */
+     instead.  The prologue may save PC, but it will point into this
+     frame's prologue, not the next frame's resume location.  Also
+     strip the saved T bit.  A valid LR may have the low bit set, but
+     a valid PC never does.  */
   if (prev_regnum == ARM_PC_REGNUM)
-    prev_regnum = ARM_LR_REGNUM;
+    {
+      CORE_ADDR lr;
+
+      lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM);
+      return frame_unwind_got_constant (this_frame, prev_regnum,
+					arm_addr_bits_remove (lr));
+    }
 
   /* SP is generally not saved to the stack, but this frame is
      identified by the next frame's stack pointer at the time of the call.
@@ -996,6 +1005,27 @@ arm_prologue_prev_register (struct frame
   if (prev_regnum == ARM_SP_REGNUM)
     return frame_unwind_got_constant (this_frame, prev_regnum, cache->prev_sp);
 
+  /* The CPSR may have been changed by the call instruction and by the
+     called function.  The only bit we can reconstruct is the T bit,
+     by checking the low bit of LR as of the call.  This is a reliable
+     indicator of Thumb-ness except for some ARM v4T pre-interworking
+     Thumb code, which could get away with a clear low bit as long as
+     the called function did not use bx.  Guess that all other
+     bits are unchanged; the condition flags are presumably lost,
+     but the processor status is likely valid.  */
+  if (prev_regnum == ARM_PS_REGNUM)
+    {
+      CORE_ADDR lr, cpsr;
+
+      cpsr = get_frame_register_unsigned (this_frame, prev_regnum);
+      lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM);
+      if (IS_THUMB_ADDR (lr))
+	cpsr |= CPSR_T;
+      else
+	cpsr &= ~CPSR_T;
+      return frame_unwind_got_constant (this_frame, prev_regnum, cpsr);
+    }
+
   return trad_frame_get_prev_register (this_frame, cache->saved_regs,
 				       prev_regnum);
 }
@@ -1113,6 +1143,59 @@ arm_unwind_sp (struct gdbarch *gdbarch, 
   return frame_unwind_register_unsigned (this_frame, ARM_SP_REGNUM);
 }
 
+static struct value *
+arm_dwarf2_prev_register (struct frame_info *this_frame, void **this_cache,
+			  int regnum)
+{
+  CORE_ADDR lr, cpsr;
+
+  switch (regnum)
+    {
+    case ARM_PC_REGNUM:
+      /* The PC is normally copied from the return column, which
+	 describes saves of LR.  However, that version may have an
+	 extra bit set to indicate Thumb state.  The bit is not
+	 part of the PC.  */
+      lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM);
+      return frame_unwind_got_constant (this_frame, regnum,
+					arm_addr_bits_remove (lr));
+
+    case ARM_PS_REGNUM:
+      /* Reconstruct the T bit; see arm_prologue_prev_register for details.  */
+      CORE_ADDR lr, cpsr;
+
+      cpsr = get_frame_register_unsigned (this_frame, prev_regnum);
+      lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM);
+      if (IS_THUMB_ADDR (lr))
+	cpsr |= CPSR_T;
+      else
+	cpsr &= ~CPSR_T;
+      return frame_unwind_got_constant (this_frame, prev_regnum, cpsr);
+
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _("Unexpected register %d"), regnum);
+    }
+}
+
+static void
+arm_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
+			   struct dwarf2_frame_state_reg *reg,
+			   struct frame_info *this_frame)
+{
+  switch (regnum)
+    {
+    case ARM_PC_REGNUM:
+    case ARM_PS_REGNUM:
+      reg->how = DWARF2_FRAME_REG_FN;
+      reg->loc.fn = arm_dwarf2_prev_register;
+      break;
+    case ARM_SP_REGNUM:
+      reg->how = DWARF2_FRAME_REG_CFA;
+      break;
+    }
+}
+
 /* When arguments must be pushed onto the stack, they go on in reverse
    order.  The code below implements a FILO (stack) to do this.  */
 
@@ -1694,7 +1777,7 @@ arm_get_next_pc (struct frame_info *fram
   unsigned long status;
   CORE_ADDR nextpc;
 
-  if (arm_pc_is_thumb (pc))
+  if (arm_frame_is_thumb (frame))
     return thumb_get_next_pc (frame, pc);
 
   pc_val = (unsigned long) pc;
@@ -2667,10 +2750,10 @@ arm_write_pc (struct regcache *regcache,
       ULONGEST val;
       regcache_cooked_read_unsigned (regcache, ARM_PS_REGNUM, &val);
       if (arm_pc_is_thumb (pc))
-	regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM, val | 0x20);
+	regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM, val | CPSR_T);
       else
 	regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM,
-					val & ~(ULONGEST) 0x20);
+					val & ~(ULONGEST) CPSR_T);
     }
 }
 
@@ -3034,6 +3117,8 @@ arm_gdbarch_init (struct gdbarch_info in
   /* Hook in the ABI-specific overrides, if they have been registered.  */
   gdbarch_init_osabi (info, gdbarch);
 
+  dwarf2_frame_set_init_reg (gdbarch, arm_dwarf2_frame_init_reg);
+
   /* Add some default predicates.  */
   frame_unwind_append_unwinder (gdbarch, &arm_stub_unwind);
   dwarf2_append_unwinders (gdbarch);
Index: src/gdb/arm-tdep.h
===================================================================
--- src.orig/gdb/arm-tdep.h	2008-01-01 17:09:44.000000000 -0500
+++ src/gdb/arm-tdep.h	2008-05-01 10:00:54.000000000 -0400
@@ -38,6 +38,7 @@ enum gdb_regnum {
   ARM_F7_REGNUM = 23, 		/* last floating point register */
   ARM_FPS_REGNUM = 24,		/* floating point status register */
   ARM_PS_REGNUM = 25,		/* Contains processor status */
+  ARM_CPSR_REGNUM = ARM_PS_REGNUM,
   ARM_WR0_REGNUM,		/* WMMX data registers.  */
   ARM_WR15_REGNUM = ARM_WR0_REGNUM + 15,
   ARM_WC0_REGNUM,		/* WMMX control registers.  */
@@ -107,6 +108,8 @@ enum gdb_regnum {
 #define FLAG_C		0x20000000
 #define FLAG_V		0x10000000
 
+#define CPSR_T		0x20
+
 /* Type of floating-point code in use by inferior.  There are really 3 models
    that are traditionally supported (plus the endianness issue), but gcc can
    only generate 2 of those.  The third is APCS_FLOAT, where arguments to
Index: src/gdb/dwarf2-frame.c
===================================================================
--- src.orig/gdb/dwarf2-frame.c	2008-04-30 17:17:53.000000000 -0400
+++ src/gdb/dwarf2-frame.c	2008-05-01 10:22:48.000000000 -0400
@@ -1139,6 +1139,9 @@ dwarf2_frame_prev_register (struct frame
       addr += get_frame_register_unsigned (this_frame, regnum);
       return frame_unwind_got_address (this_frame, regnum, addr);
 
+    case DWARF2_FRAME_REG_FN:
+      return cache->reg[regnum].loc.fn (this_frame, this_cache, regnum);
+
     default:
       internal_error (__FILE__, __LINE__, _("Unknown register rule."));
     }
Index: src/gdb/dwarf2-frame.h
===================================================================
--- src.orig/gdb/dwarf2-frame.h	2008-04-30 17:17:53.000000000 -0400
+++ src/gdb/dwarf2-frame.h	2008-05-01 10:22:05.000000000 -0400
@@ -55,6 +55,7 @@ enum dwarf2_frame_reg_rule
 
   /* These aren't defined by the DWARF2 CFI specification, but are
      used internally by GDB.  */
+  DWARF2_FRAME_REG_FN,		/* Call a registered function.  */
   DWARF2_FRAME_REG_RA,		/* Return Address.  */
   DWARF2_FRAME_REG_RA_OFFSET,	/* Return Address with offset.  */
   DWARF2_FRAME_REG_CFA,		/* Call Frame Address.  */
@@ -71,6 +72,8 @@ struct dwarf2_frame_state_reg
     LONGEST offset;
     ULONGEST reg;
     unsigned char *exp;
+    struct value *(*fn) (struct frame_info *this_frame, void **this_cache,
+			 int regnum);
   } loc;
   ULONGEST exp_len;
   enum dwarf2_frame_reg_rule how;
Index: src/gdb/testsuite/gdb.arch/thumb-prologue.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.arch/thumb-prologue.exp	2008-01-01 17:10:10.000000000 -0500
+++ src/gdb/testsuite/gdb.arch/thumb-prologue.exp	2008-05-01 10:00:54.000000000 -0400
@@ -57,5 +57,5 @@ gdb_test "backtrace 10" \
 	"backtrace in TPCS"
 
 gdb_test "info frame" \
-	".*Saved registers:.*r7 at.*r10 at.*r11 at.*lr at.*pc at .*" \
+	".*Saved registers:.*r7 at.*r10 at.*r11 at.*lr at.*" \
 	"saved registers in TPCS"



More information about the Gdb-patches mailing list