diff options
-rw-r--r-- | arch/x86/kernel/unwind_frame.c | 36 |
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 | |||
85 | static bool is_last_task_frame(struct unwind_state *state) | 91 | static 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 | /* |