From 97f361e2498ada54b48a235619eaf5af8e46427e Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 17 Aug 2009 05:07:38 +0900 Subject: sh: unwinder: Move initialization to early_initcall() and tidy up locking. This moves the initialization over to an early_initcall(). This fixes up some lockdep interaction issues. At the same time, kill off some superfluous locking in the init path. Signed-off-by: Paul Mundt --- arch/sh/kernel/dwarf.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) (limited to 'arch/sh/kernel/dwarf.c') diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index 5fd6e604816d..d0652153f576 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -26,10 +26,10 @@ #include static LIST_HEAD(dwarf_cie_list); -DEFINE_SPINLOCK(dwarf_cie_lock); +static DEFINE_SPINLOCK(dwarf_cie_lock); static LIST_HEAD(dwarf_fde_list); -DEFINE_SPINLOCK(dwarf_fde_lock); +static DEFINE_SPINLOCK(dwarf_fde_lock); static struct dwarf_cie *cached_cie; @@ -264,7 +264,7 @@ static inline int dwarf_entry_len(char *addr, unsigned long *len) */ static struct dwarf_cie *dwarf_lookup_cie(unsigned long cie_ptr) { - struct dwarf_cie *cie, *n; + struct dwarf_cie *cie; unsigned long flags; spin_lock_irqsave(&dwarf_cie_lock, flags); @@ -278,7 +278,7 @@ static struct dwarf_cie *dwarf_lookup_cie(unsigned long cie_ptr) goto out; } - list_for_each_entry_safe(cie, n, &dwarf_cie_list, link) { + list_for_each_entry(cie, &dwarf_cie_list, link) { if (cie->cie_pointer == cie_ptr) { cached_cie = cie; break; @@ -299,11 +299,12 @@ out: */ struct dwarf_fde *dwarf_lookup_fde(unsigned long pc) { + struct dwarf_fde *fde; unsigned long flags; - struct dwarf_fde *fde, *n; spin_lock_irqsave(&dwarf_fde_lock, flags); - list_for_each_entry_safe(fde, n, &dwarf_fde_list, link) { + + list_for_each_entry(fde, &dwarf_fde_list, link) { unsigned long start, end; start = fde->initial_location; @@ -787,24 +788,19 @@ static struct unwinder dwarf_unwinder = { static void dwarf_unwinder_cleanup(void) { - struct dwarf_cie *cie, *m; - struct dwarf_fde *fde, *n; - unsigned long flags; + struct dwarf_cie *cie; + struct dwarf_fde *fde; /* * Deallocate all the memory allocated for the DWARF unwinder. * Traverse all the FDE/CIE lists and remove and free all the * memory associated with those data structures. */ - spin_lock_irqsave(&dwarf_cie_lock, flags); - list_for_each_entry_safe(cie, m, &dwarf_cie_list, link) + list_for_each_entry(cie, &dwarf_cie_list, link) kfree(cie); - spin_unlock_irqrestore(&dwarf_cie_lock, flags); - spin_lock_irqsave(&dwarf_fde_lock, flags); - list_for_each_entry_safe(fde, n, &dwarf_fde_list, link) + list_for_each_entry(fde, &dwarf_fde_list, link) kfree(fde); - spin_unlock_irqrestore(&dwarf_fde_lock, flags); } /** @@ -816,7 +812,7 @@ static void dwarf_unwinder_cleanup(void) * easy to lookup the FDE for a given PC, so we build a list of FDE * and CIE entries that make it easier. */ -void dwarf_unwinder_init(void) +static int __init dwarf_unwinder_init(void) { u32 entry_type; void *p, *entry; @@ -877,9 +873,11 @@ void dwarf_unwinder_init(void) if (err) goto out; - return; + return 0; out: printk(KERN_ERR "Failed to initialise DWARF unwinder: %d\n", err); dwarf_unwinder_cleanup(); + return -EINVAL; } +early_initcall(dwarf_unwinder_init); -- cgit v1.2.3 From fb3f3e7fc6d4afb32f9eba32124beaf40313de3c Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sun, 16 Aug 2009 15:44:08 +0100 Subject: sh: unwinder: Fix memory leak and create our own kmem cache Plug a memory leak in dwarf_unwinder_dump() where we didn't free the memory that we had previously allocated for the DWARF frames and DWARF registers. Now is also a opportune time to implement our own mempool and kmem cache. It's a good idea to have a certain number of frame and register objects in reserve at all times, so that we are guaranteed to have our allocation satisfied even when memory is scarce. Since we have pools to allocate from we can implement the registers for each frame as a linked list as opposed to a sparsely populated array. Whilst it's true that the lookup time for a linked list is larger than for arrays, there's only usually a maximum of 8 registers per frame. So the overhead isn't that much of a concern. Signed-off-by: Matt Fleming --- arch/sh/kernel/dwarf.c | 201 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 136 insertions(+), 65 deletions(-) (limited to 'arch/sh/kernel/dwarf.c') diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index d0652153f576..e4810375207d 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,17 @@ #include #include +/* Reserve enough memory for two stack frames */ +#define DWARF_FRAME_MIN_REQ 2 +/* ... with 4 registers per frame. */ +#define DWARF_REG_MIN_REQ (DWARF_FRAME_MIN_REQ * 4) + +static struct kmem_cache *dwarf_frame_cachep; +static mempool_t *dwarf_frame_pool; + +static struct kmem_cache *dwarf_reg_cachep; +static mempool_t *dwarf_reg_pool; + static LIST_HEAD(dwarf_cie_list); static DEFINE_SPINLOCK(dwarf_cie_lock); @@ -33,33 +45,25 @@ static DEFINE_SPINLOCK(dwarf_fde_lock); static struct dwarf_cie *cached_cie; -/* - * Figure out whether we need to allocate some dwarf registers. If dwarf - * registers have already been allocated then we may need to realloc - * them. "reg" is a register number that we need to be able to access - * after this call. +/** + * dwarf_frame_alloc_reg - allocate memory for a DWARF register + * @frame: the DWARF frame whose list of registers we insert on + * @reg_num: the register number + * + * Allocate space for, and initialise, a dwarf reg from + * dwarf_reg_pool and insert it onto the (unsorted) linked-list of + * dwarf registers for @frame. * - * Register numbers start at zero, therefore we need to allocate space - * for "reg" + 1 registers. + * Return the initialised DWARF reg. */ -static void dwarf_frame_alloc_regs(struct dwarf_frame *frame, - unsigned int reg) +static struct dwarf_reg *dwarf_frame_alloc_reg(struct dwarf_frame *frame, + unsigned int reg_num) { - struct dwarf_reg *regs; - unsigned int num_regs = reg + 1; - size_t new_size; - size_t old_size; + struct dwarf_reg *reg; - new_size = num_regs * sizeof(*regs); - old_size = frame->num_regs * sizeof(*regs); - - /* Fast path: don't allocate any regs if we've already got enough. */ - if (frame->num_regs >= num_regs) - return; - - regs = kzalloc(new_size, GFP_ATOMIC); - if (!regs) { - printk(KERN_WARNING "Unable to allocate DWARF registers\n"); + reg = mempool_alloc(dwarf_reg_pool, GFP_ATOMIC); + if (!reg) { + printk(KERN_WARNING "Unable to allocate a DWARF register\n"); /* * Let's just bomb hard here, we have no way to * gracefully recover. @@ -67,13 +71,44 @@ static void dwarf_frame_alloc_regs(struct dwarf_frame *frame, BUG(); } - if (frame->regs) { - memcpy(regs, frame->regs, old_size); - kfree(frame->regs); + reg->number = reg_num; + reg->addr = 0; + reg->flags = 0; + + list_add(®->link, &frame->reg_list); + + return reg; +} + +static void dwarf_frame_free_regs(struct dwarf_frame *frame) +{ + struct dwarf_reg *reg, *n; + + list_for_each_entry_safe(reg, n, &frame->reg_list, link) { + list_del(®->link); + mempool_free(reg, dwarf_reg_pool); + } +} + +/** + * dwarf_frame_reg - return a DWARF register + * @frame: the DWARF frame to search in for @reg_num + * @reg_num: the register number to search for + * + * Lookup and return the dwarf reg @reg_num for this frame. Return + * NULL if @reg_num is an register invalid number. + */ +static struct dwarf_reg *dwarf_frame_reg(struct dwarf_frame *frame, + unsigned int reg_num) +{ + struct dwarf_reg *reg; + + list_for_each_entry(reg, &frame->reg_list, link) { + if (reg->number == reg_num) + return reg; } - frame->regs = regs; - frame->num_regs = num_regs; + return NULL; } /** @@ -347,6 +382,7 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, unsigned char insn; unsigned char *current_insn; unsigned int count, delta, reg, expr_len, offset; + struct dwarf_reg *regp; current_insn = insn_start; @@ -369,9 +405,9 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, count = dwarf_read_uleb128(current_insn, &offset); current_insn += count; offset *= cie->data_alignment_factor; - dwarf_frame_alloc_regs(frame, reg); - frame->regs[reg].addr = offset; - frame->regs[reg].flags |= DWARF_REG_OFFSET; + regp = dwarf_frame_alloc_reg(frame, reg); + regp->addr = offset; + regp->flags |= DWARF_REG_OFFSET; continue; /* NOTREACHED */ case DW_CFA_restore: @@ -453,17 +489,18 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, count = dwarf_read_leb128(current_insn, &offset); current_insn += count; offset *= cie->data_alignment_factor; - dwarf_frame_alloc_regs(frame, reg); - frame->regs[reg].flags |= DWARF_REG_OFFSET; - frame->regs[reg].addr = offset; + regp = dwarf_frame_alloc_reg(frame, reg); + regp->flags |= DWARF_REG_OFFSET; + regp->addr = offset; break; case DW_CFA_val_offset: count = dwarf_read_uleb128(current_insn, ®); current_insn += count; count = dwarf_read_leb128(current_insn, &offset); offset *= cie->data_alignment_factor; - frame->regs[reg].flags |= DWARF_REG_OFFSET; - frame->regs[reg].addr = offset; + regp = dwarf_frame_alloc_reg(frame, reg); + regp->flags |= DWARF_REG_OFFSET; + regp->addr = offset; break; case DW_CFA_GNU_args_size: count = dwarf_read_uleb128(current_insn, &offset); @@ -474,9 +511,10 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, current_insn += count; count = dwarf_read_uleb128(current_insn, &offset); offset *= cie->data_alignment_factor; - dwarf_frame_alloc_regs(frame, reg); - frame->regs[reg].flags |= DWARF_REG_OFFSET; - frame->regs[reg].addr = -offset; + + regp = dwarf_frame_alloc_reg(frame, reg); + regp->flags |= DWARF_REG_OFFSET; + regp->addr = -offset; break; default: pr_debug("unhandled DWARF instruction 0x%x\n", insn); @@ -502,8 +540,8 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, struct dwarf_frame *frame; struct dwarf_cie *cie; struct dwarf_fde *fde; + struct dwarf_reg *reg; unsigned long addr; - int i, offset; /* * If this is the first invocation of this recursive function we @@ -516,11 +554,16 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, if (!pc && !prev) pc = (unsigned long)current_text_addr(); - frame = kzalloc(sizeof(*frame), GFP_ATOMIC); - if (!frame) - return NULL; + frame = mempool_alloc(dwarf_frame_pool, GFP_ATOMIC); + if (!frame) { + printk(KERN_ERR "Unable to allocate a dwarf frame\n"); + BUG(); + } + INIT_LIST_HEAD(&frame->reg_list); + frame->flags = 0; frame->prev = prev; + frame->return_addr = 0; fde = dwarf_lookup_fde(pc); if (!fde) { @@ -540,7 +583,7 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, * case above, which sucks because we could print a * warning here. */ - return NULL; + goto bail; } cie = dwarf_lookup_cie(fde->cie_pointer); @@ -560,10 +603,10 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, switch (frame->flags) { case DWARF_FRAME_CFA_REG_OFFSET: if (prev) { - BUG_ON(!prev->regs[frame->cfa_register].flags); + reg = dwarf_frame_reg(prev, frame->cfa_register); + BUG_ON(!reg); - addr = prev->cfa; - addr += prev->regs[frame->cfa_register].addr; + addr = prev->cfa + reg->addr; frame->cfa = __raw_readl(addr); } else { @@ -584,23 +627,18 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, } /* If we haven't seen the return address reg, we're screwed. */ - BUG_ON(!frame->regs[DWARF_ARCH_RA_REG].flags); - - for (i = 0; i <= frame->num_regs; i++) { - struct dwarf_reg *reg = &frame->regs[i]; - - if (!reg->flags) - continue; + reg = dwarf_frame_reg(frame, DWARF_ARCH_RA_REG); + BUG_ON(!reg); - offset = reg->addr; - offset += frame->cfa; - } - - addr = frame->cfa + frame->regs[DWARF_ARCH_RA_REG].addr; + addr = frame->cfa + reg->addr; frame->return_addr = __raw_readl(addr); - frame->next = dwarf_unwind_stack(frame->return_addr, frame); return frame; + +bail: + dwarf_frame_free_regs(frame); + mempool_free(frame, dwarf_frame_pool); + return NULL; } static int dwarf_parse_cie(void *entry, void *p, unsigned long len, @@ -770,14 +808,29 @@ static void dwarf_unwinder_dump(struct task_struct *task, struct pt_regs *regs, unsigned long *sp, const struct stacktrace_ops *ops, void *data) { - struct dwarf_frame *frame; + struct dwarf_frame *frame, *_frame; + unsigned long return_addr; + + _frame = NULL; + return_addr = 0; - frame = dwarf_unwind_stack(0, NULL); + while (1) { + frame = dwarf_unwind_stack(return_addr, _frame); + + if (_frame) { + dwarf_frame_free_regs(_frame); + mempool_free(_frame, dwarf_frame_pool); + } + + _frame = frame; + + if (!frame || !frame->return_addr) + break; - while (frame && frame->return_addr) { - ops->address(data, frame->return_addr, 1); - frame = frame->next; + return_addr = frame->return_addr; + ops->address(data, return_addr, 1); } + } static struct unwinder dwarf_unwinder = { @@ -801,6 +854,9 @@ static void dwarf_unwinder_cleanup(void) list_for_each_entry(fde, &dwarf_fde_list, link) kfree(fde); + + kmem_cache_destroy(dwarf_reg_cachep); + kmem_cache_destroy(dwarf_frame_cachep); } /** @@ -827,6 +883,21 @@ static int __init dwarf_unwinder_init(void) 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", + sizeof(struct dwarf_reg), 0, SLAB_PANIC, 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); + while ((char *)entry < __stop_eh_frame) { p = entry; -- cgit v1.2.3 From 97efbbd5886e27b61c19c77d41f6491f5d96fbd0 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sun, 16 Aug 2009 15:56:35 +0100 Subject: sh: unwinder: Set the flags for DW_CFA_val_offset ops as DWARF_VAL_OFFSET The handling of DW_CFA_val_offset ops was incorrectly using the DWARF_REG_OFFSET flag but the register's value cannot be calculated using the DWARF_REG_OFFSET method. Create a new flag to indicate that a different method must be used to calculate the register's value even though there is no implementation for DWARF_VAL_OFFSET yet; it's mainly just a place holder. Signed-off-by: Matt Fleming --- arch/sh/kernel/dwarf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'arch/sh/kernel/dwarf.c') diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index e4810375207d..d271d04adccd 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -11,6 +11,7 @@ * * TODO: * - DWARF64 doesn't work. + * - Registers with DWARF_VAL_OFFSET rules aren't handled properly. */ /* #define DEBUG */ @@ -499,7 +500,7 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, count = dwarf_read_leb128(current_insn, &offset); offset *= cie->data_alignment_factor; regp = dwarf_frame_alloc_reg(frame, reg); - regp->flags |= DWARF_REG_OFFSET; + regp->flags |= DWARF_VAL_OFFSET; regp->addr = offset; break; case DW_CFA_GNU_args_size: -- cgit v1.2.3 From b344e24a8e8ceda83d1285d22e3e5baf4f5e42d3 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sun, 16 Aug 2009 21:54:48 +0100 Subject: sh: unwinder: Introduce UNWINDER_BUG() and UNWINDER_BUG_ON() We can't assume that if we execute the unwinder code and the unwinder was already running that it has faulted. Clearly two kernel threads can invoke the unwinder at the same time and may be running simultaneously. The previous approach used BUG() and BUG_ON() in the unwinder code to detect whether the unwinder was incapable of unwinding the stack, and that the next available unwinder should be used instead. A better approach is to explicitly invoke a trap handler to switch unwinders when the current unwinder cannot continue. Signed-off-by: Matt Fleming --- arch/sh/kernel/dwarf.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) (limited to 'arch/sh/kernel/dwarf.c') diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index d271d04adccd..606ece37eb42 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -69,7 +69,7 @@ static struct dwarf_reg *dwarf_frame_alloc_reg(struct dwarf_frame *frame, * Let's just bomb hard here, we have no way to * gracefully recover. */ - BUG(); + UNWINDER_BUG(); } reg->number = reg_num; @@ -232,7 +232,7 @@ static int dwarf_read_encoded_value(char *addr, unsigned long *val, break; default: pr_debug("encoding=0x%x\n", (encoding & 0x70)); - BUG(); + UNWINDER_BUG(); } if ((encoding & 0x07) == 0x00) @@ -247,7 +247,7 @@ static int dwarf_read_encoded_value(char *addr, unsigned long *val, break; default: pr_debug("encoding=0x%x\n", encoding); - BUG(); + UNWINDER_BUG(); } return count; @@ -519,6 +519,7 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, break; default: pr_debug("unhandled DWARF instruction 0x%x\n", insn); + UNWINDER_BUG(); break; } } @@ -535,8 +536,8 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, * on the callstack. Each of the lower (older) stack frames are * linked via the "prev" member. */ -struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, - struct dwarf_frame *prev) +struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, + struct dwarf_frame *prev) { struct dwarf_frame *frame; struct dwarf_cie *cie; @@ -558,7 +559,7 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, frame = mempool_alloc(dwarf_frame_pool, GFP_ATOMIC); if (!frame) { printk(KERN_ERR "Unable to allocate a dwarf frame\n"); - BUG(); + UNWINDER_BUG(); } INIT_LIST_HEAD(&frame->reg_list); @@ -605,7 +606,8 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, case DWARF_FRAME_CFA_REG_OFFSET: if (prev) { reg = dwarf_frame_reg(prev, frame->cfa_register); - BUG_ON(!reg); + UNWINDER_BUG_ON(!reg); + UNWINDER_BUG_ON(reg->flags != DWARF_REG_OFFSET); addr = prev->cfa + reg->addr; frame->cfa = __raw_readl(addr); @@ -624,12 +626,13 @@ struct dwarf_frame *dwarf_unwind_stack(unsigned long pc, frame->cfa += frame->cfa_offset; break; default: - BUG(); + UNWINDER_BUG(); } /* If we haven't seen the return address reg, we're screwed. */ reg = dwarf_frame_reg(frame, DWARF_ARCH_RA_REG); - BUG_ON(!reg); + UNWINDER_BUG_ON(!reg); + UNWINDER_BUG_ON(reg->flags != DWARF_REG_OFFSET); addr = frame->cfa + reg->addr; frame->return_addr = __raw_readl(addr); @@ -664,7 +667,7 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, cie->cie_pointer = (unsigned long)entry; cie->version = *(char *)p++; - BUG_ON(cie->version != 1); + UNWINDER_BUG_ON(cie->version != 1); cie->augmentation = p; p += strlen(cie->augmentation) + 1; @@ -694,7 +697,7 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, count = dwarf_read_uleb128(p, &length); p += count; - BUG_ON((unsigned char *)p > end); + UNWINDER_BUG_ON((unsigned char *)p > end); cie->initial_instructions = p + length; cie->augmentation++; @@ -722,16 +725,16 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len, * routine in the CIE * augmentation. */ - BUG(); + UNWINDER_BUG(); } else if (*cie->augmentation == 'S') { - BUG(); + UNWINDER_BUG(); } else { /* * Unknown augmentation. Assume * 'z' augmentation. */ p = cie->initial_instructions; - BUG_ON(!p); + UNWINDER_BUG_ON(!p); break; } } @@ -805,9 +808,11 @@ static int dwarf_parse_fde(void *entry, u32 entry_type, return 0; } -static void dwarf_unwinder_dump(struct task_struct *task, struct pt_regs *regs, +static void dwarf_unwinder_dump(struct task_struct *task, + struct pt_regs *regs, unsigned long *sp, - const struct stacktrace_ops *ops, void *data) + const struct stacktrace_ops *ops, + void *data) { struct dwarf_frame *frame, *_frame; unsigned long return_addr; @@ -831,7 +836,6 @@ static void dwarf_unwinder_dump(struct task_struct *task, struct pt_regs *regs, return_addr = frame->return_addr; ops->address(data, return_addr, 1); } - } static struct unwinder dwarf_unwinder = { -- cgit v1.2.3 From 5480675dc60c7dda7146e506981b2b40a775cc1e Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 20 Aug 2009 19:42:34 +0100 Subject: sh: Fix bug calculating the end of the FDE instructions The 'end' member of struct dwarf_fde denotes one byte past the end of the CFA instruction stream for an FDE. The value of 'end' was being calcualted incorrectly, it was being set too high. This resulted in dwarf_cfa_execute_insns() interpreting data past the end of valid instructions, thus causing all sorts of weird crashes. Signed-off-by: Matt Fleming --- arch/sh/kernel/dwarf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'arch/sh/kernel/dwarf.c') diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index 606ece37eb42..e6f427cff5ba 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -751,7 +751,8 @@ 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) + void *start, unsigned long len, + unsigned char *end) { struct dwarf_fde *fde; struct dwarf_cie *cie; @@ -798,7 +799,7 @@ static int dwarf_parse_fde(void *entry, u32 entry_type, /* Call frame instructions. */ fde->instructions = p; - fde->end = start + len; + fde->end = end; /* Add to list. */ spin_lock_irqsave(&dwarf_fde_lock, flags); @@ -932,7 +933,7 @@ static int __init dwarf_unwinder_init(void) else c_entries++; } else { - err = dwarf_parse_fde(entry, entry_type, p, len); + err = dwarf_parse_fde(entry, entry_type, p, len, end); if (err < 0) goto out; else -- cgit v1.2.3 From 5580e9044df9c0e87861739d8c527006ead92e52 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 20 Aug 2009 19:53:49 +0100 Subject: sh: Handle the DWARF op, DW_CFA_undefined Allow a DWARF register to have an undefined value. When applied to the DWARF return address register this lets lets us label a function as having no direct caller, e.g. kernel_thread_helper(). Signed-off-by: Matt Fleming --- arch/sh/kernel/dwarf.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'arch/sh/kernel/dwarf.c') diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index e6f427cff5ba..577302f31e6a 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -452,6 +452,8 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start, case DW_CFA_undefined: count = dwarf_read_uleb128(current_insn, ®); current_insn += count; + regp = dwarf_frame_alloc_reg(frame, reg); + regp->flags |= DWARF_UNDEFINED; break; case DW_CFA_def_cfa: count = dwarf_read_uleb128(current_insn, @@ -629,9 +631,16 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc, UNWINDER_BUG(); } - /* If we haven't seen the return address reg, we're screwed. */ reg = dwarf_frame_reg(frame, DWARF_ARCH_RA_REG); - UNWINDER_BUG_ON(!reg); + + /* + * If we haven't seen the return address register or the return + * address column is undefined then we must assume that this is + * the end of the callstack. + */ + if (!reg || reg->flags == DWARF_UNDEFINED) + goto bail; + UNWINDER_BUG_ON(reg->flags != DWARF_REG_OFFSET); addr = frame->cfa + reg->addr; -- cgit v1.2.3 From 4f896ffca2b72f4b719746e7fbb0b623252e6ac9 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Sat, 22 Aug 2009 19:03:25 +0900 Subject: sh: unwinder: cacheline align slab cache objects. The CIE and FDE structs are big enough and accessed regularly enough in certain configurations to make cacheline alignment useful. Signed-off-by: Paul Mundt --- arch/sh/kernel/dwarf.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'arch/sh/kernel/dwarf.c') diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index 577302f31e6a..700f7e0fd658 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -899,9 +899,12 @@ static int __init dwarf_unwinder_init(void) entry = &__start_eh_frame; dwarf_frame_cachep = kmem_cache_create("dwarf_frames", - sizeof(struct dwarf_frame), 0, SLAB_PANIC, NULL); + 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, NULL); + 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, -- cgit v1.2.3 From 2f6dafc5fcbf3fddce345c47da1f277a156fe22a Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 31 Aug 2009 13:47:06 +0900 Subject: sh: unwinder: Fix up uninitialized variable warnings on sh2a build. A couple of these popped up on the sh2a build, causing build failures. Signed-off-by: Paul Mundt --- arch/sh/kernel/dwarf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/sh/kernel/dwarf.c') diff --git a/arch/sh/kernel/dwarf.c b/arch/sh/kernel/dwarf.c index 700f7e0fd658..bc4d8d75332b 100644 --- a/arch/sh/kernel/dwarf.c +++ b/arch/sh/kernel/dwarf.c @@ -887,7 +887,7 @@ static int __init dwarf_unwinder_init(void) { u32 entry_type; void *p, *entry; - int count, err; + int count, err = 0; unsigned long len; unsigned int c_entries, f_entries; unsigned char *end; -- cgit v1.2.3