mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 20:51:03 +02:00
Merge branch 'sh/dwarf-unwinder'
Conflicts: arch/sh/kernel/dwarf.c
This commit is contained in:
@@ -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 {
|
|||||||
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)
|
|||||||
|
|
||||||
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
} 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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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 = {
|
||||||
@@ -873,6 +887,124 @@ static void dwarf_unwinder_cleanup(void)
|
|||||||
kmem_cache_destroy(dwarf_frame_cachep);
|
kmem_cache_destroy(dwarf_frame_cachep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* Parse the information in a .eh_frame section.
|
||||||
|
*/
|
||||||
|
int dwarf_parse_section(char *eh_frame_start, char *eh_frame_end,
|
||||||
|
struct module *mod)
|
||||||
|
{
|
||||||
|
u32 entry_type;
|
||||||
|
void *p, *entry;
|
||||||
|
int count, err = 0;
|
||||||
|
unsigned long len;
|
||||||
|
unsigned int c_entries, f_entries;
|
||||||
|
unsigned char *end;
|
||||||
|
|
||||||
|
c_entries = 0;
|
||||||
|
f_entries = 0;
|
||||||
|
entry = eh_frame_start;
|
||||||
|
|
||||||
|
while ((char *)entry < eh_frame_end) {
|
||||||
|
p = entry;
|
||||||
|
|
||||||
|
count = dwarf_entry_len(p, &len);
|
||||||
|
if (count == 0) {
|
||||||
|
/*
|
||||||
|
* We read a bogus length field value. There is
|
||||||
|
* nothing we can do here apart from disabling
|
||||||
|
* the DWARF unwinder. We can't even skip this
|
||||||
|
* entry and move to the next one because 'len'
|
||||||
|
* tells us where our next entry is.
|
||||||
|
*/
|
||||||
|
err = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
} else
|
||||||
|
p += count;
|
||||||
|
|
||||||
|
/* initial length does not include itself */
|
||||||
|
end = p + len;
|
||||||
|
|
||||||
|
entry_type = get_unaligned((u32 *)p);
|
||||||
|
p += 4;
|
||||||
|
|
||||||
|
if (entry_type == DW_EH_FRAME_CIE) {
|
||||||
|
err = dwarf_parse_cie(entry, p, len, end, mod);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
else
|
||||||
|
c_entries++;
|
||||||
|
} else {
|
||||||
|
err = dwarf_parse_fde(entry, entry_type, p, len,
|
||||||
|
end, mod);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
else
|
||||||
|
f_entries++;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = (char *)entry + len + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk(KERN_INFO "DWARF unwinder initialised: read %u CIEs, %u FDEs\n",
|
||||||
|
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
|
* dwarf_unwinder_init - initialise the dwarf unwinder
|
||||||
*
|
*
|
||||||
@@ -884,19 +1016,10 @@ static void dwarf_unwinder_cleanup(void)
|
|||||||
*/
|
*/
|
||||||
static int __init dwarf_unwinder_init(void)
|
static int __init dwarf_unwinder_init(void)
|
||||||
{
|
{
|
||||||
u32 entry_type;
|
int err;
|
||||||
void *p, *entry;
|
|
||||||
int count, err = 0;
|
|
||||||
unsigned long len;
|
|
||||||
unsigned int c_entries, f_entries;
|
|
||||||
unsigned char *end;
|
|
||||||
INIT_LIST_HEAD(&dwarf_cie_list);
|
INIT_LIST_HEAD(&dwarf_cie_list);
|
||||||
INIT_LIST_HEAD(&dwarf_fde_list);
|
INIT_LIST_HEAD(&dwarf_fde_list);
|
||||||
|
|
||||||
c_entries = 0;
|
|
||||||
f_entries = 0;
|
|
||||||
entry = &__start_eh_frame;
|
|
||||||
|
|
||||||
dwarf_frame_cachep = kmem_cache_create("dwarf_frames",
|
dwarf_frame_cachep = kmem_cache_create("dwarf_frames",
|
||||||
sizeof(struct dwarf_frame), 0,
|
sizeof(struct dwarf_frame), 0,
|
||||||
SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
|
SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
|
||||||
@@ -915,47 +1038,9 @@ static int __init dwarf_unwinder_init(void)
|
|||||||
mempool_free_slab,
|
mempool_free_slab,
|
||||||
dwarf_reg_cachep);
|
dwarf_reg_cachep);
|
||||||
|
|
||||||
while ((char *)entry < __stop_eh_frame) {
|
err = dwarf_parse_section(__start_eh_frame, __stop_eh_frame, NULL);
|
||||||
p = entry;
|
if (err)
|
||||||
|
goto out;
|
||||||
count = dwarf_entry_len(p, &len);
|
|
||||||
if (count == 0) {
|
|
||||||
/*
|
|
||||||
* We read a bogus length field value. There is
|
|
||||||
* nothing we can do here apart from disabling
|
|
||||||
* the DWARF unwinder. We can't even skip this
|
|
||||||
* entry and move to the next one because 'len'
|
|
||||||
* tells us where our next entry is.
|
|
||||||
*/
|
|
||||||
goto out;
|
|
||||||
} else
|
|
||||||
p += count;
|
|
||||||
|
|
||||||
/* initial length does not include itself */
|
|
||||||
end = p + len;
|
|
||||||
|
|
||||||
entry_type = get_unaligned((u32 *)p);
|
|
||||||
p += 4;
|
|
||||||
|
|
||||||
if (entry_type == DW_EH_FRAME_CIE) {
|
|
||||||
err = dwarf_parse_cie(entry, p, len, end);
|
|
||||||
if (err < 0)
|
|
||||||
goto out;
|
|
||||||
else
|
|
||||||
c_entries++;
|
|
||||||
} else {
|
|
||||||
err = dwarf_parse_fde(entry, entry_type, p, len, end);
|
|
||||||
if (err < 0)
|
|
||||||
goto out;
|
|
||||||
else
|
|
||||||
f_entries++;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = (char *)entry + len + 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
printk(KERN_INFO "DWARF unwinder initialised: read %u CIEs, %u FDEs\n",
|
|
||||||
c_entries, f_entries);
|
|
||||||
|
|
||||||
err = unwinder_register(&dwarf_unwinder);
|
err = unwinder_register(&dwarf_unwinder);
|
||||||
if (err)
|
if (err)
|
||||||
|
@@ -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,
|
|||||||
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 */
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user