* arm-linux-tdep.h (ARM_CPSR_REGNUM): Delete definition.
authorDaniel Jacobowitz <drow@false.org>
Thu, 1 May 2008 18:30:51 +0000 (18:30 +0000)
committerDaniel Jacobowitz <drow@false.org>
Thu, 1 May 2008 18:30:51 +0000 (18:30 +0000)
* 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.

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

gdb/ChangeLog
gdb/arm-linux-tdep.h
gdb/arm-tdep.c
gdb/arm-tdep.h
gdb/dwarf2-frame.c
gdb/dwarf2-frame.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.arch/thumb-prologue.exp

index 4818d15..f604794 100644 (file)
@@ -1,3 +1,26 @@
+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.
+
 2008-05-01  Nick Roberts  <nickrob@snap.net.nz>
 
        * exec.c (print_section_info): Add missing '\n'.
index b8ebf65..5b94314 100644 (file)
@@ -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 7760e8a..080c0f5 100644 (file)
@@ -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 *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 *gdbarch, CORE_ADDR pc)
 
 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 *this_frame,
   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 *this_frame,
   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 *this_frame,
   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_info *this_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_info *this_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, struct frame_info *this_frame)
   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 *frame, CORE_ADDR pc)
   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, CORE_ADDR pc)
       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 info, struct gdbarch_list *arches)
   /* 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 eaad493..80c4bda 100644 (file)
@@ -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 a4819f7..b90f976 100644 (file)
@@ -1139,6 +1139,9 @@ dwarf2_frame_prev_register (struct frame_info *this_frame, void **this_cache,
       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 dc9f003..aeff54a 100644 (file)
@@ -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 bbda0f5..85f6372 100644 (file)
@@ -1,3 +1,7 @@
+2007-05-01  Daniel Jacobowitz  <dan@codesourcery.com>
+
+       * gdb.arch/thumb-prologue.exp: Do not expect a saved PC.
+
 2008-05-01  Joel Brobecker  <brobecker@adacore.com>
 
        * gdb.base/info-target.exp: New testcase.
index b83f578..4669f31 100644 (file)
@@ -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"