Skip to content

Commit c5ac25e

Browse files
MQ-mengqingchenhuacai
authored andcommitted
LoongArch: Strip guess unwinder out from prologue unwinder
The prolugue unwinder rely on symbol info. When PC is not in kernel text address, it cannot find relative symbol info and it will be broken. The guess unwinder will be used in this case. And the guess unwinder code in prolugue unwinder is redundant. Strip it out and set the unwinder type in unwind_state. Make guess_unwinder::unwind_next_frame() as default way when other unwinders cannot unwind in some extreme case. Signed-off-by: Jinyang He <[email protected]> Signed-off-by: Huacai Chen <[email protected]>
1 parent 5bb8d34 commit c5ac25e

File tree

6 files changed

+129
-146
lines changed

6 files changed

+129
-146
lines changed

arch/loongarch/include/asm/unwind.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ struct unwind_state {
2727
unsigned long sp, pc, ra;
2828
};
2929

30+
bool default_next_frame(struct unwind_state *state);
31+
3032
void unwind_start(struct unwind_state *state,
3133
struct task_struct *task, struct pt_regs *regs);
3234
bool unwind_next_frame(struct unwind_state *state);
@@ -50,4 +52,31 @@ static inline unsigned long unwind_graph_addr(struct unwind_state *state,
5052
return ftrace_graph_ret_addr(state->task, &state->graph_idx,
5153
pc, (unsigned long *)(cfa - GRAPH_FAKE_OFFSET));
5254
}
55+
56+
static __always_inline void __unwind_start(struct unwind_state *state,
57+
struct task_struct *task, struct pt_regs *regs)
58+
{
59+
memset(state, 0, sizeof(*state));
60+
if (regs) {
61+
state->sp = regs->regs[3];
62+
state->pc = regs->csr_era;
63+
state->ra = regs->regs[1];
64+
} else if (task && task != current) {
65+
state->sp = thread_saved_fp(task);
66+
state->pc = thread_saved_ra(task);
67+
state->ra = 0;
68+
} else {
69+
state->sp = (unsigned long)__builtin_frame_address(0);
70+
state->pc = (unsigned long)__builtin_return_address(0);
71+
state->ra = 0;
72+
}
73+
state->task = task;
74+
get_stack_info(state->sp, state->task, &state->stack_info);
75+
state->pc = unwind_graph_addr(state, state->pc, state->sp);
76+
}
77+
78+
static __always_inline unsigned long __unwind_get_return_address(struct unwind_state *state)
79+
{
80+
return unwind_done(state) ? 0 : state->pc;
81+
}
5382
#endif /* _ASM_UNWIND_H */

arch/loongarch/kernel/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ extra-y := vmlinux.lds
88
obj-y += head.o cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \
99
traps.o irq.o idle.o process.o dma.o mem.o io.o reset.o switch.o \
1010
elf.o syscall.o signal.o time.o topology.o inst.o ptrace.o vdso.o \
11-
alternative.o unaligned.o
11+
alternative.o unaligned.o unwind.o
1212

1313
obj-$(CONFIG_ACPI) += acpi.o
1414
obj-$(CONFIG_EFI) += efi.o

