aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@novell.com>2006-06-26 07:57:41 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-26 13:48:17 -0400
commit176a2718f408ce92788b29127050b04dfd6e4f68 (patch)
treebb637b13098f821551d07d3a13d140a908e7bc04
parentdffead4e421e289c8434351400d24fd35723e874 (diff)
[PATCH] i386: reliable stack trace support (i386)
These are the i386-specific pieces to enable reliable stack traces. This is going to be even more useful once CFI annotations get added to he assembly code, namely to entry.S. Signed-off-by: Jan Beulich <jbeulich@novell.com> Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--arch/i386/kernel/entry.S29
-rw-r--r--arch/i386/kernel/process.c2
-rw-r--r--arch/i386/kernel/traps.c50
-rw-r--r--arch/i386/kernel/vmlinux.lds.S9
-rw-r--r--include/asm-i386/processor.h2
-rw-r--r--include/asm-i386/unwind.h98
-rw-r--r--lib/Kconfig.debug2
7 files changed, 179 insertions, 13 deletions
diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S
index cfc683f153b9..e802f3cac7e3 100644
--- a/arch/i386/kernel/entry.S
+++ b/arch/i386/kernel/entry.S
@@ -663,6 +663,35 @@ ENTRY(spurious_interrupt_bug)
663 pushl $do_spurious_interrupt_bug 663 pushl $do_spurious_interrupt_bug
664 jmp error_code 664 jmp error_code
665 665
666#ifdef CONFIG_STACK_UNWIND
667ENTRY(arch_unwind_init_running)
668 movl 4(%esp), %edx
669 movl (%esp), %ecx
670 leal 4(%esp), %eax
671 movl %ebx, EBX(%edx)
672 xorl %ebx, %ebx
673 movl %ebx, ECX(%edx)
674 movl %ebx, EDX(%edx)
675 movl %esi, ESI(%edx)
676 movl %edi, EDI(%edx)
677 movl %ebp, EBP(%edx)
678 movl %ebx, EAX(%edx)
679 movl $__USER_DS, DS(%edx)
680 movl $__USER_DS, ES(%edx)
681 movl %ebx, ORIG_EAX(%edx)
682 movl %ecx, EIP(%edx)
683 movl 12(%esp), %ecx
684 movl $__KERNEL_CS, CS(%edx)
685 movl %ebx, EFLAGS(%edx)
686 movl %eax, OLDESP(%edx)
687 movl 8(%esp), %eax
688 movl %ecx, 8(%esp)
689 movl EBX(%edx), %ebx
690 movl $__KERNEL_DS, OLDSS(%edx)
691 jmpl *%eax
692ENDPROC(arch_unwind_init_running)
693#endif
694
666.section .rodata,"a" 695.section .rodata,"a"
667#include "syscall_table.S" 696#include "syscall_table.S"
668 697
diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c
index 6259afea46d1..525432e3fef7 100644
--- a/arch/i386/kernel/process.c
+++ b/arch/i386/kernel/process.c
@@ -312,7 +312,7 @@ void show_regs(struct pt_regs * regs)
312 cr3 = read_cr3(); 312 cr3 = read_cr3();
313 cr4 = read_cr4_safe(); 313 cr4 = read_cr4_safe();
314 printk("CR0: %08lx CR2: %08lx CR3: %08lx CR4: %08lx\n", cr0, cr2, cr3, cr4); 314 printk("CR0: %08lx CR2: %08lx CR3: %08lx CR4: %08lx\n", cr0, cr2, cr3, cr4);
315 show_trace(NULL, &regs->esp); 315 show_trace(NULL, regs, &regs->esp);
316} 316}
317 317
318/* 318/*
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
index dcc14477af1f..286584667865 100644
--- a/arch/i386/kernel/traps.c
+++ b/arch/i386/kernel/traps.c
@@ -28,6 +28,7 @@
28#include <linux/utsname.h> 28#include <linux/utsname.h>
29#include <linux/kprobes.h> 29#include <linux/kprobes.h>
30#include <linux/kexec.h> 30#include <linux/kexec.h>
31#include <linux/unwind.h>
31 32
32#ifdef CONFIG_EISA 33#ifdef CONFIG_EISA
33#include <linux/ioport.h> 34#include <linux/ioport.h>
@@ -47,7 +48,7 @@
47#include <asm/desc.h> 48#include <asm/desc.h>
48#include <asm/i387.h> 49#include <asm/i387.h>
49#include <asm/nmi.h> 50#include <asm/nmi.h>
50 51#include <asm/unwind.h>
51#include <asm/smp.h> 52#include <asm/smp.h>
52#include <asm/arch_hooks.h> 53#include <asm/arch_hooks.h>
53#include <asm/kdebug.h> 54#include <asm/kdebug.h>
@@ -170,14 +171,43 @@ static inline unsigned long print_context_stack(struct thread_info *tinfo,
170 return ebp; 171 return ebp;
171} 172}
172 173
173static void show_trace_log_lvl(struct task_struct *task, 174static asmlinkage void show_trace_unwind(struct unwind_frame_info *info, void *log_lvl)
175{
176 int printed = 0; /* nr of entries already printed on current line */
177
178 while (unwind(info) == 0 && UNW_PC(info)) {
179 printed = print_addr_and_symbol(UNW_PC(info), log_lvl, printed);
180 if (arch_unw_user_mode(info))
181 break;
182 }
183 if (printed)
184 printk("\n");
185}
186
187static void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
174 unsigned long *stack, char *log_lvl) 188 unsigned long *stack, char *log_lvl)
175{ 189{
176 unsigned long ebp; 190 unsigned long ebp;
191 struct unwind_frame_info info;
177 192
178 if (!task) 193 if (!task)
179 task = current; 194 task = current;
180 195
196 if (regs) {
197 if (unwind_init_frame_info(&info, task, regs) == 0) {
198 show_trace_unwind(&info, log_lvl);
199 return;
200 }
201 } else if (task == current) {
202 if (unwind_init_running(&info, show_trace_unwind, log_lvl) == 0)
203 return;
204 } else {
205 if (unwind_init_blocked(&info, task) == 0) {
206 show_trace_unwind(&info, log_lvl);
207 return;
208 }
209 }
210
181 if (task == current) { 211 if (task == current) {
182 /* Grab ebp right from our regs */ 212 /* Grab ebp right from our regs */
183 asm ("movl %%ebp, %0" : "=r" (ebp) : ); 213 asm ("movl %%ebp, %0" : "=r" (ebp) : );
@@ -198,13 +228,13 @@ static void show_trace_log_lvl(struct task_struct *task,
198 } 228 }
199} 229}
200 230
201void show_trace(struct task_struct *task, unsigned long * stack) 231void show_trace(struct task_struct *task, struct pt_regs *regs, unsigned long * stack)
202{ 232{
203 show_trace_log_lvl(task, stack, ""); 233 show_trace_log_lvl(task, regs, stack, "");
204} 234}
205 235
206static void show_stack_log_lvl(struct task_struct *task, unsigned long *esp, 236static void show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
207 char *log_lvl) 237 unsigned long *esp, char *log_lvl)
208{ 238{
209 unsigned long *stack; 239 unsigned long *stack;
210 int i; 240 int i;
@@ -225,13 +255,13 @@ static void show_stack_log_lvl(struct task_struct *task, unsigned long *esp,
225 printk("%08lx ", *stack++); 255 printk("%08lx ", *stack++);
226 } 256 }
227 printk("\n%sCall Trace:\n", log_lvl); 257 printk("\n%sCall Trace:\n", log_lvl);
228 show_trace_log_lvl(task, esp, log_lvl); 258 show_trace_log_lvl(task, regs, esp, log_lvl);
229} 259}
230 260
231void show_stack(struct task_struct *task, unsigned long *esp) 261void show_stack(struct task_struct *task, unsigned long *esp)
232{ 262{
233 printk(" "); 263 printk(" ");
234 show_stack_log_lvl(task, esp, ""); 264 show_stack_log_lvl(task, NULL, esp, "");
235} 265}
236 266
237/* 267/*
@@ -241,7 +271,7 @@ void dump_stack(void)
241{ 271{
242 unsigned long stack; 272 unsigned long stack;
243 273
244 show_trace(current, &stack); 274 show_trace(current, NULL, &stack);
245} 275}
246 276
247EXPORT_SYMBOL(dump_stack); 277EXPORT_SYMBOL(dump_stack);
@@ -285,7 +315,7 @@ void show_registers(struct pt_regs *regs)
285 u8 __user *eip; 315 u8 __user *eip;
286 316
287 printk("\n" KERN_EMERG "Stack: "); 317 printk("\n" KERN_EMERG "Stack: ");
288 show_stack_log_lvl(NULL, (unsigned long *)esp, KERN_EMERG); 318 show_stack_log_lvl(NULL, regs, (unsigned long *)esp, KERN_EMERG);
289 319
290 printk(KERN_EMERG "Code: "); 320 printk(KERN_EMERG "Code: ");
291 321
diff --git a/arch/i386/kernel/vmlinux.lds.S b/arch/i386/kernel/vmlinux.lds.S
index 7512f39c9f25..2d4f1386e2b1 100644
--- a/arch/i386/kernel/vmlinux.lds.S
+++ b/arch/i386/kernel/vmlinux.lds.S
@@ -71,6 +71,15 @@ SECTIONS
71 .data.read_mostly : AT(ADDR(.data.read_mostly) - LOAD_OFFSET) { *(.data.read_mostly) } 71 .data.read_mostly : AT(ADDR(.data.read_mostly) - LOAD_OFFSET) { *(.data.read_mostly) }
72 _edata = .; /* End of data section */ 72 _edata = .; /* End of data section */
73 73
74#ifdef CONFIG_STACK_UNWIND
75 . = ALIGN(4);
76 .eh_frame : AT(ADDR(.eh_frame) - LOAD_OFFSET) {
77 __start_unwind = .;
78 *(.eh_frame)
79 __end_unwind = .;
80 }
81#endif
82
74 . = ALIGN(THREAD_SIZE); /* init_task */ 83 . = ALIGN(THREAD_SIZE); /* init_task */
75 .data.init_task : AT(ADDR(.data.init_task) - LOAD_OFFSET) { 84 .data.init_task : AT(ADDR(.data.init_task) - LOAD_OFFSET) {
76 *(.data.init_task) 85 *(.data.init_task)
diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h
index b796210c0f5c..55ea992da329 100644
--- a/include/asm-i386/processor.h
+++ b/include/asm-i386/processor.h
@@ -555,7 +555,7 @@ extern void prepare_to_copy(struct task_struct *tsk);
555extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); 555extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
556 556
557extern unsigned long thread_saved_pc(struct task_struct *tsk); 557extern unsigned long thread_saved_pc(struct task_struct *tsk);
558void show_trace(struct task_struct *task, unsigned long *stack); 558void show_trace(struct task_struct *task, struct pt_regs *regs, unsigned long *stack);
559 559
560unsigned long get_wchan(struct task_struct *p); 560unsigned long get_wchan(struct task_struct *p);
561 561
diff --git a/include/asm-i386/unwind.h b/include/asm-i386/unwind.h
new file mode 100644
index 000000000000..1c076897ac21
--- /dev/null
+++ b/include/asm-i386/unwind.h
@@ -0,0 +1,98 @@
1#ifndef _ASM_I386_UNWIND_H
2#define _ASM_I386_UNWIND_H
3
4/*
5 * Copyright (C) 2002-2006 Novell, Inc.
6 * Jan Beulich <jbeulich@novell.com>
7 * This code is released under version 2 of the GNU GPL.
8 */
9
10#ifdef CONFIG_STACK_UNWIND
11
12#include <linux/sched.h>
13#include <asm/fixmap.h>
14#include <asm/ptrace.h>
15#include <asm/uaccess.h>
16
17struct unwind_frame_info
18{
19 struct pt_regs regs;
20 struct task_struct *task;
21};
22
23#define UNW_PC(frame) (frame)->regs.eip
24#define UNW_SP(frame) (frame)->regs.esp
25#ifdef CONFIG_FRAME_POINTER
26#define UNW_FP(frame) (frame)->regs.ebp
27#define FRAME_RETADDR_OFFSET 4
28#define FRAME_LINK_OFFSET 0
29#define STACK_BOTTOM(tsk) STACK_LIMIT((tsk)->thread.esp0)
30#define STACK_TOP(tsk) ((tsk)->thread.esp0)
31#endif
32#define STACK_LIMIT(ptr) (((ptr) - 1) & ~(THREAD_SIZE - 1))
33
34#define UNW_REGISTER_INFO \
35 PTREGS_INFO(eax), \
36 PTREGS_INFO(ecx), \
37 PTREGS_INFO(edx), \
38 PTREGS_INFO(ebx), \
39 PTREGS_INFO(esp), \
40 PTREGS_INFO(ebp), \
41 PTREGS_INFO(esi), \
42 PTREGS_INFO(edi), \
43 PTREGS_INFO(eip)
44
45static inline void arch_unw_init_frame_info(struct unwind_frame_info *info,
46 /*const*/ struct pt_regs *regs)
47{
48 if (user_mode_vm(regs))
49 info->regs = *regs;
50 else {
51 memcpy(&info->regs, regs, offsetof(struct pt_regs, esp));
52 info->regs.esp = (unsigned long)&regs->esp;
53 info->regs.xss = __KERNEL_DS;
54 }
55}
56
57static inline void arch_unw_init_blocked(struct unwind_frame_info *info)
58{
59 memset(&info->regs, 0, sizeof(info->regs));
60 info->regs.eip = info->task->thread.eip;
61 info->regs.xcs = __KERNEL_CS;
62 __get_user(info->regs.ebp, (long *)info->task->thread.esp);
63 info->regs.esp = info->task->thread.esp;
64 info->regs.xss = __KERNEL_DS;
65 info->regs.xds = __USER_DS;
66 info->regs.xes = __USER_DS;
67}
68
69extern asmlinkage void arch_unwind_init_running(struct unwind_frame_info *,
70 asmlinkage void (*callback)(struct unwind_frame_info *,
71 void *arg),
72 void *arg);
73
74static inline int arch_unw_user_mode(const struct unwind_frame_info *info)
75{
76#if 0 /* This can only work when selector register and EFLAGS saves/restores
77 are properly annotated (and tracked in UNW_REGISTER_INFO). */
78 return user_mode_vm(&info->regs);
79#else
80 return info->regs.eip < PAGE_OFFSET
81 || (info->regs.eip >= __fix_to_virt(FIX_VSYSCALL)
82 && info->regs.eip < __fix_to_virt(FIX_VSYSCALL) + PAGE_SIZE)
83 || info->regs.esp < PAGE_OFFSET;
84#endif
85}
86
87#else
88
89#define UNW_PC(frame) ((void)(frame), 0)
90
91static inline int arch_unw_user_mode(const void *info)
92{
93 return 0;
94}
95
96#endif
97
98#endif /* _ASM_I386_UNWIND_H */
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index bbaed84a686b..8bab0102ac73 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -199,7 +199,7 @@ config UNWIND_INFO
199config STACK_UNWIND 199config STACK_UNWIND
200 bool "Stack unwind support" 200 bool "Stack unwind support"
201 depends on UNWIND_INFO 201 depends on UNWIND_INFO
202 depends on X86_64 202 depends on X86
203 help 203 help
204 This enables more precise stack traces, omitting all unrelated 204 This enables more precise stack traces, omitting all unrelated
205 occurrences of pointers into kernel code from the dump. 205 occurrences of pointers into kernel code from the dump.