diff options
author | Daniel Jacobowitz <drow@false.org> | 2008-05-01 18:30:51 +0000 |
---|---|---|
committer | Daniel Jacobowitz <drow@false.org> | 2008-05-01 18:30:51 +0000 |
commit | b39cc96258b1a5fb23014fb3712f24a7578e1070 (patch) | |
tree | 09535e8c6262da6fd31dd630d09e72c8aeacbf94 /gdb/arm-tdep.c | |
parent | 36d520f5f660dd788e0bcc5471439af7e5aca7bc (diff) | |
download | gdb-b39cc96258b1a5fb23014fb3712f24a7578e1070.zip gdb-b39cc96258b1a5fb23014fb3712f24a7578e1070.tar.gz 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.
Diffstat (limited to 'gdb/arm-tdep.c')
-rw-r--r-- | gdb/arm-tdep.c | 147 |
1 files changed, 116 insertions, 31 deletions
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); |