arch/loongarch/kernel/traps.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,6 @@ static void show_backtrace(struct task_struct *task, const struct pt_regs *regs,
7272
if (!task)
7373
task = current;
7474

75-
if (user_mode(regs))
76-
state.type = UNWINDER_GUESS;
77-
7875
printk("%sCall Trace:", loglvl);
7976
for (unwind_start(&state, task, pregs);
8077
!unwind_done(&state); unwind_next_frame(&state)) {

arch/loongarch/kernel/unwind.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) 2022-2023 Loongson Technology Corporation Limited
4+
*/
5+
#include <linux/kernel.h>
6+
#include <linux/ftrace.h>
7+
8+
#include <asm/unwind.h>
9+
10+
bool default_next_frame(struct unwind_state *state)
11+
{
12+
struct stack_info *info = &state->stack_info;
13+
unsigned long addr;
14+
15+
if (unwind_done(state))
16+
return false;
17+
18+
do {
19+
for (state->sp += sizeof(unsigned long);
20+
state->sp < info->end; state->sp += sizeof(unsigned long)) {
21+
addr = *(unsigned long *)(state->sp);
22+
state->pc = unwind_graph_addr(state, addr, state->sp + 8);
23+
if (__kernel_text_address(state->pc))
24+
return true;
25+
}
26+
27+
state->sp = info->next_sp;
28+
29+
} while (!get_stack_info(state->sp, state->task, info));
30+
31+
return false;
32+
}

arch/loongarch/kernel/unwind_guess.c

Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,71 +2,25 @@
22
/*
33
* Copyright (C) 2022 Loongson Technology Corporation Limited
44
*/
5-
#include <linux/kernel.h>
6-
#include <linux/ftrace.h>
7-
85
#include <asm/unwind.h>
96

107
unsigned long unwind_get_return_address(struct unwind_state *state)
118
{
12-
if (unwind_done(state))
13-
return 0;
14-
15-
return state->pc;
9+
return __unwind_get_return_address(state);
1610
}
1711
EXPORT_SYMBOL_GPL(unwind_get_return_address);
1812

1913
void unwind_start(struct unwind_state *state, struct task_struct *task,
2014
struct pt_regs *regs)
2115
{
22-
memset(state, 0, sizeof(*state));
23-
24-
if (regs) {
25-
state->sp = regs->regs[3];
26-
state->pc = regs->csr_era;
27-
} else if (task && task != current) {
28-
state->sp = thread_saved_fp(task);
29-
state->pc = thread_saved_ra(task);
30-
} else {
31-
state->sp = (unsigned long)__builtin_frame_address(0);
32-
state->pc = (unsigned long)__builtin_return_address(0);
33-
}
34-
35-
state->task = task;
36-
state->first = true;
37-
state->pc = unwind_graph_addr(state, state->pc, state->sp);
38-
get_stack_info(state->sp, state->task, &state->stack_info);
39-
16+
__unwind_start(state, task, regs);
4017
if (!unwind_done(state) && !__kernel_text_address(state->pc))
4118
unwind_next_frame(state);
4219
}
4320
EXPORT_SYMBOL_GPL(unwind_start);
4421

4522
bool unwind_next_frame(struct unwind_state *state)
4623
{
47-
struct stack_info *info = &state->stack_info;
48-
unsigned long addr;
49-
50-
if (unwind_done(state))
51-
return false;
52-
53-
if (state->first)
54-
state->first = false;
55-
56-
do {
57-
for (state->sp += sizeof(unsigned long);
58-
state->sp < info->end;
59-
state->sp += sizeof(unsigned long)) {
60-
addr = *(unsigned long *)(state->sp);
61-
state->pc = unwind_graph_addr(state, addr, state->sp + 8);
62-
if (__kernel_text_address(state->pc))
63-
return true;
64-
}
65-
66-
state->sp = info->next_sp;
67-
68-
} while (!get_stack_info(state->sp, state->task, info));
69-
70-
return false;
24+
return default_next_frame(state);
7125
}
7226
EXPORT_SYMBOL_GPL(unwind_next_frame);

arch/loongarch/kernel/unwind_prologue.c

Lines changed: 64 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -19,32 +19,14 @@ static inline void unwind_state_fixup(struct unwind_state *state)
1919
#endif
2020
}
2121

22-
unsigned long unwind_get_return_address(struct unwind_state *state)
23-
{
24-
if (unwind_done(state))
25-
return 0;
26-
27-
return state->pc;
28-
}
29-
EXPORT_SYMBOL_GPL(unwind_get_return_address);
30-
31-
static bool unwind_by_guess(struct unwind_state *state)
32-
{
33-
struct stack_info *info = &state->stack_info;
34-
unsigned long addr;
35-
36-
for (state->sp += sizeof(unsigned long);
37-
state->sp < info->end;
38-
state->sp += sizeof(unsigned long)) {
39-
addr = *(unsigned long *)(state->sp);
40-
state->pc = unwind_graph_addr(state, addr, state->sp + 8);
41-
if (__kernel_text_address(state->pc))
42-
return true;
43-
}
44-
45-
return false;
46-
}
47-
22+
/*
23+
* LoongArch function prologue is like follows,
24+
* [instructions not use stack var]
25+
* addi.d sp, sp, -imm
26+
* st.d xx, sp, offset <- save callee saved regs and
27+
* st.d yy, sp, offset save ra if function is nest.
28+
* [others instructions]
29+
*/
4830
static bool unwind_by_prologue(struct unwind_state *state)
4931
{
5032
long frame_ra = -1;
@@ -91,6 +73,10 @@ static bool unwind_by_prologue(struct unwind_state *state)
9173
ip++;
9274
}
9375

76+
/*
77+
* Can't find stack alloc action, PC may be in a leaf function. Only the
78+
* first being true is reasonable, otherwise indicate analysis is broken.
79+
*/
9480
if (!frame_size) {
9581
if (state->first)
9682
goto first;
@@ -108,6 +94,7 @@ static bool unwind_by_prologue(struct unwind_state *state)
10894
ip++;
10995
}
11096

97+
/* Can't find save $ra action, PC may be in a leaf function, too. */
11198
if (frame_ra < 0) {
11299
if (state->first) {
113100
state->sp = state->sp + frame_size;
@@ -116,96 +103,48 @@ static bool unwind_by_prologue(struct unwind_state *state)
116103
return false;
117104
}
118105

119-
if (state->first)
120-
state->first = false;
121-
122106
state->pc = *(unsigned long *)(state->sp + frame_ra);
123107
state->sp = state->sp + frame_size;
124108
goto out;
125109

126110
first:
127-
state->first = false;
128-
if (state->pc == state->ra)
129-
return false;
130-
131111
state->pc = state->ra;
132112

133113
out:
114+
state->first = false;
134115
unwind_state_fixup(state);
135116
return !!__kernel_text_address(state->pc);
136117
}
137118

