Commit 8ec006c5 authored by Paul Mundt's avatar Paul Mundt

Merge branch 'sh/dwarf-unwinder'

Conflicts:
	arch/sh/kernel/dwarf.c
parents 3d4e0cfb 5ab78ff6
...@@ -241,6 +241,12 @@ struct dwarf_cie { ...@@ -241,6 +241,12 @@ struct dwarf_cie {
unsigned long flags; unsigned long flags;
#define DWARF_CIE_Z_AUGMENTATION (1 << 0) #define DWARF_CIE_Z_AUGMENTATION (1 << 0)
/*
* 'mod' will be non-NULL if this CIE came from a module's
* .eh_frame section.
*/
struct module *mod;
}; };
/** /**
...@@ -255,6 +261,12 @@ struct dwarf_fde { ...@@ -255,6 +261,12 @@ struct dwarf_fde {
unsigned char *instructions; unsigned char *instructions;
unsigned char *end; unsigned char *end;
struct list_head link; struct list_head link;
/*
* 'mod' will be non-NULL if this FDE came from a module's
* .eh_frame section.
*/
struct module *mod;
}; };
/** /**
...@@ -364,6 +376,10 @@ static inline unsigned int DW_CFA_operand(unsigned long insn) ...@@ -364,6 +376,10 @@ static inline unsigned int DW_CFA_operand(unsigned long insn)
extern struct dwarf_frame *dwarf_unwind_stack(unsigned long, extern struct dwarf_frame *dwarf_unwind_stack(unsigned long,
struct dwarf_frame *); struct dwarf_frame *);
extern void dwarf_free_frame(struct dwarf_frame *);
extern int dwarf_parse_section(char *, char *, struct module *);
extern void dwarf_module_unload(struct module *);
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
#define CFI_STARTPROC .cfi_startproc #define CFI_STARTPROC .cfi_startproc
......
...@@ -529,7 +529,18 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, ...@@ -529,7 +529,18 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start,
} }
/** /**
* dwarf_unwind_stack - recursively unwind the stack * dwarf_free_frame - free the memory allocated for @frame
* @frame: the frame to free
*/
void dwarf_free_frame(struct dwarf_frame *frame)
{
dwarf_frame_free_regs(frame);
mempool_free(frame, dwarf_frame_pool);
}
/**
* dwarf_unwind_stack - unwind the stack
*
* @pc: address of the function to unwind * @pc: address of the function to unwind
* @prev: struct dwarf_frame of the previous stackframe on the callstack * @prev: struct dwarf_frame of the previous stackframe on the callstack
* *
...@@ -547,9 +558,9 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, ...@@ -547,9 +558,9 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc,
unsigned long addr; unsigned long addr;
/* /*
* If this is the first invocation of this recursive function we * If we're starting at the top of the stack we need get the
* need get the contents of a physical register to get the CFA * contents of a physical register to get the CFA in order to
* in order to begin the virtual unwinding of the stack. * begin the virtual unwinding of the stack.
* *
* NOTE: the return address is guaranteed to be setup by the * NOTE: the return address is guaranteed to be setup by the
* time this function makes its first function call. * time this function makes its first function call.
...@@ -571,9 +582,8 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, ...@@ -571,9 +582,8 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc,
fde = dwarf_lookup_fde(pc); fde = dwarf_lookup_fde(pc);
if (!fde) { if (!fde) {
/* /*
* This is our normal exit path - the one that stops the * This is our normal exit path. There are two reasons
* recursion. There's two reasons why we might exit * why we might exit here,
* here,
* *
* a) pc has no asscociated DWARF frame info and so * a) pc has no asscociated DWARF frame info and so
* we don't know how to unwind this frame. This is * we don't know how to unwind this frame. This is
...@@ -615,10 +625,10 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, ...@@ -615,10 +625,10 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc,
} else { } else {
/* /*
* Again, this is the first invocation of this * Again, we're starting from the top of the
* recurisve function. We need to physically * stack. We need to physically read
* read the contents of a register in order to * the contents of a register in order to get
* get the Canonical Frame Address for this * the Canonical Frame Address for this
* function. * function.
*/ */
frame->cfa = dwarf_read_arch_reg(frame->cfa_register); frame->cfa = dwarf_read_arch_reg(frame->cfa_register);
...@@ -648,13 +658,12 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, ...@@ -648,13 +658,12 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc,
return frame; return frame;
bail: bail:
dwarf_frame_free_regs(frame); dwarf_free_frame(frame);
mempool_free(frame, dwarf_frame_pool);
return NULL; return NULL;
} }
static int dwarf_parse_cie(void *entry, void *p, unsigned long len, static int dwarf_parse_cie(void *entry, void *p, unsigned long len,
unsigned char *end) unsigned char *end, struct module *mod)
{ {
struct dwarf_cie *cie; struct dwarf_cie *cie;
unsigned long flags; unsigned long flags;
...@@ -750,6 +759,8 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, ...@@ -750,6 +759,8 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len,
cie->initial_instructions = p; cie->initial_instructions = p;
cie->instructions_end = end; cie->instructions_end = end;
cie->mod = mod;
/* Add to list */ /* Add to list */
spin_lock_irqsave(&dwarf_cie_lock, flags); spin_lock_irqsave(&dwarf_cie_lock, flags);
list_add_tail(&cie->link, &dwarf_cie_list); list_add_tail(&cie->link, &dwarf_cie_list);
...@@ -760,7 +771,7 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, ...@@ -760,7 +771,7 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len,
static int dwarf_parse_fde(void *entry, u32 entry_type, static int dwarf_parse_fde(void *entry, u32 entry_type,
void *start, unsigned long len, void *start, unsigned long len,
unsigned char *end) unsigned char *end, struct module *mod)
{ {
struct dwarf_fde *fde; struct dwarf_fde *fde;
struct dwarf_cie *cie; struct dwarf_cie *cie;
...@@ -809,6 +820,8 @@ static int dwarf_parse_fde(void *entry, u32 entry_type, ...@@ -809,6 +820,8 @@ static int dwarf_parse_fde(void *entry, u32 entry_type,
fde->instructions = p; fde->instructions = p;
fde->end = end; fde->end = end;
fde->mod = mod;
/* Add to list. */ /* Add to list. */
spin_lock_irqsave(&dwarf_fde_lock, flags); spin_lock_irqsave(&dwarf_fde_lock, flags);
list_add_tail(&fde->link, &dwarf_fde_list); list_add_tail(&fde->link, &dwarf_fde_list);
...@@ -832,10 +845,8 @@ static void dwarf_unwinder_dump(struct task_struct *task, ...@@ -832,10 +845,8 @@ static void dwarf_unwinder_dump(struct task_struct *task,
while (1) { while (1) {
frame = dwarf_unwind_stack(return_addr, _frame); frame = dwarf_unwind_stack(return_addr, _frame);
if (_frame) { if (_frame)
dwarf_frame_free_regs(_frame); dwarf_free_frame(_frame);
mempool_free(_frame, dwarf_frame_pool);
}
_frame = frame; _frame = frame;
...@@ -845,6 +856,9 @@ static void dwarf_unwinder_dump(struct task_struct *task, ...@@ -845,6 +856,9 @@ static void dwarf_unwinder_dump(struct task_struct *task,
return_addr = frame->return_addr; return_addr = frame->return_addr;
ops->address(data, return_addr, 1); ops->address(data, return_addr, 1);
} }
if (frame)
dwarf_free_frame(frame);
} }
static struct unwinder dwarf_unwinder = { static struct unwinder dwarf_unwinder = {
...@@ -874,15 +888,15 @@ static void dwarf_unwinder_cleanup(void) ...@@ -874,15 +888,15 @@ static void dwarf_unwinder_cleanup(void)
} }
/** /**
* dwarf_unwinder_init - initialise the dwarf unwinder * dwarf_parse_section - parse DWARF section
* @eh_frame_start: start address of the .eh_frame section
* @eh_frame_end: end address of the .eh_frame section
* @mod: the kernel module containing the .eh_frame section
* *
* Build the data structures describing the .dwarf_frame section to * Parse the information in a .eh_frame section.
* make it easier to lookup CIE and FDE entries. Because the
* .eh_frame section is packed as tightly as possible it is not
* easy to lookup the FDE for a given PC, so we build a list of FDE
* and CIE entries that make it easier.
*/ */
static int __init dwarf_unwinder_init(void) int dwarf_parse_section(char *eh_frame_start, char *eh_frame_end,
struct module *mod)
{ {
u32 entry_type; u32 entry_type;
void *p, *entry; void *p, *entry;
...@@ -890,32 +904,12 @@ static int __init dwarf_unwinder_init(void) ...@@ -890,32 +904,12 @@ static int __init dwarf_unwinder_init(void)
unsigned long len; unsigned long len;
unsigned int c_entries, f_entries; unsigned int c_entries, f_entries;
unsigned char *end; unsigned char *end;
INIT_LIST_HEAD(&dwarf_cie_list);
INIT_LIST_HEAD(&dwarf_fde_list);
c_entries = 0; c_entries = 0;
f_entries = 0; f_entries = 0;
entry = &__start_eh_frame; entry = eh_frame_start;
dwarf_frame_cachep = kmem_cache_create("dwarf_frames",
sizeof(struct dwarf_frame), 0,
SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
dwarf_reg_cachep = kmem_cache_create("dwarf_regs",
sizeof(struct dwarf_reg), 0,
SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
dwarf_frame_pool = mempool_create(DWARF_FRAME_MIN_REQ,
mempool_alloc_slab,
mempool_free_slab,
dwarf_frame_cachep);
dwarf_reg_pool = mempool_create(DWARF_REG_MIN_REQ, while ((char *)entry < eh_frame_end) {
mempool_alloc_slab,
mempool_free_slab,
dwarf_reg_cachep);
while ((char *)entry < __stop_eh_frame) {
p = entry; p = entry;
count = dwarf_entry_len(p, &len); count = dwarf_entry_len(p, &len);
...@@ -927,6 +921,7 @@ static int __init dwarf_unwinder_init(void) ...@@ -927,6 +921,7 @@ static int __init dwarf_unwinder_init(void)
* entry and move to the next one because 'len' * entry and move to the next one because 'len'
* tells us where our next entry is. * tells us where our next entry is.
*/ */
err = -EINVAL;
goto out; goto out;
} else } else
p += count; p += count;
...@@ -938,13 +933,14 @@ static int __init dwarf_unwinder_init(void) ...@@ -938,13 +933,14 @@ static int __init dwarf_unwinder_init(void)
p += 4; p += 4;
if (entry_type == DW_EH_FRAME_CIE) { if (entry_type == DW_EH_FRAME_CIE) {
err = dwarf_parse_cie(entry, p, len, end); err = dwarf_parse_cie(entry, p, len, end, mod);
if (err < 0) if (err < 0)
goto out; goto out;
else else
c_entries++; c_entries++;
} else { } else {
err = dwarf_parse_fde(entry, entry_type, p, len, end); err = dwarf_parse_fde(entry, entry_type, p, len,
end, mod);
if (err < 0) if (err < 0)
goto out; goto out;
else else
...@@ -957,6 +953,95 @@ static int __init dwarf_unwinder_init(void) ...@@ -957,6 +953,95 @@ static int __init dwarf_unwinder_init(void)
printk(KERN_INFO "DWARF unwinder initialised: read %u CIEs, %u FDEs\n", printk(KERN_INFO "DWARF unwinder initialised: read %u CIEs, %u FDEs\n",
c_entries, f_entries); c_entries, f_entries);
return 0;
out:
return err;
}
/**
* dwarf_module_unload - remove FDE/CIEs associated with @mod
* @mod: the module that is being unloaded
*
* Remove any FDEs and CIEs from the global lists that came from
* @mod's .eh_frame section because @mod is being unloaded.
*/
void dwarf_module_unload(struct module *mod)
{
struct dwarf_fde *fde;
struct dwarf_cie *cie;
unsigned long flags;
spin_lock_irqsave(&dwarf_cie_lock, flags);
again_cie:
list_for_each_entry(cie, &dwarf_cie_list, link) {
if (cie->mod == mod)
break;
}
if (&cie->link != &dwarf_cie_list) {
list_del(&cie->link);
kfree(cie);
goto again_cie;
}
spin_unlock_irqrestore(&dwarf_cie_lock, flags);
spin_lock_irqsave(&dwarf_fde_lock, flags);
again_fde:
list_for_each_entry(fde, &dwarf_fde_list, link) {
if (fde->mod == mod)
break;
}
if (&fde->link != &dwarf_fde_list) {
list_del(&fde->link);
kfree(fde);
goto again_fde;
}
spin_unlock_irqrestore(&dwarf_fde_lock, flags);
}
/**
* dwarf_unwinder_init - initialise the dwarf unwinder
*
* Build the data structures describing the .dwarf_frame section to
* make it easier to lookup CIE and FDE entries. Because the
* .eh_frame section is packed as tightly as possible it is not
* easy to lookup the FDE for a given PC, so we build a list of FDE
* and CIE entries that make it easier.
*/
static int __init dwarf_unwinder_init(void)
{
int err;
INIT_LIST_HEAD(&dwarf_cie_list);
INIT_LIST_HEAD(&dwarf_fde_list);
dwarf_frame_cachep = kmem_cache_create("dwarf_frames",
sizeof(struct dwarf_frame), 0,
SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
dwarf_reg_cachep = kmem_cache_create("dwarf_regs",
sizeof(struct dwarf_reg), 0,
SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
dwarf_frame_pool = mempool_create(DWARF_FRAME_MIN_REQ,
mempool_alloc_slab,
mempool_free_slab,
dwarf_frame_cachep);
dwarf_reg_pool = mempool_create(DWARF_REG_MIN_REQ,
mempool_alloc_slab,
mempool_free_slab,
dwarf_reg_cachep);
err = dwarf_parse_section(__start_eh_frame, __stop_eh_frame, NULL);
if (err)
goto out;
err = unwinder_register(&dwarf_unwinder); err = unwinder_register(&dwarf_unwinder);
if (err) if (err)
goto out; goto out;
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <asm/dwarf.h>
void *module_alloc(unsigned long size) void *module_alloc(unsigned long size)
{ {
...@@ -145,10 +146,41 @@ int module_finalize(const Elf_Ehdr *hdr, ...@@ -145,10 +146,41 @@ int module_finalize(const Elf_Ehdr *hdr,
const Elf_Shdr *sechdrs, const Elf_Shdr *sechdrs,
struct module *me) struct module *me)
{ {
#ifdef CONFIG_DWARF_UNWINDER
unsigned int i, err;
unsigned long start, end;
char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
start = end = 0;
for (i = 1; i < hdr->e_shnum; i++) {
/* Alloc bit cleared means "ignore it." */
if ((sechdrs[i].sh_flags & SHF_ALLOC)
&& !strcmp(secstrings+sechdrs[i].sh_name, ".eh_frame")) {
start = sechdrs[i].sh_addr;
end = start + sechdrs[i].sh_size;
break;
}
}
/* Did we find the .eh_frame section? */
if (i != hdr->e_shnum) {
err = dwarf_parse_section((char *)start, (char *)end, me);
if (err)
printk(KERN_WARNING "%s: failed to parse DWARF info\n",
me->name);
}
#endif /* CONFIG_DWARF_UNWINDER */
return module_bug_finalize(hdr, sechdrs, me); return module_bug_finalize(hdr, sechdrs, me);
} }
void module_arch_cleanup(struct module *mod) void module_arch_cleanup(struct module *mod)
{ {
module_bug_cleanup(mod); module_bug_cleanup(mod);
#ifdef CONFIG_DWARF_UNWINDER
dwarf_module_unload(mod);
#endif /* CONFIG_DWARF_UNWINDER */
} }
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