diff options
-rw-r--r-- | gdb/ChangeLog | 23 | ||||
-rw-r--r-- | gdb/arm-linux-tdep.h | 2 | ||||
-rw-r--r-- | gdb/arm-tdep.c | 147 | ||||
-rw-r--r-- | gdb/arm-tdep.h | 3 | ||||
-rw-r--r-- | gdb/dwarf2-frame.c | 3 | ||||
-rw-r--r-- | gdb/dwarf2-frame.h | 3 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 4 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/thumb-prologue.exp | 2 |
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" |