138-
void unwind_start(struct unwind_state *state, struct task_struct *task,
139-
struct pt_regs *regs)
140-
{
141-
memset(state, 0, sizeof(*state));
142-
state->type = UNWINDER_PROLOGUE;
143-
144-
if (regs) {
145-
state->sp = regs->regs[3];
146-
state->pc = regs->csr_era;
147-
state->ra = regs->regs[1];
148-
if (!__kernel_text_address(state->pc))
149-
state->type = UNWINDER_GUESS;
150-
} else if (task && task != current) {
151-
state->sp = thread_saved_fp(task);
152-
state->pc = thread_saved_ra(task);
153-
state->ra = 0;
154-
} else {
155-
state->sp = (unsigned long)__builtin_frame_address(0);
156-
state->pc = (unsigned long)__builtin_return_address(0);
157-
state->ra = 0;
158-
}
159-
160-
state->task = task;
161-
state->first = true;
162-
state->pc = unwind_graph_addr(state, state->pc, state->sp);
163-
get_stack_info(state->sp, state->task, &state->stack_info);
164-
165-
if (!unwind_done(state) && !__kernel_text_address(state->pc))
166-
unwind_next_frame(state);
167-
}
168-
EXPORT_SYMBOL_GPL(unwind_start);
169-
170-
bool unwind_next_frame(struct unwind_state *state)
119+
static bool next_frame(struct unwind_state *state)
171120
{
172-
struct stack_info *info = &state->stack_info;
173-
struct pt_regs *regs;
174121
unsigned long pc;
122+
struct pt_regs *regs;
123+
struct stack_info *info = &state->stack_info;
175124

176125
if (unwind_done(state))
177126
return false;
178127

179128
do {
180-
switch (state->type) {
181-
case UNWINDER_GUESS:
182-
state->first = false;
183-
if (unwind_by_guess(state))
184-
return true;
185-
break;
186-
187-
case UNWINDER_PROLOGUE:
188-
if (unwind_by_prologue(state)) {
189-
state->pc = unwind_graph_addr(state, state->pc, state->sp);
190-
return true;
191-
}
129+
if (unwind_by_prologue(state)) {
130+
state->pc = unwind_graph_addr(state, state->pc, state->sp);
131+
return true;
132+
}
192133

193-
if (info->type == STACK_TYPE_IRQ &&
194-
info->end == state->sp) {
195-
regs = (struct pt_regs *)info->next_sp;
196-
pc = regs->csr_era;
134+
if (info->type == STACK_TYPE_IRQ && info->end == state->sp) {
135+
regs = (struct pt_regs *)info->next_sp;
136+
pc = regs->csr_era;
197137

198-
if (user_mode(regs) || !__kernel_text_address(pc))
199-
return false;
138+
if (user_mode(regs) || !__kernel_text_address(pc))
139+
return false;
200140

201-
state->first = true;
202-
state->ra = regs->regs[1];
203-
state->sp = regs->regs[3];
204-
state->pc = pc;
205-
get_stack_info(state->sp, state->task, info);
141+
state->first = true;
142+
state->pc = pc;
143+
state->ra = regs->regs[1];
144+
state->sp = regs->regs[3];
145+
get_stack_info(state->sp, state->task, info);
206146

207-
return true;
208-
}
147+
return true;
209148
}
210149

211150
state->sp = info->next_sp;
@@ -214,4 +153,36 @@ bool unwind_next_frame(struct unwind_state *state)
214153

215154
return false;
216155
}
156+
157+
unsigned long unwind_get_return_address(struct unwind_state *state)
158+
{
159+
return __unwind_get_return_address(state);
160+
}
161+
EXPORT_SYMBOL_GPL(unwind_get_return_address);
162+
163+
void unwind_start(struct unwind_state *state, struct task_struct *task,
164+
struct pt_regs *regs)
165+
{
166+
__unwind_start(state, task, regs);
167+
state->type = UNWINDER_PROLOGUE;
168+
state->first = true;
169+
170+
/*
171+
* The current PC is not kernel text address, we cannot find its
172+
* relative symbol. Thus, prologue analysis will be broken. Luckily,
173+
* we can use the default_next_frame().
174+
*/
175+
if (!__kernel_text_address(state->pc)) {
176+
state->type = UNWINDER_GUESS;
177+
if (!unwind_done(state))
178+
unwind_next_frame(state);
179+
}
180+
}
181+
EXPORT_SYMBOL_GPL(unwind_start);
182+
183+
bool unwind_next_frame(struct unwind_state *state)
184+
{
185+
return state->type == UNWINDER_PROLOGUE ?
186+
next_frame(state) : default_next_frame(state);
187+
}
217188
EXPORT_SYMBOL_GPL(unwind_next_frame);

0 commit comments

Comments
 (0)