MIPS: Fix issues in backtraces
authorCorey Minyard <cminyard@mvista.com>
Thu, 10 Aug 2017 18:27:37 +0000 (13:27 -0500)
committerRalf Baechle <ralf@linux-mips.org>
Wed, 6 Sep 2017 09:01:00 +0000 (11:01 +0200)
I saw two problems when doing backtraces:

The compiler was putting a "fast return" at the top of some
functions, before it set up the frame.  The backtrace code
would stop when it saw a jump instruction, so it would never
get to the stack frame setup and would thus misinterpret it.
To fix this, don't look for jump instructions until the
frame setup has been seen.

The assembly code here is:

ffffffff80b885a0 <serial8250_handle_irq>:
ffffffff80b885a0:       c8a00003        bbit0   a1,0x0,ffffffff80b885b0 <serial8250_handle_irq+0x10>
ffffffff80b885a4:       0000102d        move    v0,zero
ffffffff80b885a8:       03e00008        jr      ra
ffffffff80b885ac:       00000000        nop
ffffffff80b885b0:       67bdffd0        daddiu  sp,sp,-48
ffffffff80b885b4:       ffb00008        sd      s0,8(sp)

The second problem was the compiler was putting the last
instruction of the frame save in the delay slot of the
jump instruction.  If it saved the RA in there, the
backtrace could would miss it and misinterpret the frame.
To fix this, make sure to process the instruction after
the first jump seen.

The assembly code for this is:

ffffffff80806fd0 <plat_irq_dispatch>:
ffffffff80806fd0:       67bdffd0        daddiu  sp,sp,-48
ffffffff80806fd4:       ffb30020        sd      s3,32(sp)
ffffffff80806fd8:       24130018        li      s3,24
ffffffff80806fdc:       ffb20018        sd      s2,24(sp)
ffffffff80806fe0:       3c12811c        lui     s2,0x811c
ffffffff80806fe4:       ffb10010        sd      s1,16(sp)
ffffffff80806fe8:       3c11811c        lui     s1,0x811c
ffffffff80806fec:       ffb00008        sd      s0,8(sp)
ffffffff80806ff0:       3c10811c        lui     s0,0x811c
ffffffff80806ff4:       08201c03        j       ffffffff8080700c <plat_irq_dispa
tch+0x3c>
ffffffff80806ff8:       ffbf0028        sd      ra,40(sp)

Signed-off-by: Corey Minyard <cminyard@mvista.com>
Cc: linux-mips@linux-mips.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/16992/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/kernel/process.c

index 5351e1f..a1d930a 100644 (file)
@@ -349,6 +349,7 @@ static int get_frame_info(struct mips_frame_info *info)
        union mips_instruction insn, *ip, *ip_end;
        const unsigned int max_insns = 128;
        unsigned int i;
+       bool saw_jump = false;
 
        info->pc_offset = -1;
        info->frame_size = 0;
@@ -370,9 +371,6 @@ static int get_frame_info(struct mips_frame_info *info)
                        insn.word = ip->word;
                }
 
-               if (is_jump_ins(&insn))
-                       break;
-
                if (!info->frame_size) {
                        if (is_sp_move_ins(&insn))
                        {
@@ -396,10 +394,28 @@ static int get_frame_info(struct mips_frame_info *info)
                                info->frame_size = - ip->i_format.simmediate;
                        }
                        continue;
+               } else if (!saw_jump && is_jump_ins(ip)) {
+                       /*
+                        * If we see a jump instruction, we are finished
+                        * with the frame save.
+                        *
+                        * Some functions can have a shortcut return at
+                        * the beginning of the function, so don't start
+                        * looking for jump instruction until we see the
+                        * frame setup.
+                        *
+                        * The RA save instruction can get put into the
+                        * delay slot of the jump instruction, so look
+                        * at the next instruction, too.
+                        */
+                       saw_jump = true;
+                       continue;
                }
                if (info->pc_offset == -1 &&
                    is_ra_save_ins(&insn, &info->pc_offset))
                        break;
+               if (saw_jump)
+                       break;
        }
        if (info->frame_size && info->pc_offset >= 0) /* nested */
                return 0;