From bd353861c735b2265c9d8b2559960c693e7c68ab Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 14 Aug 2009 01:58:43 +0900 Subject: sh: dwarf unwinder support. This is a first cut at a generic DWARF unwinder for the kernel. It's still lacking DWARF64 support and the DWARF expression support hasn't been tested very well but it is generating proper stacktraces on SH for WARN_ON() and NULL dereferences. Signed-off-by: Matt Fleming Signed-off-by: Paul Mundt --- arch/sh/include/asm/dwarf.h | 407 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 407 insertions(+) create mode 100644 arch/sh/include/asm/dwarf.h (limited to 'arch/sh/include/asm/dwarf.h') diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h new file mode 100644 index 000000000000..23eba12880b3 --- /dev/null +++ b/arch/sh/include/asm/dwarf.h @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2009 Matt Fleming + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + */ +#ifndef __ASM_SH_DWARF_H +#define __ASM_SH_DWARF_H + +#ifdef CONFIG_DWARF_UNWINDER + +/* + * DWARF expression operations + */ +#define DW_OP_addr 0x03 +#define DW_OP_deref 0x06 +#define DW_OP_const1u 0x08 +#define DW_OP_const1s 0x09 +#define DW_OP_const2u 0x0a +#define DW_OP_const2s 0x0b +#define DW_OP_const4u 0x0c +#define DW_OP_const4s 0x0d +#define DW_OP_const8u 0x0e +#define DW_OP_const8s 0x0f +#define DW_OP_constu 0x10 +#define DW_OP_consts 0x11 +#define DW_OP_dup 0x12 +#define DW_OP_drop 0x13 +#define DW_OP_over 0x14 +#define DW_OP_pick 0x15 +#define DW_OP_swap 0x16 +#define DW_OP_rot 0x17 +#define DW_OP_xderef 0x18 +#define DW_OP_abs 0x19 +#define DW_OP_and 0x1a +#define DW_OP_div 0x1b +#define DW_OP_minus 0x1c +#define DW_OP_mod 0x1d +#define DW_OP_mul 0x1e +#define DW_OP_neg 0x1f +#define DW_OP_not 0x20 +#define DW_OP_or 0x21 +#define DW_OP_plus 0x22 +#define DW_OP_plus_uconst 0x23 +#define DW_OP_shl 0x24 +#define DW_OP_shr 0x25 +#define DW_OP_shra 0x26 +#define DW_OP_xor 0x27 +#define DW_OP_skip 0x2f +#define DW_OP_bra 0x28 +#define DW_OP_eq 0x29 +#define DW_OP_ge 0x2a +#define DW_OP_gt 0x2b +#define DW_OP_le 0x2c +#define DW_OP_lt 0x2d +#define DW_OP_ne 0x2e +#define DW_OP_lit0 0x30 +#define DW_OP_lit1 0x31 +#define DW_OP_lit2 0x32 +#define DW_OP_lit3 0x33 +#define DW_OP_lit4 0x34 +#define DW_OP_lit5 0x35 +#define DW_OP_lit6 0x36 +#define DW_OP_lit7 0x37 +#define DW_OP_lit8 0x38 +#define DW_OP_lit9 0x39 +#define DW_OP_lit10 0x3a +#define DW_OP_lit11 0x3b +#define DW_OP_lit12 0x3c +#define DW_OP_lit13 0x3d +#define DW_OP_lit14 0x3e +#define DW_OP_lit15 0x3f +#define DW_OP_lit16 0x40 +#define DW_OP_lit17 0x41 +#define DW_OP_lit18 0x42 +#define DW_OP_lit19 0x43 +#define DW_OP_lit20 0x44 +#define DW_OP_lit21 0x45 +#define DW_OP_lit22 0x46 +#define DW_OP_lit23 0x47 +#define DW_OP_lit24 0x48 +#define DW_OP_lit25 0x49 +#define DW_OP_lit26 0x4a +#define DW_OP_lit27 0x4b +#define DW_OP_lit28 0x4c +#define DW_OP_lit29 0x4d +#define DW_OP_lit30 0x4e +#define DW_OP_lit31 0x4f +#define DW_OP_reg0 0x50 +#define DW_OP_reg1 0x51 +#define DW_OP_reg2 0x52 +#define DW_OP_reg3 0x53 +#define DW_OP_reg4 0x54 +#define DW_OP_reg5 0x55 +#define DW_OP_reg6 0x56 +#define DW_OP_reg7 0x57 +#define DW_OP_reg8 0x58 +#define DW_OP_reg9 0x59 +#define DW_OP_reg10 0x5a +#define DW_OP_reg11 0x5b +#define DW_OP_reg12 0x5c +#define DW_OP_reg13 0x5d +#define DW_OP_reg14 0x5e +#define DW_OP_reg15 0x5f +#define DW_OP_reg16 0x60 +#define DW_OP_reg17 0x61 +#define DW_OP_reg18 0x62 +#define DW_OP_reg19 0x63 +#define DW_OP_reg20 0x64 +#define DW_OP_reg21 0x65 +#define DW_OP_reg22 0x66 +#define DW_OP_reg23 0x67 +#define DW_OP_reg24 0x68 +#define DW_OP_reg25 0x69 +#define DW_OP_reg26 0x6a +#define DW_OP_reg27 0x6b +#define DW_OP_reg28 0x6c +#define DW_OP_reg29 0x6d +#define DW_OP_reg30 0x6e +#define DW_OP_reg31 0x6f +#define DW_OP_breg0 0x70 +#define DW_OP_breg1 0x71 +#define DW_OP_breg2 0x72 +#define DW_OP_breg3 0x73 +#define DW_OP_breg4 0x74 +#define DW_OP_breg5 0x75 +#define DW_OP_breg6 0x76 +#define DW_OP_breg7 0x77 +#define DW_OP_breg8 0x78 +#define DW_OP_breg9 0x79 +#define DW_OP_breg10 0x7a +#define DW_OP_breg11 0x7b +#define DW_OP_breg12 0x7c +#define DW_OP_breg13 0x7d +#define DW_OP_breg14 0x7e +#define DW_OP_breg15 0x7f +#define DW_OP_breg16 0x80 +#define DW_OP_breg17 0x81 +#define DW_OP_breg18 0x82 +#define DW_OP_breg19 0x83 +#define DW_OP_breg20 0x84 +#define DW_OP_breg21 0x85 +#define DW_OP_breg22 0x86 +#define DW_OP_breg23 0x87 +#define DW_OP_breg24 0x88 +#define DW_OP_breg25 0x89 +#define DW_OP_breg26 0x8a +#define DW_OP_breg27 0x8b +#define DW_OP_breg28 0x8c +#define DW_OP_breg29 0x8d +#define DW_OP_breg30 0x8e +#define DW_OP_breg31 0x8f +#define DW_OP_regx 0x90 +#define DW_OP_fbreg 0x91 +#define DW_OP_bregx 0x92 +#define DW_OP_piece 0x93 +#define DW_OP_deref_size 0x94 +#define DW_OP_xderef_size 0x95 +#define DW_OP_nop 0x96 +#define DW_OP_push_object_address 0x97 +#define DW_OP_call2 0x98 +#define DW_OP_call4 0x99 +#define DW_OP_call_ref 0x9a +#define DW_OP_form_tls_address 0x9b +#define DW_OP_call_frame_cfa 0x9c +#define DW_OP_bit_piece 0x9d +#define DW_OP_lo_user 0xe0 +#define DW_OP_hi_user 0xff + +/* + * Addresses used in FDE entries in the .eh_frame section may be encoded + * using one of the following encodings. + */ +#define DW_EH_PE_absptr 0x00 +#define DW_EH_PE_omit 0xff +#define DW_EH_PE_uleb128 0x01 +#define DW_EH_PE_udata2 0x02 +#define DW_EH_PE_udata4 0x03 +#define DW_EH_PE_udata8 0x04 +#define DW_EH_PE_sleb128 0x09 +#define DW_EH_PE_sdata2 0x0a +#define DW_EH_PE_sdata4 0x0b +#define DW_EH_PE_sdata8 0x0c +#define DW_EH_PE_signed 0x09 + +#define DW_EH_PE_pcrel 0x10 + +/* + * The architecture-specific register number that contains the return + * address in the .debug_frame table. + */ +#define DWARF_ARCH_RA_REG 17 + +/* + * At what offset into dwarf_unwind_stack() is DWARF_ARCH_RA_REG setup? + */ +#define DWARF_ARCH_UNWIND_OFFSET 0x20 + +#ifndef __ASSEMBLY__ +/* + * Read either the frame pointer (r14) or the stack pointer (r15). + * NOTE: this MUST be inlined. + */ +static __always_inline unsigned long dwarf_read_arch_reg(unsigned int reg) +{ + unsigned long value; + + switch (reg) { + case 14: + __asm__ __volatile__("mov r14, %0\n" : "=r" (value)); + break; + case 15: + __asm__ __volatile__("mov r15, %0\n" : "=r" (value)); + break; + default: + BUG(); + } + + return value; +} + +/** + * dwarf_cie - Common Information Entry + */ +struct dwarf_cie { + unsigned long length; + unsigned long cie_id; + unsigned char version; + const char *augmentation; + unsigned int code_alignment_factor; + int data_alignment_factor; + + /* Which column in the rule table represents return addr of func. */ + unsigned int return_address_reg; + + unsigned char *initial_instructions; + unsigned char *instructions_end; + + unsigned char encoding; + + unsigned long cie_pointer; + + struct list_head link; + + unsigned long flags; +#define DWARF_CIE_Z_AUGMENTATION (1 << 0) +}; + +/** + * dwarf_fde - Frame Description Entry + */ +struct dwarf_fde { + unsigned long length; + unsigned long cie_pointer; + struct dwarf_cie *cie; + unsigned long initial_location; + unsigned long address_range; + unsigned char *instructions; + unsigned char *end; + struct list_head link; +}; + +/** + * dwarf_frame - DWARF information for a frame in the call stack + */ +struct dwarf_frame { + struct dwarf_frame *prev, *next; + + unsigned long pc; + + struct dwarf_reg *regs; + unsigned int num_regs; /* how many regs are allocated? */ + + unsigned int depth; /* what level are we in the callstack? */ + + unsigned long cfa; + + /* Valid when DW_FRAME_CFA_REG_OFFSET is set in flags */ + unsigned int cfa_register; + unsigned int cfa_offset; + + /* Valid when DW_FRAME_CFA_REG_EXP is set in flags */ + unsigned char *cfa_expr; + unsigned int cfa_expr_len; + + unsigned long flags; +#define DWARF_FRAME_CFA_REG_OFFSET (1 << 0) +#define DWARF_FRAME_CFA_REG_EXP (1 << 1) + + unsigned long return_addr; +}; + +/** + * dwarf_reg - DWARF register + * @flags: Describes how to calculate the value of this register + */ +struct dwarf_reg { + unsigned long addr; + unsigned long flags; +#define DWARF_REG_OFFSET (1 << 0) +}; + +/** + * dwarf_stack - a DWARF stack contains a collection of DWARF frames + * @depth: the number of frames in the stack + * @level: an array of DWARF frames, indexed by stack level + * + */ +struct dwarf_stack { + unsigned int depth; + struct dwarf_frame **level; +}; + +/* + * Call Frame instruction opcodes. + */ +#define DW_CFA_advance_loc 0x40 +#define DW_CFA_offset 0x80 +#define DW_CFA_restore 0xc0 +#define DW_CFA_nop 0x00 +#define DW_CFA_set_loc 0x01 +#define DW_CFA_advance_loc1 0x02 +#define DW_CFA_advance_loc2 0x03 +#define DW_CFA_advance_loc4 0x04 +#define DW_CFA_offset_extended 0x05 +#define DW_CFA_restore_extended 0x06 +#define DW_CFA_undefined 0x07 +#define DW_CFA_same_value 0x08 +#define DW_CFA_register 0x09 +#define DW_CFA_remember_state 0x0a +#define DW_CFA_restore_state 0x0b +#define DW_CFA_def_cfa 0x0c +#define DW_CFA_def_cfa_register 0x0d +#define DW_CFA_def_cfa_offset 0x0e +#define DW_CFA_def_cfa_expression 0x0f +#define DW_CFA_expression 0x10 +#define DW_CFA_offset_extended_sf 0x11 +#define DW_CFA_def_cfa_sf 0x12 +#define DW_CFA_def_cfa_offset_sf 0x13 +#define DW_CFA_val_offset 0x14 +#define DW_CFA_val_offset_sf 0x15 +#define DW_CFA_val_expression 0x16 +#define DW_CFA_lo_user 0x1c +#define DW_CFA_hi_user 0x3f + +/* + * Some call frame instructions encode their operands in the opcode. We + * need some helper functions to extract both the opcode and operands + * from an instruction. + */ +static inline unsigned int DW_CFA_opcode(unsigned long insn) +{ + return (insn & 0xc0); +} + +static inline unsigned int DW_CFA_operand(unsigned long insn) +{ + return (insn & 0x3f); +} + +#define DW_EH_FRAME_CIE 0 /* .eh_frame CIE IDs are 0 */ +#define DW_CIE_ID 0xffffffff +#define DW64_CIE_ID 0xffffffffffffffffULL + +/* + * DWARF FDE/CIE length field values. + */ +#define DW_EXT_LO 0xfffffff0 +#define DW_EXT_HI 0xffffffff +#define DW_EXT_DWARF64 DW_EXT_HI + +extern void dwarf_unwinder_init(void); + +extern struct dwarf_frame *dwarf_unwind_stack(unsigned long, + struct dwarf_frame *); +#endif /* __ASSEMBLY__ */ + +#define CFI_STARTPROC .cfi_startproc +#define CFI_ENDPROC .cfi_endproc +#define CFI_DEF_CFA .cfi_def_cfa +#define CFI_REGISTER .cfi_register +#define CFI_REL_OFFSET .cfi_rel_offset + +#else + +/* + * Use the asm comment character to ignore the rest of the line. + */ +#define CFI_IGNORE ! + +#define CFI_STARTPROC CFI_IGNORE +#define CFI_ENDPROC CFI_IGNORE +#define CFI_DEF_CFA CFI_IGNORE +#define CFI_REGISTER CFI_IGNORE +#define CFI_REL_OFFSET CFI_IGNORE + +#ifndef __ASSEMBLY__ +static inline void dwarf_unwinder_init(void) +{ +} +#endif + +#endif /* CONFIG_DWARF_UNWINDER */ + +#endif /* __ASM_SH_DWARF_H */ -- cgit v1.2.1 From f826466772ae52f26152287fcb2259351de78f0f Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 13 Aug 2009 20:41:31 +0100 Subject: sh: Delete DWARF_ARCH_UNWIND_OFFSET Trying to figure out the best value for DWARF_ARCH_UNWIND_OFFSET is tricky at best. Various things can change the size (and offset from the beginning of the function) of the prologue. Notably, turning on ftrace adds calls to mcount at the beginning of functions, thereby pushing the prologue further into the function. So replace DWARF_ARCH_UNWIND_OFFSET with some code that continues to execute CFA instructions until the value of return address register is defined. This is safe to do because we know that the return address must have been pushed onto the frame before our first function call; we just can't figure out where at compile-time. Signed-off-by: Matt Fleming Signed-off-by: Paul Mundt --- arch/sh/include/asm/dwarf.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'arch/sh/include/asm/dwarf.h') diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h index 23eba12880b3..60b180728d8d 100644 --- a/arch/sh/include/asm/dwarf.h +++ b/arch/sh/include/asm/dwarf.h @@ -193,11 +193,6 @@ */ #define DWARF_ARCH_RA_REG 17 -/* - * At what offset into dwarf_unwind_stack() is DWARF_ARCH_RA_REG setup? - */ -#define DWARF_ARCH_UNWIND_OFFSET 0x20 - #ifndef __ASSEMBLY__ /* * Read either the frame pointer (r14) or the stack pointer (r15). -- cgit v1.2.1 From cd7246f0e2747bd2b43d25d0f63f05db182a62c0 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sun, 16 Aug 2009 01:44:33 +0100 Subject: sh: Add support for DWARF GNU extensions Also, remove the "fix" to DW_CFA_def_cfa_register where we reset the frame's cfa_offset to 0. This action is incorrect when handling DW_CFA_def_cfa_register as the DWARF spec specifically states that the previous contents of cfa_offset should be used with the new register. The reason that I thought cfa_offset should be reset to 0 was because it was being assigned a bogus value prior to executing the DW_CFA_def_cfa_register op. It turns out that the bogus cfa_offset value came from interpreting .cfi_escape pseudo-ops (those used by the GNU extensions) as CFA_DW_def_cfa ops. Signed-off-by: Matt Fleming --- arch/sh/include/asm/dwarf.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'arch/sh/include/asm/dwarf.h') diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h index 60b180728d8d..d3d3837c5e1b 100644 --- a/arch/sh/include/asm/dwarf.h +++ b/arch/sh/include/asm/dwarf.h @@ -340,6 +340,10 @@ struct dwarf_stack { #define DW_CFA_lo_user 0x1c #define DW_CFA_hi_user 0x3f +/* GNU extension opcodes */ +#define DW_CFA_GNU_args_size 0x2e +#define DW_CFA_GNU_negative_offset_extended 0x2f + /* * Some call frame instructions encode their operands in the opcode. We * need some helper functions to extract both the opcode and operands -- cgit v1.2.1 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/include/asm/dwarf.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'arch/sh/include/asm/dwarf.h') diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h index d3d3837c5e1b..2fbe8720411e 100644 --- a/arch/sh/include/asm/dwarf.h +++ b/arch/sh/include/asm/dwarf.h @@ -370,8 +370,6 @@ static inline unsigned int DW_CFA_operand(unsigned long insn) #define DW_EXT_HI 0xffffffff #define DW_EXT_DWARF64 DW_EXT_HI -extern void dwarf_unwinder_init(void); - extern struct dwarf_frame *dwarf_unwind_stack(unsigned long, struct dwarf_frame *); #endif /* __ASSEMBLY__ */ -- cgit v1.2.1 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/include/asm/dwarf.h | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'arch/sh/include/asm/dwarf.h') diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h index 2fbe8720411e..a22fbe98303f 100644 --- a/arch/sh/include/asm/dwarf.h +++ b/arch/sh/include/asm/dwarf.h @@ -265,10 +265,7 @@ struct dwarf_frame { unsigned long pc; - struct dwarf_reg *regs; - unsigned int num_regs; /* how many regs are allocated? */ - - unsigned int depth; /* what level are we in the callstack? */ + struct list_head reg_list; unsigned long cfa; @@ -292,22 +289,15 @@ struct dwarf_frame { * @flags: Describes how to calculate the value of this register */ struct dwarf_reg { + struct list_head link; + + unsigned int number; + unsigned long addr; unsigned long flags; #define DWARF_REG_OFFSET (1 << 0) }; -/** - * dwarf_stack - a DWARF stack contains a collection of DWARF frames - * @depth: the number of frames in the stack - * @level: an array of DWARF frames, indexed by stack level - * - */ -struct dwarf_stack { - unsigned int depth; - struct dwarf_frame **level; -}; - /* * Call Frame instruction opcodes. */ @@ -372,7 +362,7 @@ static inline unsigned int DW_CFA_operand(unsigned long insn) extern struct dwarf_frame *dwarf_unwind_stack(unsigned long, struct dwarf_frame *); -#endif /* __ASSEMBLY__ */ +#endif /* !__ASSEMBLY__ */ #define CFI_STARTPROC .cfi_startproc #define CFI_ENDPROC .cfi_endproc -- cgit v1.2.1 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/include/asm/dwarf.h | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/sh/include/asm/dwarf.h') diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h index a22fbe98303f..8b0bcc087385 100644 --- a/arch/sh/include/asm/dwarf.h +++ b/arch/sh/include/asm/dwarf.h @@ -296,6 +296,7 @@ struct dwarf_reg { unsigned long addr; unsigned long flags; #define DWARF_REG_OFFSET (1 << 0) +#define DWARF_VAL_OFFSET (1 << 1) }; /* -- cgit v1.2.1 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/include/asm/dwarf.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'arch/sh/include/asm/dwarf.h') diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h index 8b0bcc087385..c367ed3373c5 100644 --- a/arch/sh/include/asm/dwarf.h +++ b/arch/sh/include/asm/dwarf.h @@ -297,6 +297,7 @@ struct dwarf_reg { unsigned long flags; #define DWARF_REG_OFFSET (1 << 0) #define DWARF_VAL_OFFSET (1 << 1) +#define DWARF_UNDEFINED (1 << 2) }; /* @@ -370,6 +371,7 @@ extern struct dwarf_frame *dwarf_unwind_stack(unsigned long, #define CFI_DEF_CFA .cfi_def_cfa #define CFI_REGISTER .cfi_register #define CFI_REL_OFFSET .cfi_rel_offset +#define CFI_UNDEFINED .cfi_undefined #else @@ -383,6 +385,7 @@ extern struct dwarf_frame *dwarf_unwind_stack(unsigned long, #define CFI_DEF_CFA CFI_IGNORE #define CFI_REGISTER CFI_IGNORE #define CFI_REL_OFFSET CFI_IGNORE +#define CFI_UNDEFINED CFI_IGNORE #ifndef __ASSEMBLY__ static inline void dwarf_unwinder_init(void) -- cgit v1.2.1 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/include/asm/dwarf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/sh/include/asm/dwarf.h') diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h index c367ed3373c5..ced6795891a6 100644 --- a/arch/sh/include/asm/dwarf.h +++ b/arch/sh/include/asm/dwarf.h @@ -200,7 +200,7 @@ */ static __always_inline unsigned long dwarf_read_arch_reg(unsigned int reg) { - unsigned long value; + unsigned long value = 0; switch (reg) { case 14: -- cgit v1.2.1 From a6a2f2ad67506090e332f440457553c0ec011d68 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 9 Oct 2009 23:20:54 +0100 Subject: 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 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'arch/sh/include/asm/dwarf.h') 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 -- cgit v1.2.1 From ed4fe7f488008f38d5f423f0bcc736b1779d6ddc Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sat, 10 Oct 2009 16:03:11 +0100 Subject: 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 + 1 file changed, 1 insertion(+) (limited to 'arch/sh/include/asm/dwarf.h') 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 *); -- cgit v1.2.1 From ac4fac8cb24ab209ae373a3e3e9995dff7d0c394 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 13 Oct 2009 13:10:14 +0900 Subject: sh: Generalize CALLER_ADDRx support. This splits out the unwinder implementation and adds a new return_address() abstraction modelled after the ARM code. The DWARF unwinder is tied in to this, returning NULL otherwise in the case of being unable to support arbitrary depths. This enables us to get correct behaviour with the unwinder enabled, as well as disabling the arbitrary depth support when frame pointers are enabled, as arbitrary depths with __builtin_return_address() are not supported regardless. With this abstraction it's also possible to layer on a simplified implementation with frame pointers in the event that the unwinder isn't enabled, although this is left as a future exercise. Signed-off-by: Paul Mundt --- arch/sh/include/asm/dwarf.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'arch/sh/include/asm/dwarf.h') diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h index fc51e66f2380..d985148af19f 100644 --- a/arch/sh/include/asm/dwarf.h +++ b/arch/sh/include/asm/dwarf.h @@ -194,6 +194,11 @@ #define DWARF_ARCH_RA_REG 17 #ifndef __ASSEMBLY__ + +#include +#include +#include + /* * Read either the frame pointer (r14) or the stack pointer (r15). * NOTE: this MUST be inlined. -- cgit v1.2.1 From 5a3abba77dc0eb0b00332c21899123cdfa3b19e5 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 13 Oct 2009 13:32:19 +0900 Subject: sh: Tidy up the dwarf module helpers. This enables us to build the dwarf unwinder both with modules enabled and disabled in addition to reducing code size in the latter case. The helpers are also consolidated, and modified to resemble the BUG module helpers. Signed-off-by: Paul Mundt --- arch/sh/include/asm/dwarf.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'arch/sh/include/asm/dwarf.h') diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h index d985148af19f..bdccbbfdc0bd 100644 --- a/arch/sh/include/asm/dwarf.h +++ b/arch/sh/include/asm/dwarf.h @@ -198,6 +198,7 @@ #include #include #include +#include /* * Read either the frame pointer (r14) or the stack pointer (r15). @@ -382,8 +383,10 @@ 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 *); + +extern int module_dwarf_finalize(const Elf_Ehdr *, const Elf_Shdr *, + struct module *); +extern void module_dwarf_cleanup(struct module *); #endif /* !__ASSEMBLY__ */ @@ -412,6 +415,10 @@ extern void dwarf_module_unload(struct module *); static inline void dwarf_unwinder_init(void) { } + +#define module_dwarf_finalize(hdr, sechdrs, me) (0) +#define module_dwarf_cleanup(mod) do { } while (0) + #endif #endif /* CONFIG_DWARF_UNWINDER */ -- cgit v1.2.1 From 858918b77b29d0e9ce7f524d1b57d602d85f5d64 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Sun, 7 Feb 2010 12:40:36 +0000 Subject: sh: Optimise FDE/CIE lookup by using red-black trees Now that the DWARF unwinder is being used to provide perf callstacks unwinding speed is an issue. It is no longer being used in exceptional circumstances where we don't care about runtime performance, e.g. when panicing, so it makes sense improve performance is possible. With this patch I saw a 42% improvement in unwind time when calling return_address(1). Greater improvements will be seen as the number of levels unwound increases as each unwind is now cheaper. Note that insertion time has doubled but that's just the price we pay for keeping the trees balanced. However, this is a one-time cost for kernel boot/module load and so the improvements in lookup time dominate the extra time we spend keeping the trees balanced. Signed-off-by: Matt Fleming Signed-off-by: Paul Mundt --- arch/sh/include/asm/dwarf.h | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'arch/sh/include/asm/dwarf.h') diff --git a/arch/sh/include/asm/dwarf.h b/arch/sh/include/asm/dwarf.h index bdccbbfdc0bd..d62abd1d0c05 100644 --- a/arch/sh/include/asm/dwarf.h +++ b/arch/sh/include/asm/dwarf.h @@ -243,16 +243,13 @@ struct dwarf_cie { unsigned long cie_pointer; - struct list_head link; - 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; + /* linked-list entry if this CIE is from a module */ + struct list_head link; + + struct rb_node node; }; /** @@ -266,13 +263,11 @@ struct dwarf_fde { unsigned long address_range; unsigned char *instructions; unsigned char *end; + + /* linked-list entry if this FDE is from a module */ struct list_head link; - /* - * 'mod' will be non-NULL if this FDE came from a module's - * .eh_frame section. - */ - struct module *mod; + struct rb_node node; }; /** -- cgit v1.2.1