LoongArch: Use correct sp value to get graph addr in stack unwinders
authorJinyang He <hejinyang@loongson.cn>
Tue, 17 Jan 2023 03:42:16 +0000 (11:42 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Tue, 17 Jan 2023 03:42:16 +0000 (11:42 +0800)
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>
arch/loongarch/include/asm/ftrace.h
arch/loongarch/include/asm/unwind.h
arch/loongarch/kernel/unwind_guess.c
arch/loongarch/kernel/unwind_prologue.c

index 90f9d3399b2aafe62a495e93f0735898d4b28715..3418d32d4fc7d2d24a96563132d42f2bcc012d38 100644 (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 */
index f2b52b9ea93d2862d69e9e7f1075f6e1bcfee619..834ba6bd15d294dfd694d06cea45f727c2a40801 100644 (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 */
index 9ef5f2a47f8808d48d7238e651bd95f2011ad0e3..e7e637bdba17e9a84af11188617314194d002ef6 100644 (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;
                }
 
index 613545ce6a97662f902a5bfdba75b8ac90744aa5..463ba2391bdf4f88e9634116e66e04565736f78d 100644 (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;