aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/sh/include/asm/dwarf.h5
-rw-r--r--arch/sh/kernel/dwarf.c44
2 files changed, 35 insertions, 14 deletions
diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h
index 23eba12880b3..60b180728d8d 100644
--- a/arch/sh/include/asm/dwarf.h
+++ b/arch/sh/include/asm/dwarf.h
@@ -193,11 +193,6 @@
*/
#define DWARF_ARCH_RA_REG 17
-/*
- * At what offset into dwarf_unwind_stack() is DWARF_ARCH_RA_REG setup?
- */
-#define DWARF_ARCH_UNWIND_OFFSET 0x20
-
#ifndef __ASSEMBLY__
/*
* Read either the frame pointer (r14) or the stack pointer (r15).
diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c
index 49d039f19426..83f3cc92549f 100644
--- a/arch/sh/kernel/dwarf.c
+++ b/arch/sh/kernel/dwarf.c
@@ -330,6 +330,7 @@ struct dwarf_fde *dwarf_lookup_fde(unsigned long pc)
* @fde: the FDE for this function
* @frame: the instructions calculate the CFA for this frame
* @pc: the program counter of the address we're interested in
+ * @define_ra: keep executing insns until the return addr reg is defined?
*
* Execute the Call Frame instruction sequence starting at
* @insn_start and ending at @insn_end. The instructions describe
@@ -341,17 +342,36 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start,
struct dwarf_cie *cie,
struct dwarf_fde *fde,
struct dwarf_frame *frame,
- unsigned long pc)
+ unsigned long pc,
+ bool define_ra)
{
unsigned char insn;
unsigned char *current_insn;
unsigned int count, delta, reg, expr_len, offset;
+ bool seen_ra_reg;
current_insn = insn_start;
- while (current_insn < insn_end && frame->pc <= pc) {
+ /*
+ * If we're executing instructions for the dwarf_unwind_stack()
+ * FDE we need to keep executing instructions until the value of
+ * DWARF_ARCH_RA_REG is defined. See the comment in
+ * dwarf_unwind_stack() for more details.
+ */
+ if (define_ra)
+ seen_ra_reg = false;
+ else
+ seen_ra_reg = true;
+
+ while (current_insn < insn_end && (frame->pc <= pc || !seen_ra_reg) ) {
insn = __raw_readb(current_insn++);
+ if (!seen_ra_reg) {
+ if (frame->num_regs >= DWARF_ARCH_RA_REG &&
+ frame->regs[DWARF_ARCH_RA_REG].flags)
+ seen_ra_reg = true;
+ }
+
/*
* Firstly, handle the opcodes that embed their operands
* in the instructions.
@@ -490,20 +510,25 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc,
struct dwarf_fde *fde;
unsigned long addr;
int i, offset;
+ bool define_ra = false;
/*
* If this is the first invocation of this recursive function we
* need get the contents of a physical register to get the CFA
* in order to begin the virtual unwinding of the stack.
*
- * The constant DWARF_ARCH_UNWIND_OFFSET is added to the address of
- * this function because the return address register
- * (DWARF_ARCH_RA_REG) will probably not be initialised until a
- * few instructions into the prologue.
+ * Setting "define_ra" to true indictates that we want
+ * dwarf_cfa_execute_insns() to continue executing instructions
+ * until we know how to calculate the value of DWARF_ARCH_RA_REG
+ * (which we need in order to kick off the whole unwinding
+ * process).
+ *
+ * NOTE: the return address is guaranteed to be setup by the
+ * time this function makes its first function call.
*/
if (!pc && !prev) {
pc = (unsigned long)&dwarf_unwind_stack;
- pc += DWARF_ARCH_UNWIND_OFFSET;
+ define_ra = true;
}
frame = kzalloc(sizeof(*frame), GFP_KERNEL);
@@ -539,11 +564,12 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc,
/* CIE initial instructions */
dwarf_cfa_execute_insns(cie->initial_instructions,
- cie->instructions_end, cie, fde, frame, pc);
+ cie->instructions_end, cie, fde,
+ frame, pc, false);
/* FDE instructions */
dwarf_cfa_execute_insns(fde->instructions, fde->end, cie,
- fde, frame, pc);
+ fde, frame, pc, define_ra);
/* Calculate the CFA */
switch (frame->flags) {