LoongArch: Use correct sp value to get graph addr in stack unwinders

The stack frame when function_graph enable like follows,

---------  <- function sp_on_entry
    |
    |
    |
 FAKE_RA   <- sp_on_entry - sizeof(pt_regs) + PT_R1
    |
---------  <- sp_on_entry - sizeof(pt_regs)

So if we want to get the &FAKE_RA we should get sp_on_entry first. In
the unwinder_prologue case, we can get the sp_on_entry as state->sp,
because we try to calculate each CFA and the ra saved address. But in
the unwinder_guess case, we cannot get it because we do not try to
calculate the CFA. Although LoongArch have not fixed frame, the $ra is
saved at CFA - 8 in most cases, we can try guess, too. As we store the
pc in state, we not need to dereference state->sp, too.

Signed-off-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
This commit is contained in:
Jinyang He 2023-01-17 11:42:16 +08:00 committed by Huacai Chen
parent 429a9671f2
commit 5bb8d34449
4 changed files with 20 additions and 24 deletions

View File

@ -10,8 +10,6 @@
#define FTRACE_REGS_PLT_IDX 1
#define NR_FTRACE_PLTS 2
#define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1]))
#ifdef CONFIG_FUNCTION_TRACER
#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */

View File

@ -8,7 +8,9 @@
#define _ASM_UNWIND_H
#include <linux/sched.h>
#include <linux/ftrace.h>
#include <asm/ptrace.h>
#include <asm/stacktrace.h>
enum unwinder_type {
@ -40,4 +42,12 @@ static inline bool unwind_error(struct unwind_state *state)
return state->error;
}
#define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1]))
static inline unsigned long unwind_graph_addr(struct unwind_state *state,
unsigned long pc, unsigned long cfa)
{
return ftrace_graph_ret_addr(state->task, &state->graph_idx,
pc, (unsigned long *)(cfa - GRAPH_FAKE_OFFSET));
}
#endif /* _ASM_UNWIND_H */

View File

@ -11,10 +11,8 @@ unsigned long unwind_get_return_address(struct unwind_state *state)
{
if (unwind_done(state))
return 0;
else if (state->first)
return state->pc;
return *(unsigned long *)(state->sp);
return state->pc;
}
EXPORT_SYMBOL_GPL(unwind_get_return_address);
@ -36,7 +34,7 @@ void unwind_start(struct unwind_state *state, struct task_struct *task,
state->task = task;
state->first = true;
state->pc = unwind_graph_addr(state, state->pc, state->sp);
get_stack_info(state->sp, state->task, &state->stack_info);
if (!unwind_done(state) && !__kernel_text_address(state->pc))
@ -60,9 +58,8 @@ bool unwind_next_frame(struct unwind_state *state)
state->sp < info->end;
state->sp += sizeof(unsigned long)) {
addr = *(unsigned long *)(state->sp);
state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
if (__kernel_text_address(addr))
state->pc = unwind_graph_addr(state, addr, state->sp + 8);
if (__kernel_text_address(state->pc))
return true;
}

View File

@ -21,16 +21,10 @@ static inline void unwind_state_fixup(struct unwind_state *state)
unsigned long unwind_get_return_address(struct unwind_state *state)
{
if (unwind_done(state))
return 0;
else if (state->type)
return state->pc;
else if (state->first)
return state->pc;
return *(unsigned long *)(state->sp);
return state->pc;
}
EXPORT_SYMBOL_GPL(unwind_get_return_address);
@ -43,9 +37,8 @@ static bool unwind_by_guess(struct unwind_state *state)
state->sp < info->end;
state->sp += sizeof(unsigned long)) {
addr = *(unsigned long *)(state->sp);
state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
if (__kernel_text_address(addr))
state->pc = unwind_graph_addr(state, addr, state->sp + 8);
if (__kernel_text_address(state->pc))
return true;
}
@ -166,7 +159,7 @@ void unwind_start(struct unwind_state *state, struct task_struct *task,
state->task = task;
state->first = true;
state->pc = unwind_graph_addr(state, state->pc, state->sp);
get_stack_info(state->sp, state->task, &state->stack_info);
if (!unwind_done(state) && !__kernel_text_address(state->pc))
@ -193,8 +186,7 @@ bool unwind_next_frame(struct unwind_state *state)
case UNWINDER_PROLOGUE:
if (unwind_by_prologue(state)) {
state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
state->pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
state->pc = unwind_graph_addr(state, state->pc, state->sp);
return true;
}
@ -209,8 +201,7 @@ bool unwind_next_frame(struct unwind_state *state)
state->first = true;
state->ra = regs->regs[1];
state->sp = regs->regs[3];
state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
state->pc = pc;
get_stack_info(state->sp, state->task, info);
return true;