aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kernel/unwind_frame.c36
1 files changed, 30 insertions, 6 deletions
diff --git a/arch/x86/kernel/unwind_frame.c b/arch/x86/kernel/unwind_frame.c
index 478d15dbaee4..08339262b666 100644
--- a/arch/x86/kernel/unwind_frame.c
+++ b/arch/x86/kernel/unwind_frame.c
@@ -82,19 +82,43 @@ static size_t regs_size(struct pt_regs *regs)
82 return sizeof(*regs); 82 return sizeof(*regs);
83} 83}
84 84
85#ifdef CONFIG_X86_32
86#define GCC_REALIGN_WORDS 3
87#else
88#define GCC_REALIGN_WORDS 1
89#endif
90
85static bool is_last_task_frame(struct unwind_state *state) 91static bool is_last_task_frame(struct unwind_state *state)
86{ 92{
87 unsigned long bp = (unsigned long)state->bp; 93 unsigned long *last_bp = (unsigned long *)task_pt_regs(state->task) - 2;
88 unsigned long regs = (unsigned long)task_pt_regs(state->task); 94 unsigned long *aligned_bp = last_bp - GCC_REALIGN_WORDS;
89 95
90 /* 96 /*
91 * We have to check for the last task frame at two different locations 97 * We have to check for the last task frame at two different locations
92 * because gcc can occasionally decide to realign the stack pointer and 98 * because gcc can occasionally decide to realign the stack pointer and
93 * change the offset of the stack frame by a word in the prologue of a 99 * change the offset of the stack frame in the prologue of a function
94 * function called by head/entry code. 100 * called by head/entry code. Examples:
101 *
102 * <start_secondary>:
103 * push %edi
104 * lea 0x8(%esp),%edi
105 * and $0xfffffff8,%esp
106 * pushl -0x4(%edi)
107 * push %ebp
108 * mov %esp,%ebp
109 *
110 * <x86_64_start_kernel>:
111 * lea 0x8(%rsp),%r10
112 * and $0xfffffffffffffff0,%rsp
113 * pushq -0x8(%r10)
114 * push %rbp
115 * mov %rsp,%rbp
116 *
117 * Note that after aligning the stack, it pushes a duplicate copy of
118 * the return address before pushing the frame pointer.
95 */ 119 */
96 return bp == regs - FRAME_HEADER_SIZE || 120 return (state->bp == last_bp ||
97 bp == regs - FRAME_HEADER_SIZE - sizeof(long); 121 (state->bp == aligned_bp && *(aligned_bp+1) == *(last_bp+1)));
98} 122}
99 123
100/* 124/*