Skip to content

Commit

Permalink
[PATCH] x86_64: reliable stack trace support
Browse files Browse the repository at this point in the history
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: Jan Beulich <[email protected]>
Signed-off-by: Andi Kleen <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Jan Beulich authored and Linus Torvalds committed Jun 26, 2006
1 parent 2b28592 commit 4552d5d
Show file tree
Hide file tree
Showing 8 changed files with 1,072 additions and 3 deletions.
7 changes: 7 additions & 0 deletions include/linux/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ extern const char linux_banner[];

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#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_ALERT "<1>" /* action must be taken immediately */
Expand Down Expand Up @@ -336,6 +337,12 @@ struct sysinfo {
/* Force a compilation error if condition is true */
#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 */
#define __FUNCTION__ (__func__)

Expand Down
3 changes: 3 additions & 0 deletions include/linux/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,9 @@ struct module
/* The size of the executable code in each section. */
unsigned long init_text_size, core_text_size;

/* The handle returned from unwind_add_table. */
void *unwind_info;

/* Arch-specific module values */
struct mod_arch_specific arch;

Expand Down
119 changes: 119 additions & 0 deletions include/linux/unwind.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#ifndef _LINUX_UNWIND_H
#define _LINUX_UNWIND_H

/*
* Copyright (C) 2002-2006 Novell, Inc.
* Jan Beulich <[email protected]>
* 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 */
2 changes: 2 additions & 0 deletions init/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <linux/rmap.h>
#include <linux/mempolicy.h>
#include <linux/key.h>
#include <linux/unwind.h>

#include <asm/io.h>
#include <asm/bugs.h>
Expand Down Expand Up @@ -482,6 +483,7 @@ asmlinkage void __init start_kernel(void)
__stop___param - __start___param,
&unknown_bootoption);
sort_main_extable();
unwind_init();
trap_init();
rcu_init();
init_IRQ();
Expand Down
1 change: 1 addition & 0 deletions kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_STACK_UNWIND) += unwind.o
obj-$(CONFIG_PM) += power/
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
obj-$(CONFIG_KEXEC) += kexec.o
Expand Down
16 changes: 15 additions & 1 deletion kernel/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/unwind.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
#include <asm/cacheflush.h>
Expand Down Expand Up @@ -1051,6 +1052,8 @@ static void free_module(struct module *mod)
remove_sect_attrs(mod);
mod_kobject_remove(mod);

unwind_remove_table(mod->unwind_info, 0);

/* Arch-specific cleanup. */
module_arch_cleanup(mod);

Expand Down Expand Up @@ -1412,7 +1415,7 @@ static struct module *load_module(void __user *umod,
unsigned int i, symindex = 0, strindex = 0, setupindex, exindex,
exportindex, modindex, obsparmindex, infoindex, gplindex,
crcindex, gplcrcindex, versindex, pcpuindex, gplfutureindex,
gplfuturecrcindex;
gplfuturecrcindex, unwindex = 0;
struct module *mod;
long err = 0;
void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */
Expand Down Expand Up @@ -1502,6 +1505,9 @@ static struct module *load_module(void __user *umod,
versindex = find_sec(hdr, sechdrs, secstrings, "__versions");
infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo");
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 */
sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC;
Expand All @@ -1510,6 +1516,8 @@ static struct module *load_module(void __user *umod,
sechdrs[symindex].sh_flags |= SHF_ALLOC;
sechdrs[strindex].sh_flags |= SHF_ALLOC;
#endif
if (unwindex)
sechdrs[unwindex].sh_flags |= SHF_ALLOC;

/* Check module struct version now, before we try to use module. */
if (!check_modstruct_version(sechdrs, versindex, mod)) {
Expand Down Expand Up @@ -1738,6 +1746,11 @@ static struct module *load_module(void __user *umod,
goto arch_cleanup;
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 */
vfree(hdr);

Expand Down Expand Up @@ -1836,6 +1849,7 @@ sys_init_module(void __user *umod,
mod->state = MODULE_STATE_LIVE;
/* Drop initial reference. */
module_put(mod);
unwind_remove_table(mod->unwind_info, 1);
module_free(mod, mod->module_init);
mod->module_init = NULL;
mod->init_size = 0;
Expand Down
Loading

0 comments on commit 4552d5d

Please sign in to comment.