aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Jacobowitz <drow@false.org>2008-05-01 18:30:51 +0000
committerDaniel Jacobowitz <drow@false.org>2008-05-01 18:30:51 +0000
commitb39cc96258b1a5fb23014fb3712f24a7578e1070 (patch)
tree09535e8c6262da6fd31dd630d09e72c8aeacbf94
parent36d520f5f660dd788e0bcc5471439af7e5aca7bc (diff)
downloadfsf-binutils-gdb-b39cc96258b1a5fb23014fb3712f24a7578e1070.zip
fsf-binutils-gdb-b39cc96258b1a5fb23014fb3712f24a7578e1070.tar.gz
fsf-binutils-gdb-b39cc96258b1a5fb23014fb3712f24a7578e1070.tar.bz2
* 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. * gdb.arch/thumb-prologue.exp: Do not expect a saved PC.
-rw-r--r--gdb/ChangeLog23
-rw-r--r--gdb/arm-linux-tdep.h2
-rw-r--r--gdb/arm-tdep.c147
-rw-r--r--gdb/arm-tdep.h3
-rw-r--r--gdb/dwarf2-frame.c3
-rw-r--r--gdb/dwarf2-frame.h3
-rw-r--r--gdb/testsuite/ChangeLog4
-rw-r--r--gdb/testsuite/gdb.arch/thumb-prologue.exp2
8 files changed, 153 insertions, 34 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 4818d15..f604794 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -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'.
diff --git a/gdb/arm-linux-tdep.h b/gdb/arm-linux-tdep.h
index b8ebf65..5b94314 100644
--- a/gdb/arm-linux-tdep.h
+++ b/gdb/arm-linux-tdep.h
@@ -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)
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 7760e8a..080c0f5 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -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);
diff --git a/gdb/arm-tdep.h b/gdb/arm-tdep.h
index eaad493..80c4bda 100644
--- a/gdb/arm-tdep.h
+++ b/gdb/arm-tdep.h
@@ -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
diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c
index a4819f7..b90f976 100644
--- a/gdb/dwarf2-frame.c
+++ b/gdb/dwarf2-frame.c
@@ -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."));
}
diff --git a/gdb/dwarf2-frame.h b/gdb/dwarf2-frame.h
index dc9f003..aeff54a 100644
--- a/gdb/dwarf2-frame.h
+++ b/gdb/dwarf2-frame.h
@@ -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;
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index bbda0f5..85f6372 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -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.
diff --git a/gdb/testsuite/gdb.arch/thumb-prologue.exp b/gdb/testsuite/gdb.arch/thumb-prologue.exp
index b83f578..4669f31 100644
--- a/gdb/testsuite/gdb.arch/thumb-prologue.exp
+++ b/gdb/testsuite/gdb.arch/thumb-prologue.exp
@@ -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"