aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/unwind_frame.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/unwind_frame.c')
-rw-r--r--arch/x86/kernel/unwind_frame.c49
1 files changed, 40 insertions, 9 deletions
diff --git a/arch/x86/kernel/unwind_frame.c b/arch/x86/kernel/unwind_frame.c
index 82c6d7f1fd73..b9389d72b2f7 100644
--- a/arch/x86/kernel/unwind_frame.c
+++ b/arch/x86/kernel/unwind_frame.c
@@ -104,6 +104,11 @@ static inline unsigned long *last_frame(struct unwind_state *state)
104 return (unsigned long *)task_pt_regs(state->task) - 2; 104 return (unsigned long *)task_pt_regs(state->task) - 2;
105} 105}
106 106
107static bool is_last_frame(struct unwind_state *state)
108{
109 return state->bp == last_frame(state);
110}
111
107#ifdef CONFIG_X86_32 112#ifdef CONFIG_X86_32
108#define GCC_REALIGN_WORDS 3 113#define GCC_REALIGN_WORDS 3
109#else 114#else
@@ -115,16 +120,15 @@ static inline unsigned long *last_aligned_frame(struct unwind_state *state)
115 return last_frame(state) - GCC_REALIGN_WORDS; 120 return last_frame(state) - GCC_REALIGN_WORDS;
116} 121}
117 122
118static bool is_last_task_frame(struct unwind_state *state) 123static bool is_last_aligned_frame(struct unwind_state *state)
119{ 124{
120 unsigned long *last_bp = last_frame(state); 125 unsigned long *last_bp = last_frame(state);
121 unsigned long *aligned_bp = last_aligned_frame(state); 126 unsigned long *aligned_bp = last_aligned_frame(state);
122 127
123 /* 128 /*
124 * We have to check for the last task frame at two different locations 129 * GCC can occasionally decide to realign the stack pointer and change
125 * because gcc can occasionally decide to realign the stack pointer and 130 * the offset of the stack frame in the prologue of a function called
126 * change the offset of the stack frame in the prologue of a function 131 * by head/entry code. Examples:
127 * called by head/entry code. Examples:
128 * 132 *
129 * <start_secondary>: 133 * <start_secondary>:
130 * push %edi 134 * push %edi
@@ -141,11 +145,38 @@ static bool is_last_task_frame(struct unwind_state *state)
141 * push %rbp 145 * push %rbp
142 * mov %rsp,%rbp 146 * mov %rsp,%rbp
143 * 147 *
144 * Note that after aligning the stack, it pushes a duplicate copy of 148 * After aligning the stack, it pushes a duplicate copy of the return
145 * the return address before pushing the frame pointer. 149 * address before pushing the frame pointer.
150 */
151 return (state->bp == aligned_bp && *(aligned_bp + 1) == *(last_bp + 1));
152}
153
154static bool is_last_ftrace_frame(struct unwind_state *state)
155{
156 unsigned long *last_bp = last_frame(state);
157 unsigned long *last_ftrace_bp = last_bp - 3;
158
159 /*
160 * When unwinding from an ftrace handler of a function called by entry
161 * code, the stack layout of the last frame is:
162 *
163 * bp
164 * parent ret addr
165 * bp
166 * function ret addr
167 * parent ret addr
168 * pt_regs
169 * -----------------
146 */ 170 */
147 return (state->bp == last_bp || 171 return (state->bp == last_ftrace_bp &&
148 (state->bp == aligned_bp && *(aligned_bp+1) == *(last_bp+1))); 172 *state->bp == *(state->bp + 2) &&
173 *(state->bp + 1) == *(state->bp + 4));
174}
175
176static bool is_last_task_frame(struct unwind_state *state)
177{
178 return is_last_frame(state) || is_last_aligned_frame(state) ||
179 is_last_ftrace_frame(state);
149} 180}
150 181
151/* 182/*