From 369c397ba4feb2e14ba5ce9ad34b95b886382273 Mon Sep 17 00:00:00 2001 From: Joel Brobecker Date: Mon, 27 Jun 2011 16:36:00 +0000 Subject: [PATCH] Add support for single register window model on SPARC 2011-06-27 Eric Botcazou * sparc-tdep.h (struct sparc_frame_cache): Add frame_offset, saved_regs_mask and copied_regs_mask fields. (sparc_record_save_insn): New prototype. * sparc-tdep.c (sparc_alloc_frame_cache): Initialize the new fields. (sparc_record_save_insn): New function. (sparc_analyze_prologue): Add head comment. Recognize store insns of call-saved registers. Use OFFSET consistently. Recognize flat frames and cache their settings. (sparc32_skip_prologue): Handle flat frames. (sparc_frame_cache): Add frame_offset to the base address. (sparc32_frame_cache): Adjust to new frame description. (sparc32_frame_prev_register): Likewise. * sparc64-tdep.c (sparc64_frame_prev_register): Likewise. * sparc-sol2-tdep.c (sparc32_sol2_sigtramp_frame_cache): Likewise. * sparc64-sol2-tdep.c (sparc64_sol2_sigtramp_frame_cache): Likewise. * sparcnbsd-tdep.c (sparc32nbsd_sigcontext_frame_cache): Force the frame by calling sparc_record_save_insn. * sparc64nbsd-tdep.c (sparc64nbsd_sigcontext_frame_cache): Likewise. * sparcobsd-tdep.c (sparc32obsd_sigtramp_frame_cache): Likewise. * sparc64obsd-tdep.c (sparc64obsd_frame_cache): Likewise. --- gdb/ChangeLog | 23 ++++++ gdb/sparc-sol2-tdep.c | 3 +- gdb/sparc-tdep.c | 169 ++++++++++++++++++++++++++++++++++------ gdb/sparc-tdep.h | 13 ++++ gdb/sparc64-sol2-tdep.c | 3 +- gdb/sparc64-tdep.c | 17 ++-- gdb/sparc64nbsd-tdep.c | 2 +- gdb/sparc64obsd-tdep.c | 2 +- gdb/sparcnbsd-tdep.c | 2 +- gdb/sparcobsd-tdep.c | 2 +- 10 files changed, 198 insertions(+), 38 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index cd62b1c941..b7c5e20cda 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,26 @@ +2011-06-27 Eric Botcazou + + * sparc-tdep.h (struct sparc_frame_cache): Add frame_offset, + saved_regs_mask and copied_regs_mask fields. + (sparc_record_save_insn): New prototype. + * sparc-tdep.c (sparc_alloc_frame_cache): Initialize the new fields. + (sparc_record_save_insn): New function. + (sparc_analyze_prologue): Add head comment. Recognize store insns + of call-saved registers. Use OFFSET consistently. Recognize flat + frames and cache their settings. + (sparc32_skip_prologue): Handle flat frames. + (sparc_frame_cache): Add frame_offset to the base address. + (sparc32_frame_cache): Adjust to new frame description. + (sparc32_frame_prev_register): Likewise. + * sparc64-tdep.c (sparc64_frame_prev_register): Likewise. + * sparc-sol2-tdep.c (sparc32_sol2_sigtramp_frame_cache): Likewise. + * sparc64-sol2-tdep.c (sparc64_sol2_sigtramp_frame_cache): Likewise. + * sparcnbsd-tdep.c (sparc32nbsd_sigcontext_frame_cache): Force the + frame by calling sparc_record_save_insn. + * sparc64nbsd-tdep.c (sparc64nbsd_sigcontext_frame_cache): Likewise. + * sparcobsd-tdep.c (sparc32obsd_sigtramp_frame_cache): Likewise. + * sparc64obsd-tdep.c (sparc64obsd_frame_cache): Likewise. + 2011-06-27 Tristan Gingold * dwarf2read.c (struct dwarf2_section_info): Replace was_mmapped diff --git a/gdb/sparc-sol2-tdep.c b/gdb/sparc-sol2-tdep.c index a49eb7f36b..bff15de07e 100644 --- a/gdb/sparc-sol2-tdep.c +++ b/gdb/sparc-sol2-tdep.c @@ -93,7 +93,8 @@ sparc32_sol2_sigtramp_frame_cache (struct frame_info *this_frame, /* The third argument is a pointer to an instance of `ucontext_t', which has a member `uc_mcontext' that contains the saved registers. */ - regnum = (cache->frameless_p ? SPARC_O2_REGNUM : SPARC_I2_REGNUM); + regnum = + (cache->copied_regs_mask & 0x04) ? SPARC_I2_REGNUM : SPARC_O2_REGNUM; mcontext_addr = get_frame_register_unsigned (this_frame, regnum) + 40; cache->saved_regs[SPARC32_PSR_REGNUM].addr = mcontext_addr + 0 * 4; diff --git a/gdb/sparc-tdep.c b/gdb/sparc-tdep.c index 42c13ac5a2..faa7b3a4ad 100644 --- a/gdb/sparc-tdep.c +++ b/gdb/sparc-tdep.c @@ -592,7 +592,9 @@ sparc_alloc_frame_cache (void) /* Frameless until proven otherwise. */ cache->frameless_p = 1; - + cache->frame_offset = 0; + cache->saved_regs_mask = 0; + cache->copied_regs_mask = 0; cache->struct_return_p = 0; return cache; @@ -784,6 +786,31 @@ sparc_skip_stack_check (const CORE_ADDR start_pc) return start_pc; } +/* Record the effect of a SAVE instruction on CACHE. */ + +void +sparc_record_save_insn (struct sparc_frame_cache *cache) +{ + /* The frame is set up. */ + cache->frameless_p = 0; + + /* The frame pointer contains the CFA. */ + cache->frame_offset = 0; + + /* The `local' and `in' registers are all saved. */ + cache->saved_regs_mask = 0xffff; + + /* The `out' registers are all renamed. */ + cache->copied_regs_mask = 0xff; +} + +/* Do a full analysis of the prologue at PC and update CACHE accordingly. + Bail out early if CURRENT_PC is reached. Return the address where + the analysis stopped. + + We handle both the traditional register window model and the single + register window (aka flat) model. */ + CORE_ADDR sparc_analyze_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, CORE_ADDR current_pc, struct sparc_frame_cache *cache) @@ -813,13 +840,40 @@ sparc_analyze_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, insn = sparc_fetch_instruction (pc); + /* Recognize store insns and record their sources. */ + while (X_OP (insn) == 3 + && (X_OP3 (insn) == 0x4 /* stw */ + || X_OP3 (insn) == 0x7 /* std */ + || X_OP3 (insn) == 0xe) /* stx */ + && X_RS1 (insn) == SPARC_SP_REGNUM) + { + int regnum = X_RD (insn); + + /* Recognize stores into the corresponding stack slots. */ + if (regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM + && ((X_I (insn) + && X_SIMM13 (insn) == (X_OP3 (insn) == 0xe + ? (regnum - SPARC_L0_REGNUM) * 8 + BIAS + : (regnum - SPARC_L0_REGNUM) * 4)) + || (!X_I (insn) && regnum == SPARC_L0_REGNUM))) + { + cache->saved_regs_mask |= (1 << (regnum - SPARC_L0_REGNUM)); + if (X_OP3 (insn) == 0x7) + cache->saved_regs_mask |= (1 << (regnum + 1 - SPARC_L0_REGNUM)); + } + + offset += 4; + + insn = sparc_fetch_instruction (pc + offset); + } + /* Recognize a SETHI insn and record its destination. */ if (X_OP (insn) == 0 && X_OP2 (insn) == 0x04) { dest = X_RD (insn); offset += 4; - insn = sparc_fetch_instruction (pc + 4); + insn = sparc_fetch_instruction (pc + offset); } /* Allow for an arithmetic operation on DEST or %g1. */ @@ -828,14 +882,62 @@ sparc_analyze_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, { offset += 4; - insn = sparc_fetch_instruction (pc + 8); + insn = sparc_fetch_instruction (pc + offset); } /* Check for the SAVE instruction that sets up the frame. */ if (X_OP (insn) == 2 && X_OP3 (insn) == 0x3c) { - cache->frameless_p = 0; - return pc + offset + 4; + sparc_record_save_insn (cache); + offset += 4; + return pc + offset; + } + + /* Check for an arithmetic operation on %sp. */ + if (X_OP (insn) == 2 + && (X_OP3 (insn) == 0 || X_OP3 (insn) == 0x4) + && X_RS1 (insn) == SPARC_SP_REGNUM + && X_RD (insn) == SPARC_SP_REGNUM) + { + if (X_I (insn)) + { + cache->frame_offset = X_SIMM13 (insn); + if (X_OP3 (insn) == 0) + cache->frame_offset = -cache->frame_offset; + } + offset += 4; + + insn = sparc_fetch_instruction (pc + offset); + + /* Check for an arithmetic operation that sets up the frame. */ + if (X_OP (insn) == 2 + && (X_OP3 (insn) == 0 || X_OP3 (insn) == 0x4) + && X_RS1 (insn) == SPARC_SP_REGNUM + && X_RD (insn) == SPARC_FP_REGNUM) + { + cache->frameless_p = 0; + cache->frame_offset = 0; + /* We could check that the amount subtracted to %sp above is the + same as the one added here, but this seems superfluous. */ + cache->copied_regs_mask |= 0x40; + offset += 4; + + insn = sparc_fetch_instruction (pc + offset); + } + + /* Check for a move (or) operation that copies the return register. */ + if (X_OP (insn) == 2 + && X_OP3 (insn) == 0x2 + && !X_I (insn) + && X_RS1 (insn) == SPARC_G0_REGNUM + && X_RS2 (insn) == SPARC_O7_REGNUM + && X_RD (insn) == SPARC_I7_REGNUM) + { + cache->copied_regs_mask |= 0x80; + offset += 4; + } + + return pc + offset; } return pc; @@ -878,21 +980,36 @@ sparc32_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc) indeed what GCC seems to be doing. In that case GCC will generate debug information that points to the stack slots instead of the registers, so we should consider the instructions that - write out these incoming arguments onto the stack. Of course we - only need to do this if we have a stack frame. */ + write out these incoming arguments onto the stack. */ - while (!cache.frameless_p) + while (1) { unsigned long insn = sparc_fetch_instruction (start_pc); - /* Recognize instructions that store incoming arguments in - %i0...%i5 into the corresponding stack slot. */ - if (X_OP (insn) == 3 && (X_OP3 (insn) & 0x3c) == 0x04 && X_I (insn) - && (X_RD (insn) >= 24 && X_RD (insn) <= 29) && X_RS1 (insn) == 30 - && X_SIMM13 (insn) == 68 + (X_RD (insn) - 24) * 4) + /* Recognize instructions that store incoming arguments into the + corresponding stack slots. */ + if (X_OP (insn) == 3 && (X_OP3 (insn) & 0x3c) == 0x04 + && X_I (insn) && X_RS1 (insn) == SPARC_FP_REGNUM) { - start_pc += 4; - continue; + int regnum = X_RD (insn); + + /* Case of arguments still in %o[0..5]. */ + if (regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O5_REGNUM + && !(cache.copied_regs_mask & (1 << (regnum - SPARC_O0_REGNUM))) + && X_SIMM13 (insn) == 68 + (regnum - SPARC_O0_REGNUM) * 4) + { + start_pc += 4; + continue; + } + + /* Case of arguments copied into %i[0..5]. */ + if (regnum >= SPARC_I0_REGNUM && regnum <= SPARC_I5_REGNUM + && (cache.copied_regs_mask & (1 << (regnum - SPARC_I0_REGNUM))) + && X_SIMM13 (insn) == 68 + (regnum - SPARC_I0_REGNUM) * 4) + { + start_pc += 4; + continue; + } } break; @@ -935,6 +1052,8 @@ sparc_frame_cache (struct frame_info *this_frame, void **this_cache) get_frame_register_unsigned (this_frame, SPARC_FP_REGNUM); } + cache->base += cache->frame_offset; + if (cache->base & 1) cache->base += BIAS; @@ -983,7 +1102,8 @@ sparc32_frame_cache (struct frame_info *this_frame, void **this_cache) an "unimp" instruction. If it is, then it is a struct-return function. */ CORE_ADDR pc; - int regnum = cache->frameless_p ? SPARC_O7_REGNUM : SPARC_I7_REGNUM; + int regnum = + (cache->copied_regs_mask & 0x80) ? SPARC_I7_REGNUM : SPARC_O7_REGNUM; pc = get_frame_register_unsigned (this_frame, regnum) + 8; if (sparc_is_unimp_insn (pc)) @@ -1025,7 +1145,8 @@ sparc32_frame_prev_register (struct frame_info *this_frame, if (cache->struct_return_p) pc += 4; - regnum = cache->frameless_p ? SPARC_O7_REGNUM : SPARC_I7_REGNUM; + regnum = + (cache->copied_regs_mask & 0x80) ? SPARC_I7_REGNUM : SPARC_O7_REGNUM; pc += get_frame_register_unsigned (this_frame, regnum) + 8; return frame_unwind_got_constant (this_frame, regnum, pc); } @@ -1045,20 +1166,20 @@ sparc32_frame_prev_register (struct frame_info *this_frame, } } - /* The previous frame's `local' and `in' registers have been saved + /* The previous frame's `local' and `in' registers may have been saved in the register save area. */ - if (!cache->frameless_p - && regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM) + if (regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM + && (cache->saved_regs_mask & (1 << (regnum - SPARC_L0_REGNUM)))) { CORE_ADDR addr = cache->base + (regnum - SPARC_L0_REGNUM) * 4; return frame_unwind_got_memory (this_frame, regnum, addr); } - /* The previous frame's `out' registers are accessible as the - current frame's `in' registers. */ - if (!cache->frameless_p - && regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O7_REGNUM) + /* The previous frame's `out' registers may be accessible as the current + frame's `in' registers. */ + if (regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O7_REGNUM + && (cache->copied_regs_mask & (1 << (regnum - SPARC_O0_REGNUM)))) regnum += (SPARC_I0_REGNUM - SPARC_O0_REGNUM); return frame_unwind_got_register (this_frame, regnum, regnum); diff --git a/gdb/sparc-tdep.h b/gdb/sparc-tdep.h index 0eb7afaeeb..d4e8b067c6 100644 --- a/gdb/sparc-tdep.h +++ b/gdb/sparc-tdep.h @@ -146,6 +146,15 @@ struct sparc_frame_cache /* Do we have a frame? */ int frameless_p; + /* The offset from the base register to the CFA. */ + int frame_offset; + + /* Mask of `local' and `in' registers saved in the register save area. */ + unsigned short int saved_regs_mask; + + /* Mask of `out' registers copied or renamed to their `in' sibling. */ + unsigned char copied_regs_mask; + /* Do we have a Structure, Union or Quad-Precision return value? */ int struct_return_p; @@ -159,6 +168,10 @@ extern unsigned long sparc_fetch_instruction (CORE_ADDR pc); /* Fetch StackGhost Per-Process XOR cookie. */ extern ULONGEST sparc_fetch_wcookie (struct gdbarch *gdbarch); +/* Record the effect of a SAVE instruction on CACHE. */ +extern void sparc_record_save_insn (struct sparc_frame_cache *cache); + +/* Do a full analysis of the prologue at PC and update CACHE accordingly. */ extern CORE_ADDR sparc_analyze_prologue (struct gdbarch *gdbarch, CORE_ADDR pc, CORE_ADDR current_pc, struct sparc_frame_cache *cache); diff --git a/gdb/sparc64-sol2-tdep.c b/gdb/sparc64-sol2-tdep.c index d11619d869..6e108d15a7 100644 --- a/gdb/sparc64-sol2-tdep.c +++ b/gdb/sparc64-sol2-tdep.c @@ -67,7 +67,8 @@ sparc64_sol2_sigtramp_frame_cache (struct frame_info *this_frame, /* The third argument is a pointer to an instance of `ucontext_t', which has a member `uc_mcontext' that contains the saved registers. */ - regnum = (cache->frameless_p ? SPARC_O2_REGNUM : SPARC_I2_REGNUM); + regnum = + (cache->copied_regs_mask & 0x04) ? SPARC_I2_REGNUM : SPARC_O2_REGNUM; mcontext_addr = get_frame_register_unsigned (this_frame, regnum) + 64; cache->saved_regs[SPARC64_CCR_REGNUM].addr = mcontext_addr + 0 * 8; diff --git a/gdb/sparc64-tdep.c b/gdb/sparc64-tdep.c index 6efa2202a5..0430ecf22a 100644 --- a/gdb/sparc64-tdep.c +++ b/gdb/sparc64-tdep.c @@ -520,7 +520,8 @@ sparc64_frame_prev_register (struct frame_info *this_frame, void **this_cache, { CORE_ADDR pc = (regnum == SPARC64_NPC_REGNUM) ? 4 : 0; - regnum = cache->frameless_p ? SPARC_O7_REGNUM : SPARC_I7_REGNUM; + regnum = + (cache->copied_regs_mask & 0x80) ? SPARC_I7_REGNUM : SPARC_O7_REGNUM; pc += get_frame_register_unsigned (this_frame, regnum) + 8; return frame_unwind_got_constant (this_frame, regnum, pc); } @@ -540,20 +541,20 @@ sparc64_frame_prev_register (struct frame_info *this_frame, void **this_cache, } } - /* The previous frame's `local' and `in' registers have been saved + /* The previous frame's `local' and `in' registers may have been saved in the register save area. */ - if (!cache->frameless_p - && regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM) + if (regnum >= SPARC_L0_REGNUM && regnum <= SPARC_I7_REGNUM + && (cache->saved_regs_mask & (1 << (regnum - SPARC_L0_REGNUM)))) { CORE_ADDR addr = cache->base + (regnum - SPARC_L0_REGNUM) * 8; return frame_unwind_got_memory (this_frame, regnum, addr); } - /* The previous frame's `out' registers are accessable as the - current frame's `in' registers. */ - if (!cache->frameless_p - && regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O7_REGNUM) + /* The previous frame's `out' registers may be accessible as the current + frame's `in' registers. */ + if (regnum >= SPARC_O0_REGNUM && regnum <= SPARC_O7_REGNUM + && (cache->copied_regs_mask & (1 << (regnum - SPARC_O0_REGNUM)))) regnum += (SPARC_I0_REGNUM - SPARC_O0_REGNUM); return frame_unwind_got_register (this_frame, regnum, regnum); diff --git a/gdb/sparc64nbsd-tdep.c b/gdb/sparc64nbsd-tdep.c index 5d6c27c17f..94e0f3e83c 100644 --- a/gdb/sparc64nbsd-tdep.c +++ b/gdb/sparc64nbsd-tdep.c @@ -173,7 +173,7 @@ sparc64nbsd_sigcontext_frame_cache (struct frame_info *this_frame, /* Since we couldn't find the frame's function, the cache was initialized under the assumption that we're frameless. */ - cache->frameless_p = 0; + sparc_record_save_insn (cache); addr = get_frame_register_unsigned (this_frame, SPARC_FP_REGNUM); if (addr & 1) addr += BIAS; diff --git a/gdb/sparc64obsd-tdep.c b/gdb/sparc64obsd-tdep.c index adc16a18a4..c7746dcfba 100644 --- a/gdb/sparc64obsd-tdep.c +++ b/gdb/sparc64obsd-tdep.c @@ -142,7 +142,7 @@ sparc64obsd_frame_cache (struct frame_info *this_frame, void **this_cache) /* Since we couldn't find the frame's function, the cache was initialized under the assumption that we're frameless. */ - cache->frameless_p = 0; + sparc_record_save_insn (cache); addr = get_frame_register_unsigned (this_frame, SPARC_FP_REGNUM); if (addr & 1) addr += BIAS; diff --git a/gdb/sparcnbsd-tdep.c b/gdb/sparcnbsd-tdep.c index 8bbaa4bc9d..594dd03015 100644 --- a/gdb/sparcnbsd-tdep.c +++ b/gdb/sparcnbsd-tdep.c @@ -202,7 +202,7 @@ sparc32nbsd_sigcontext_frame_cache (struct frame_info *this_frame, /* Since we couldn't find the frame's function, the cache was initialized under the assumption that we're frameless. */ - cache->frameless_p = 0; + sparc_record_save_insn (cache); addr = get_frame_register_unsigned (this_frame, SPARC_FP_REGNUM); cache->base = addr; } diff --git a/gdb/sparcobsd-tdep.c b/gdb/sparcobsd-tdep.c index b9e954001a..52f9d70c54 100644 --- a/gdb/sparcobsd-tdep.c +++ b/gdb/sparcobsd-tdep.c @@ -91,7 +91,7 @@ sparc32obsd_sigtramp_frame_cache (struct frame_info *this_frame, /* Since we couldn't find the frame's function, the cache was initialized under the assumption that we're frameless. */ - cache->frameless_p = 0; + sparc_record_save_insn (cache); addr = get_frame_register_unsigned (this_frame, SPARC_FP_REGNUM); cache->base = addr; } -- 2.34.1