diff options
Diffstat (limited to 'arch/sh/kernel/dwarf.c')
-rw-r--r-- | arch/sh/kernel/dwarf.c | 44 |
1 files changed, 35 insertions, 9 deletions
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) { |