From a6a2f2ad67506090e332f440457553c0ec011d68 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 9 Oct 2009 23:20:54 +0100 Subject: [PATCH 1/3] sh: Teach the DWARF unwinder about modules Pass a module's .eh_frame section to the DWARF unwinder at module load time so that the section's FDEs and CIEs can be registered with the DWARF unwinder. This allows us to unwind the stack through module code when generating backtraces. Signed-off-by: Matt Fleming --- arch/sh/include/asm/dwarf.h | 15 +++ arch/sh/kernel/dwarf.c | 181 +++++++++++++++++++++++++----------- arch/sh/kernel/module.c | 32 +++++++ 3 files changed, 175 insertions(+), 53 deletions(-) diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h index c367ed3373c5..aacdc746d07c 100644 --- a/arch/sh/include/asm/dwarf.h +++ b/arch/sh/include/asm/dwarf.h @@ -241,6 +241,12 @@ struct dwarf_cie { unsigned long flags; #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 *end; 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,9 @@ static inline unsigned int DW_CFA_operand(unsigned long insn) extern struct dwarf_frame *dwarf_unwind_stack(unsigned long, struct dwarf_frame *); +extern int dwarf_parse_section(char *, char *, struct module *); +extern void dwarf_module_unload(struct module *); + #endif /* !__ASSEMBLY__ */ #define CFI_STARTPROC .cfi_startproc diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index 577302f31e6a..981315c6d656 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -655,7 +655,7 @@ bail: } 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; unsigned long flags; @@ -751,6 +751,8 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, cie->initial_instructions = p; cie->instructions_end = end; + cie->mod = mod; + /* Add to list */ spin_lock_irqsave(&dwarf_cie_lock, flags); list_add_tail(&cie->link, &dwarf_cie_list); @@ -761,7 +763,7 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, static int dwarf_parse_fde(void *entry, u32 entry_type, void *start, unsigned long len, - unsigned char *end) + unsigned char *end, struct module *mod) { struct dwarf_fde *fde; struct dwarf_cie *cie; @@ -810,6 +812,8 @@ static int dwarf_parse_fde(void *entry, u32 entry_type, fde->instructions = p; fde->end = end; + fde->mod = mod; + /* Add to list. */ spin_lock_irqsave(&dwarf_fde_lock, flags); list_add_tail(&fde->link, &dwarf_fde_list); @@ -874,6 +878,124 @@ static void dwarf_unwinder_cleanup(void) 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; + 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 * @@ -885,19 +1007,10 @@ static void dwarf_unwinder_cleanup(void) */ static int __init dwarf_unwinder_init(void) { - u32 entry_type; - void *p, *entry; - int count, err; - unsigned long len; - unsigned int c_entries, f_entries; - unsigned char *end; + int err; INIT_LIST_HEAD(&dwarf_cie_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", sizeof(struct dwarf_frame), 0, SLAB_PANIC, NULL); dwarf_reg_cachep = kmem_cache_create("dwarf_regs", @@ -913,47 +1026,9 @@ static int __init dwarf_unwinder_init(void) mempool_free_slab, dwarf_reg_cachep); - while ((char *)entry < __stop_eh_frame) { - 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. - */ - 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 = dwarf_parse_section(__start_eh_frame, __stop_eh_frame, NULL); + if (err) + goto out; err = unwinder_register(&dwarf_unwinder); if (err) diff --git a/arch/sh/kernel/module.c b/arch/sh/kernel/module.c index c2efdcde266f..d297a148d16c 100644 --- a/arch/sh/kernel/module.c +++ b/arch/sh/kernel/module.c @@ -32,6 +32,7 @@ #include #include #include +#include void *module_alloc(unsigned long size) { @@ -145,10 +146,41 @@ int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, 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); } void module_arch_cleanup(struct module *mod) { module_bug_cleanup(mod); + +#ifdef CONFIG_DWARF_UNWINDER + dwarf_module_unload(mod); +#endif /* CONFIG_DWARF_UNWINDER */ } From ed4fe7f488008f38d5f423f0bcc736b1779d6ddc Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sat, 10 Oct 2009 16:03:11 +0100 Subject: [PATCH 2/3] sh: Fix memory leak in dwarf_unwind_stack() If we broke out of the while (1) loop because the return address of "frame" was zero, then "frame" needs to be free'd before we return. Signed-off-by: Matt Fleming --- arch/sh/include/asm/dwarf.h | 1 + arch/sh/kernel/dwarf.c | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h index aacdc746d07c..eef87539963d 100644 --- a/arch/sh/include/asm/dwarf.h +++ b/arch/sh/include/asm/dwarf.h @@ -376,6 +376,7 @@ static inline unsigned int DW_CFA_operand(unsigned long insn) extern struct dwarf_frame *dwarf_unwind_stack(unsigned long, 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 *); diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index 981315c6d656..ce8bff45d72c 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -529,6 +529,16 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, return 0; } +/** + * 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 - recursively unwind the stack * @pc: address of the function to unwind @@ -649,8 +659,7 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, return frame; bail: - dwarf_frame_free_regs(frame); - mempool_free(frame, dwarf_frame_pool); + dwarf_free_frame(frame); return NULL; } @@ -837,10 +846,8 @@ static void dwarf_unwinder_dump(struct task_struct *task, while (1) { frame = dwarf_unwind_stack(return_addr, _frame); - if (_frame) { - dwarf_frame_free_regs(_frame); - mempool_free(_frame, dwarf_frame_pool); - } + if (_frame) + dwarf_free_frame(_frame); _frame = frame; @@ -850,6 +857,9 @@ static void dwarf_unwinder_dump(struct task_struct *task, return_addr = frame->return_addr; ops->address(data, return_addr, 1); } + + if (frame) + dwarf_free_frame(frame); } static struct unwinder dwarf_unwinder = { From c2d474d6f8b48b6698343cfc1a3630c4647aa7b2 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sat, 10 Oct 2009 16:17:06 +0100 Subject: [PATCH 3/3] sh: Remove any reference to recursive functions from comments Originally, dwarf_unwind_stack() was a recursive function and it seems that some of the old comments were never updated. Signed-off-by: Matt Fleming --- arch/sh/kernel/dwarf.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index ce8bff45d72c..f242cd120cf1 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -540,7 +540,8 @@ void dwarf_free_frame(struct dwarf_frame *frame) } /** - * dwarf_unwind_stack - recursively unwind the stack + * dwarf_unwind_stack - unwind the stack + * * @pc: address of the function to unwind * @prev: struct dwarf_frame of the previous stackframe on the callstack * @@ -558,9 +559,9 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, unsigned long addr; /* - * If this is the first invocation of this recursive function we - * need get the contents of a physical register to get the CFA - * in order to begin the virtual unwinding of the stack. + * If we're starting at the top of the stack we need get the + * contents of a physical register to get the CFA in order to + * begin the virtual unwinding of the stack. * * NOTE: the return address is guaranteed to be setup by the * time this function makes its first function call. @@ -582,9 +583,8 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, fde = dwarf_lookup_fde(pc); if (!fde) { /* - * This is our normal exit path - the one that stops the - * recursion. There's two reasons why we might exit - * here, + * This is our normal exit path. There are two reasons + * why we might exit here, * * a) pc has no asscociated DWARF frame info and so * we don't know how to unwind this frame. This is @@ -626,10 +626,10 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, } else { /* - * Again, this is the first invocation of this - * recurisve function. We need to physically - * read the contents of a register in order to - * get the Canonical Frame Address for this + * Again, we're starting from the top of the + * stack. We need to physically read + * the contents of a register in order to get + * the Canonical Frame Address for this * function. */ frame->cfa = dwarf_read_arch_reg(frame->cfa_register);