From aee16625b193916d7616c37a1d20a703d9b3dd15 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 10 Aug 2017 13:27:37 -0500 Subject: [PATCH] MIPS: Fix issues in backtraces 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 : ffffffff80b885a0: c8a00003 bbit0 a1,0x0,ffffffff80b885b0 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 : 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 ffffffff80806ff8: ffbf0028 sd ra,40(sp) Signed-off-by: Corey Minyard 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 --- arch/mips/kernel/process.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 5351e1f3950d..a1d930a8705b 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -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;