Commit 4552d5dc authored by Jan Beulich's avatar Jan Beulich Committed by Linus Torvalds

[PATCH] x86_64: reliable stack trace support

These are the generic bits needed to enable reliable stack traces based
on Dwarf2-like (.eh_frame) unwind information. Subsequent patches will
enable x86-64 and i386 to make use of this.

Thanks to Andi Kleen and Ingo Molnar, who pointed out several possibilities
for improvement.
Signed-off-by: default avatarJan Beulich <jbeulich@novell.com>
Signed-off-by: default avatarAndi Kleen <ak@suse.de>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 2b28592b
...@@ -32,6 +32,7 @@ extern const char linux_banner[]; ...@@ -32,6 +32,7 @@ extern const char linux_banner[];
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define ALIGN(x,a) (((x)+(a)-1)&~((a)-1)) #define ALIGN(x,a) (((x)+(a)-1)&~((a)-1))
#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
#define KERN_EMERG "<0>" /* system is unusable */ #define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */ #define KERN_ALERT "<1>" /* action must be taken immediately */
...@@ -336,6 +337,12 @@ struct sysinfo { ...@@ -336,6 +337,12 @@ struct sysinfo {
/* Force a compilation error if condition is true */ /* Force a compilation error if condition is true */
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
/* Force a compilation error if condition is true, but also produce a
result (of value 0 and type size_t), so the expression can be used
e.g. in a structure initializer (or where-ever else comma expressions
aren't permitted). */
#define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1)
/* Trap pasters of __FUNCTION__ at compile-time */ /* Trap pasters of __FUNCTION__ at compile-time */
#define __FUNCTION__ (__func__) #define __FUNCTION__ (__func__)
......
...@@ -285,6 +285,9 @@ struct module ...@@ -285,6 +285,9 @@ struct module
/* The size of the executable code in each section. */ /* The size of the executable code in each section. */
unsigned long init_text_size, core_text_size; unsigned long init_text_size, core_text_size;
/* The handle returned from unwind_add_table. */
void *unwind_info;
/* Arch-specific module values */ /* Arch-specific module values */
struct mod_arch_specific arch; struct mod_arch_specific arch;
......
#ifndef _LINUX_UNWIND_H
#define _LINUX_UNWIND_H
/*
* Copyright (C) 2002-2006 Novell, Inc.
* Jan Beulich <jbeulich@novell.com>
* This code is released under version 2 of the GNU GPL.
*
* A simple API for unwinding kernel stacks. This is used for
* debugging and error reporting purposes. The kernel doesn't need
* full-blown stack unwinding with all the bells and whistles, so there
* is not much point in implementing the full Dwarf2 unwind API.
*/
#include <linux/config.h>
struct module;
#ifdef CONFIG_STACK_UNWIND
#include <asm/unwind.h>
#ifndef ARCH_UNWIND_SECTION_NAME
#define ARCH_UNWIND_SECTION_NAME ".eh_frame"
#endif
/*
* Initialize unwind support.
*/
extern void unwind_init(void);
extern void *unwind_add_table(struct module *,
const void *table_start,
unsigned long table_size);
extern void unwind_remove_table(void *handle, int init_only);
extern int unwind_init_frame_info(struct unwind_frame_info *,
struct task_struct *,
/*const*/ struct pt_regs *);
/*
* Prepare to unwind a blocked task.
*/
extern int unwind_init_blocked(struct unwind_frame_info *,
struct task_struct *);
/*
* Prepare to unwind the currently running thread.
*/
extern int unwind_init_running(struct unwind_frame_info *,
asmlinkage void (*callback)(struct unwind_frame_info *,
void *arg),
void *arg);
/*
* Unwind to previous to frame. Returns 0 if successful, negative
* number in case of an error.
*/
extern int unwind(struct unwind_frame_info *);
/*
* Unwind until the return pointer is in user-land (or until an error
* occurs). Returns 0 if successful, negative number in case of
* error.
*/
extern int unwind_to_user(struct unwind_frame_info *);
#else
struct unwind_frame_info {};
static inline void unwind_init(void) {}
static inline void *unwind_add_table(struct module *mod,
const void *table_start,
unsigned long table_size)
{
return NULL;
}
static inline void unwind_remove_table(void *handle, int init_only)
{
}
static inline int unwind_init_frame_info(struct unwind_frame_info *info,
struct task_struct *tsk,
const struct pt_regs *regs)
{
return -ENOSYS;
}
static inline int unwind_init_blocked(struct unwind_frame_info *info,
struct task_struct *tsk)
{
return -ENOSYS;
}
static inline int unwind_init_running(struct unwind_frame_info *info,
asmlinkage void (*cb)(struct unwind_frame_info *,
void *arg),
void *arg)
{
return -ENOSYS;
}
static inline int unwind(struct unwind_frame_info *info)
{
return -ENOSYS;
}
static inline int unwind_to_user(struct unwind_frame_info *info)
{
return -ENOSYS;
}
#endif
#endif /* _LINUX_UNWIND_H */
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include <linux/rmap.h> #include <linux/rmap.h>
#include <linux/mempolicy.h> #include <linux/mempolicy.h>
#include <linux/key.h> #include <linux/key.h>
#include <linux/unwind.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/bugs.h> #include <asm/bugs.h>
...@@ -482,6 +483,7 @@ asmlinkage void __init start_kernel(void) ...@@ -482,6 +483,7 @@ asmlinkage void __init start_kernel(void)
__stop___param - __start___param, __stop___param - __start___param,
&unknown_bootoption); &unknown_bootoption);
sort_main_extable(); sort_main_extable();
unwind_init();
trap_init(); trap_init();
rcu_init(); rcu_init();
init_IRQ(); init_IRQ();
......
...@@ -21,6 +21,7 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o ...@@ -21,6 +21,7 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_STACK_UNWIND) += unwind.o
obj-$(CONFIG_PM) += power/ obj-$(CONFIG_PM) += power/
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
obj-$(CONFIG_KEXEC) += kexec.o obj-$(CONFIG_KEXEC) += kexec.o
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/unwind.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
...@@ -1051,6 +1052,8 @@ static void free_module(struct module *mod) ...@@ -1051,6 +1052,8 @@ static void free_module(struct module *mod)
remove_sect_attrs(mod); remove_sect_attrs(mod);
mod_kobject_remove(mod); mod_kobject_remove(mod);
unwind_remove_table(mod->unwind_info, 0);
/* Arch-specific cleanup. */ /* Arch-specific cleanup. */
module_arch_cleanup(mod); module_arch_cleanup(mod);
...@@ -1412,7 +1415,7 @@ static struct module *load_module(void __user *umod, ...@@ -1412,7 +1415,7 @@ static struct module *load_module(void __user *umod,
unsigned int i, symindex = 0, strindex = 0, setupindex, exindex, unsigned int i, symindex = 0, strindex = 0, setupindex, exindex,
exportindex, modindex, obsparmindex, infoindex, gplindex, exportindex, modindex, obsparmindex, infoindex, gplindex,
crcindex, gplcrcindex, versindex, pcpuindex, gplfutureindex, crcindex, gplcrcindex, versindex, pcpuindex, gplfutureindex,
gplfuturecrcindex; gplfuturecrcindex, unwindex = 0;
struct module *mod; struct module *mod;
long err = 0; long err = 0;
void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */ void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
...@@ -1502,6 +1505,9 @@ static struct module *load_module(void __user *umod, ...@@ -1502,6 +1505,9 @@ static struct module *load_module(void __user *umod,
versindex = find_sec(hdr, sechdrs, secstrings, "__versions"); versindex = find_sec(hdr, sechdrs, secstrings, "__versions");
infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo"); infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo");
pcpuindex = find_pcpusec(hdr, sechdrs, secstrings); pcpuindex = find_pcpusec(hdr, sechdrs, secstrings);
#ifdef ARCH_UNWIND_SECTION_NAME
unwindex = find_sec(hdr, sechdrs, secstrings, ARCH_UNWIND_SECTION_NAME);
#endif
/* Don't keep modinfo section */ /* Don't keep modinfo section */
sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC; sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
...@@ -1510,6 +1516,8 @@ static struct module *load_module(void __user *umod, ...@@ -1510,6 +1516,8 @@ static struct module *load_module(void __user *umod,
sechdrs[symindex].sh_flags |= SHF_ALLOC; sechdrs[symindex].sh_flags |= SHF_ALLOC;
sechdrs[strindex].sh_flags |= SHF_ALLOC; sechdrs[strindex].sh_flags |= SHF_ALLOC;
#endif #endif
if (unwindex)
sechdrs[unwindex].sh_flags |= SHF_ALLOC;
/* Check module struct version now, before we try to use module. */ /* Check module struct version now, before we try to use module. */
if (!check_modstruct_version(sechdrs, versindex, mod)) { if (!check_modstruct_version(sechdrs, versindex, mod)) {
...@@ -1738,6 +1746,11 @@ static struct module *load_module(void __user *umod, ...@@ -1738,6 +1746,11 @@ static struct module *load_module(void __user *umod,
goto arch_cleanup; goto arch_cleanup;
add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs); add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs);
/* Size of section 0 is 0, so this works well if no unwind info. */
mod->unwind_info = unwind_add_table(mod,
(void *)sechdrs[unwindex].sh_addr,
sechdrs[unwindex].sh_size);
/* Get rid of temporary copy */ /* Get rid of temporary copy */
vfree(hdr); vfree(hdr);
...@@ -1836,6 +1849,7 @@ sys_init_module(void __user *umod, ...@@ -1836,6 +1849,7 @@ sys_init_module(void __user *umod,
mod->state = MODULE_STATE_LIVE; mod->state = MODULE_STATE_LIVE;
/* Drop initial reference. */ /* Drop initial reference. */
module_put(mod); module_put(mod);
unwind_remove_table(mod->unwind_info, 1);
module_free(mod, mod->module_init); module_free(mod, mod->module_init);
mod->module_init = NULL; mod->module_init = NULL;
mod->init_size = 0; mod->init_size = 0;
......
This diff is collapsed.
...@@ -188,14 +188,22 @@ config FRAME_POINTER ...@@ -188,14 +188,22 @@ config FRAME_POINTER
config UNWIND_INFO config UNWIND_INFO
bool "Compile the kernel with frame unwind information" bool "Compile the kernel with frame unwind information"
depends on !IA64 depends on !IA64 && !PARISC
depends on !MODULES || !(MIPS || PARISC || PPC || SUPERH || V850) depends on !MODULES || !(MIPS || PPC || SUPERH || V850)
help help
If you say Y here the resulting kernel image will be slightly larger If you say Y here the resulting kernel image will be slightly larger
but not slower, and it will give very useful debugging information. but not slower, and it will give very useful debugging information.
If you don't debug the kernel, you can say N, but we may not be able If you don't debug the kernel, you can say N, but we may not be able
to solve problems without frame unwind information or frame pointers. to solve problems without frame unwind information or frame pointers.
config STACK_UNWIND
bool "Stack unwind support"
depends on UNWIND_INFO
depends on n
help
This enables more precise stack traces, omitting all unrelated
occurrences of pointers into kernel code from the dump.
config FORCED_INLINING config FORCED_INLINING
bool "Force gcc to inline functions marked 'inline'" bool "Force gcc to inline functions marked 'inline'"
depends on DEBUG_KERNEL depends on DEBUG_KERNEL
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment