Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
718dbf37
Commit
718dbf37
authored
Aug 14, 2009
by
Paul Mundt
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'sh/dwarf-unwinder'
parents
f54aab4a
f8264667
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
1363 additions
and
1 deletion
+1363
-1
arch/sh/Kconfig.debug
arch/sh/Kconfig.debug
+8
-0
arch/sh/Makefile
arch/sh/Makefile
+4
-0
arch/sh/include/asm/dwarf.h
arch/sh/include/asm/dwarf.h
+402
-0
arch/sh/include/asm/entry-macros.S
arch/sh/include/asm/entry-macros.S
+12
-0
arch/sh/include/asm/sections.h
arch/sh/include/asm/sections.h
+1
-0
arch/sh/include/asm/vmlinux.lds.h
arch/sh/include/asm/vmlinux.lds.h
+17
-0
arch/sh/kernel/Makefile_32
arch/sh/kernel/Makefile_32
+1
-0
arch/sh/kernel/Makefile_64
arch/sh/kernel/Makefile_64
+1
-0
arch/sh/kernel/cpu/sh3/entry.S
arch/sh/kernel/cpu/sh3/entry.S
+1
-0
arch/sh/kernel/dwarf.c
arch/sh/kernel/dwarf.c
+901
-0
arch/sh/kernel/entry-common.S
arch/sh/kernel/entry-common.S
+8
-0
arch/sh/kernel/irq.c
arch/sh/kernel/irq.c
+4
-0
arch/sh/kernel/vmlinux.lds.S
arch/sh/kernel/vmlinux.lds.S
+3
-1
No files found.
arch/sh/Kconfig.debug
View file @
718dbf37
...
@@ -110,6 +110,14 @@ config DUMP_CODE
...
@@ -110,6 +110,14 @@ config DUMP_CODE
Those looking for more verbose debugging output should say Y.
Those looking for more verbose debugging output should say Y.
config DWARF_UNWINDER
bool "Enable the DWARF unwinder for stacktraces"
select FRAME_POINTER
default n
help
Enabling this option will make stacktraces more accurate, at
the cost of an increase in overall kernel size.
config SH_NO_BSS_INIT
config SH_NO_BSS_INIT
bool "Avoid zeroing BSS (to speed-up startup on suitable platforms)"
bool "Avoid zeroing BSS (to speed-up startup on suitable platforms)"
depends on DEBUG_KERNEL
depends on DEBUG_KERNEL
...
...
arch/sh/Makefile
View file @
718dbf37
...
@@ -191,6 +191,10 @@ ifeq ($(CONFIG_MCOUNT),y)
...
@@ -191,6 +191,10 @@ ifeq ($(CONFIG_MCOUNT),y)
KBUILD_CFLAGS
+=
-pg
KBUILD_CFLAGS
+=
-pg
endif
endif
ifeq
($(CONFIG_DWARF_UNWINDER),y)
KBUILD_CFLAGS
+=
-fasynchronous-unwind-tables
endif
libs-$(CONFIG_SUPERH32)
:=
arch
/sh/lib/
$
(
libs-y
)
libs-$(CONFIG_SUPERH32)
:=
arch
/sh/lib/
$
(
libs-y
)
libs-$(CONFIG_SUPERH64)
:=
arch
/sh/lib64/
$
(
libs-y
)
libs-$(CONFIG_SUPERH64)
:=
arch
/sh/lib64/
$
(
libs-y
)
...
...
arch/sh/include/asm/dwarf.h
0 → 100644
View file @
718dbf37
/*
* Copyright (C) 2009 Matt Fleming <matt@console-pimps.org>
*
* 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
#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 */
arch/sh/include/asm/entry-macros.S
View file @
718dbf37
...
@@ -108,3 +108,15 @@
...
@@ -108,3 +108,15 @@
#else
#else
# define PREF(x) nop
# define PREF(x) nop
#endif
#endif
/
*
*
Macro
for
use
within
assembly
.
Because
the
DWARF
unwinder
*
needs
to
use
the
frame
register
to
unwind
the
stack
,
we
*
need
to
setup
r14
with
the
value
of
the
stack
pointer
as
*
the
return
address
is
usually
on
the
stack
somewhere
.
*/
.
macro
setup_frame_reg
#ifdef CONFIG_DWARF_UNWINDER
mov
r15
,
r14
#endif
.
endm
arch/sh/include/asm/sections.h
View file @
718dbf37
...
@@ -7,6 +7,7 @@ extern void __nosave_begin, __nosave_end;
...
@@ -7,6 +7,7 @@ extern void __nosave_begin, __nosave_end;
extern
long
__machvec_start
,
__machvec_end
;
extern
long
__machvec_start
,
__machvec_end
;
extern
char
__uncached_start
,
__uncached_end
;
extern
char
__uncached_start
,
__uncached_end
;
extern
char
_ebss
[];
extern
char
_ebss
[];
extern
char
__start_eh_frame
[],
__stop_eh_frame
[];
#endif
/* __ASM_SH_SECTIONS_H */
#endif
/* __ASM_SH_SECTIONS_H */
arch/sh/include/asm/vmlinux.lds.h
0 → 100644
View file @
718dbf37
#ifndef __ASM_SH_VMLINUX_LDS_H
#define __ASM_SH_VMLINUX_LDS_H
#include <asm-generic/vmlinux.lds.h>
#ifdef CONFIG_DWARF_UNWINDER
#define DWARF_EH_FRAME \
.eh_frame : AT(ADDR(.eh_frame) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start_eh_frame) = .; \
*(.eh_frame) \
VMLINUX_SYMBOL(__stop_eh_frame) = .; \
}
#else
#define DWARF_EH_FRAME
#endif
#endif
/* __ASM_SH_VMLINUX_LDS_H */
arch/sh/kernel/Makefile_32
View file @
718dbf37
...
@@ -33,6 +33,7 @@ obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
...
@@ -33,6 +33,7 @@ obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_DUMP_CODE) += disassemble.o
obj-$(CONFIG_DUMP_CODE) += disassemble.o
obj-$(CONFIG_HIBERNATION) += swsusp.o
obj-$(CONFIG_HIBERNATION) += swsusp.o
obj-$(CONFIG_DWARF_UNWINDER) += dwarf.o
obj-$(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) += localtimer.o
obj-$(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) += localtimer.o
...
...
arch/sh/kernel/Makefile_64
View file @
718dbf37
...
@@ -13,6 +13,7 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
...
@@ -13,6 +13,7 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_IO_TRAPPED) += io_trapped.o
obj-$(CONFIG_IO_TRAPPED) += io_trapped.o
obj-$(CONFIG_GENERIC_GPIO) += gpio.o
obj-$(CONFIG_GENERIC_GPIO) += gpio.o
obj-$(CONFIG_DWARF_UNWINDER) += dwarf.o
obj-$(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) += localtimer.o
obj-$(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) += localtimer.o
...
...
arch/sh/kernel/cpu/sh3/entry.S
View file @
718dbf37
...
@@ -137,6 +137,7 @@ ENTRY(tlb_protection_violation_store)
...
@@ -137,6 +137,7 @@ ENTRY(tlb_protection_violation_store)
mov
#
1
,
r5
mov
#
1
,
r5
call_dpf
:
call_dpf
:
setup_frame_reg
mov.l
1
f
,
r0
mov.l
1
f
,
r0
mov
r5
,
r8
mov
r5
,
r8
mov.l
@
r0
,
r6
mov.l
@
r0
,
r6
...
...
arch/sh/kernel/dwarf.c
0 → 100644
View file @
718dbf37
/*
* Copyright (C) 2009 Matt Fleming <matt@console-pimps.org>
*
* 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.
*
* This is an implementation of a DWARF unwinder. Its main purpose is
* for generating stacktrace information. Based on the DWARF 3
* specification from http://www.dwarfstd.org.
*
* TODO:
* - DWARF64 doesn't work.
*/
/* #define DEBUG */
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <asm/dwarf.h>
#include <asm/unwinder.h>
#include <asm/sections.h>
#include <asm/unaligned.h>
#include <asm/dwarf.h>
#include <asm/stacktrace.h>
static
LIST_HEAD
(
dwarf_cie_list
);
DEFINE_SPINLOCK
(
dwarf_cie_lock
);
static
LIST_HEAD
(
dwarf_fde_list
);
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.
*
* Register numbers start at zero, therefore we need to allocate space
* for "reg" + 1 registers.
*/
static
void
dwarf_frame_alloc_regs
(
struct
dwarf_frame
*
frame
,
unsigned
int
reg
)
{
struct
dwarf_reg
*
regs
;
unsigned
int
num_regs
=
reg
+
1
;
size_t
new_size
;
size_t
old_size
;
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_KERNEL
);
if
(
!
regs
)
{
printk
(
KERN_WARNING
"Unable to allocate DWARF registers
\n
"
);
/*
* Let's just bomb hard here, we have no way to
* gracefully recover.
*/
BUG
();
}
if
(
frame
->
regs
)
{
memcpy
(
regs
,
frame
->
regs
,
old_size
);
kfree
(
frame
->
regs
);
}
frame
->
regs
=
regs
;
frame
->
num_regs
=
num_regs
;
}
/**
* dwarf_read_addr - read dwarf data
* @src: source address of data
* @dst: destination address to store the data to
*
* Read 'n' bytes from @src, where 'n' is the size of an address on
* the native machine. We return the number of bytes read, which
* should always be 'n'. We also have to be careful when reading
* from @src and writing to @dst, because they can be arbitrarily
* aligned. Return 'n' - the number of bytes read.
*/
static
inline
int
dwarf_read_addr
(
unsigned
long
*
src
,
unsigned
long
*
dst
)
{
u32
val
=
get_unaligned
(
src
);
put_unaligned
(
val
,
dst
);
return
sizeof
(
unsigned
long
*
);
}
/**
* dwarf_read_uleb128 - read unsigned LEB128 data
* @addr: the address where the ULEB128 data is stored
* @ret: address to store the result
*
* Decode an unsigned LEB128 encoded datum. The algorithm is taken
* from Appendix C of the DWARF 3 spec. For information on the
* encodings refer to section "7.6 - Variable Length Data". Return
* the number of bytes read.
*/
static
inline
unsigned
long
dwarf_read_uleb128
(
char
*
addr
,
unsigned
int
*
ret
)
{
unsigned
int
result
;
unsigned
char
byte
;
int
shift
,
count
;
result
=
0
;
shift
=
0
;
count
=
0
;
while
(
1
)
{
byte
=
__raw_readb
(
addr
);
addr
++
;
count
++
;
result
|=
(
byte
&
0x7f
)
<<
shift
;
shift
+=
7
;
if
(
!
(
byte
&
0x80
))
break
;
}
*
ret
=
result
;
return
count
;
}
/**
* dwarf_read_leb128 - read signed LEB128 data
* @addr: the address of the LEB128 encoded data
* @ret: address to store the result
*
* Decode signed LEB128 data. The algorithm is taken from Appendix
* C of the DWARF 3 spec. Return the number of bytes read.
*/
static
inline
unsigned
long
dwarf_read_leb128
(
char
*
addr
,
int
*
ret
)
{
unsigned
char
byte
;
int
result
,
shift
;
int
num_bits
;
int
count
;
result
=
0
;
shift
=
0
;
count
=
0
;
while
(
1
)
{
byte
=
__raw_readb
(
addr
);
addr
++
;
result
|=
(
byte
&
0x7f
)
<<
shift
;
shift
+=
7
;
count
++
;
if
(
!
(
byte
&
0x80
))
break
;
}
/* The number of bits in a signed integer. */
num_bits
=
8
*
sizeof
(
result
);
if
((
shift
<
num_bits
)
&&
(
byte
&
0x40
))
result
|=
(
-
1
<<
shift
);
*
ret
=
result
;
return
count
;
}
/**
* dwarf_read_encoded_value - return the decoded value at @addr
* @addr: the address of the encoded value
* @val: where to write the decoded value
* @encoding: the encoding with which we can decode @addr
*
* GCC emits encoded address in the .eh_frame FDE entries. Decode
* the value at @addr using @encoding. The decoded value is written
* to @val and the number of bytes read is returned.
*/
static
int
dwarf_read_encoded_value
(
char
*
addr
,
unsigned
long
*
val
,
char
encoding
)
{
unsigned
long
decoded_addr
=
0
;
int
count
=
0
;
switch
(
encoding
&
0x70
)
{
case
DW_EH_PE_absptr
:
break
;
case
DW_EH_PE_pcrel
:
decoded_addr
=
(
unsigned
long
)
addr
;
break
;
default:
pr_debug
(
"encoding=0x%x
\n
"
,
(
encoding
&
0x70
));
BUG
();
}
if
((
encoding
&
0x07
)
==
0x00
)
encoding
|=
DW_EH_PE_udata4
;
switch
(
encoding
&
0x0f
)
{
case
DW_EH_PE_sdata4
:
case
DW_EH_PE_udata4
:
count
+=
4
;
decoded_addr
+=
get_unaligned
((
u32
*
)
addr
);
__raw_writel
(
decoded_addr
,
val
);
break
;
default:
pr_debug
(
"encoding=0x%x
\n
"
,
encoding
);
BUG
();
}
return
count
;
}
/**
* dwarf_entry_len - return the length of an FDE or CIE
* @addr: the address of the entry
* @len: the length of the entry
*
* Read the initial_length field of the entry and store the size of
* the entry in @len. We return the number of bytes read. Return a
* count of 0 on error.
*/
static
inline
int
dwarf_entry_len
(
char
*
addr
,
unsigned
long
*
len
)
{
u32
initial_len
;
int
count
;
initial_len
=
get_unaligned
((
u32
*
)
addr
);
count
=
4
;
/*
* An initial length field value in the range DW_LEN_EXT_LO -
* DW_LEN_EXT_HI indicates an extension, and should not be
* interpreted as a length. The only extension that we currently
* understand is the use of DWARF64 addresses.
*/
if
(
initial_len
>=
DW_EXT_LO
&&
initial_len
<=
DW_EXT_HI
)
{
/*
* The 64-bit length field immediately follows the
* compulsory 32-bit length field.
*/
if
(
initial_len
==
DW_EXT_DWARF64
)
{
*
len
=
get_unaligned
((
u64
*
)
addr
+
4
);
count
=
12
;
}
else
{
printk
(
KERN_WARNING
"Unknown DWARF extension
\n
"
);
count
=
0
;
}
}
else
*
len
=
initial_len
;
return
count
;
}
/**
* dwarf_lookup_cie - locate the cie
* @cie_ptr: pointer to help with lookup
*/
static
struct
dwarf_cie
*
dwarf_lookup_cie
(
unsigned
long
cie_ptr
)
{
struct
dwarf_cie
*
cie
,
*
n
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
dwarf_cie_lock
,
flags
);
/*
* We've cached the last CIE we looked up because chances are
* that the FDE wants this CIE.
*/
if
(
cached_cie
&&
cached_cie
->
cie_pointer
==
cie_ptr
)
{
cie
=
cached_cie
;
goto
out
;
}
list_for_each_entry_safe
(
cie
,
n
,
&
dwarf_cie_list
,
link
)
{
if
(
cie
->
cie_pointer
==
cie_ptr
)
{
cached_cie
=
cie
;
break
;
}
}
/* Couldn't find the entry in the list. */
if
(
&
cie
->
link
==
&
dwarf_cie_list
)
cie
=
NULL
;
out:
spin_unlock_irqrestore
(
&
dwarf_cie_lock
,
flags
);
return
cie
;
}
/**
* dwarf_lookup_fde - locate the FDE that covers pc
* @pc: the program counter
*/
struct
dwarf_fde
*
dwarf_lookup_fde
(
unsigned
long
pc
)
{
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
)
{
unsigned
long
start
,
end
;
start
=
fde
->
initial_location
;
end
=
fde
->
initial_location
+
fde
->
address_range
;
if
(
pc
>=
start
&&
pc
<
end
)
break
;
}
/* Couldn't find the entry in the list. */
if
(
&
fde
->
link
==
&
dwarf_fde_list
)
fde
=
NULL
;
spin_unlock_irqrestore
(
&
dwarf_fde_lock
,
flags
);
return
fde
;
}
/**
* dwarf_cfa_execute_insns - execute instructions to calculate a CFA
* @insn_start: address of the first instruction
* @insn_end: address of the last instruction
* @cie: the CIE for this function
* @fde: the FDE for this function
* @frame: the instructions calculate the CFA for this frame
* @pc: the program counter of the address we're interested in
* @define_ra: keep executing insns until the return addr reg is defined?
*
* Execute the Call Frame instruction sequence starting at
* @insn_start and ending at @insn_end. The instructions describe
* how to calculate the Canonical Frame Address of a stackframe.
* Store the results in @frame.
*/
static
int
dwarf_cfa_execute_insns
(
unsigned
char
*
insn_start
,
unsigned
char
*
insn_end
,
struct
dwarf_cie
*
cie
,
struct
dwarf_fde
*
fde
,
struct
dwarf_frame
*
frame
,
unsigned
long
pc
,
bool
define_ra
)
{
unsigned
char
insn
;
unsigned
char
*
current_insn
;
unsigned
int
count
,
delta
,
reg
,
expr_len
,
offset
;
bool
seen_ra_reg
;
current_insn
=
insn_start
;
/*
* If we're executing instructions for the dwarf_unwind_stack()
* FDE we need to keep executing instructions until the value of
* DWARF_ARCH_RA_REG is defined. See the comment in
* dwarf_unwind_stack() for more details.
*/
if
(
define_ra
)
seen_ra_reg
=
false
;
else
seen_ra_reg
=
true
;
while
(
current_insn
<
insn_end
&&
(
frame
->
pc
<=
pc
||
!
seen_ra_reg
)
)
{
insn
=
__raw_readb
(
current_insn
++
);
if
(
!
seen_ra_reg
)
{
if
(
frame
->
num_regs
>=
DWARF_ARCH_RA_REG
&&
frame
->
regs
[
DWARF_ARCH_RA_REG
].
flags
)
seen_ra_reg
=
true
;
}
/*
* Firstly, handle the opcodes that embed their operands
* in the instructions.
*/
switch
(
DW_CFA_opcode
(
insn
))
{
case
DW_CFA_advance_loc
:
delta
=
DW_CFA_operand
(
insn
);
delta
*=
cie
->
code_alignment_factor
;
frame
->
pc
+=
delta
;
continue
;
/* NOTREACHED */
case
DW_CFA_offset
:
reg
=
DW_CFA_operand
(
insn
);
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
;
continue
;
/* NOTREACHED */
case
DW_CFA_restore
:
reg
=
DW_CFA_operand
(
insn
);
continue
;
/* NOTREACHED */
}
/*
* Secondly, handle the opcodes that don't embed their
* operands in the instruction.
*/
switch
(
insn
)
{
case
DW_CFA_nop
:
continue
;
case
DW_CFA_advance_loc1
:
delta
=
*
current_insn
++
;
frame
->
pc
+=
delta
*
cie
->
code_alignment_factor
;
break
;
case
DW_CFA_advance_loc2
:
delta
=
get_unaligned
((
u16
*
)
current_insn
);
current_insn
+=
2
;
frame
->
pc
+=
delta
*
cie
->
code_alignment_factor
;
break
;
case
DW_CFA_advance_loc4
:
delta
=
get_unaligned
((
u32
*
)
current_insn
);
current_insn
+=
4
;
frame
->
pc
+=
delta
*
cie
->
code_alignment_factor
;
break
;
case
DW_CFA_offset_extended
:
count
=
dwarf_read_uleb128
(
current_insn
,
&
reg
);
current_insn
+=
count
;
count
=
dwarf_read_uleb128
(
current_insn
,
&
offset
);
current_insn
+=
count
;
offset
*=
cie
->
data_alignment_factor
;
break
;
case
DW_CFA_restore_extended
:
count
=
dwarf_read_uleb128
(
current_insn
,
&
reg
);
current_insn
+=
count
;
break
;
case
DW_CFA_undefined
:
count
=
dwarf_read_uleb128
(
current_insn
,
&
reg
);
current_insn
+=
count
;
break
;
case
DW_CFA_def_cfa
:
count
=
dwarf_read_uleb128
(
current_insn
,
&
frame
->
cfa_register
);
current_insn
+=
count
;
count
=
dwarf_read_uleb128
(
current_insn
,
&
frame
->
cfa_offset
);
current_insn
+=
count
;
frame
->
flags
|=
DWARF_FRAME_CFA_REG_OFFSET
;
break
;
case
DW_CFA_def_cfa_register
:
count
=
dwarf_read_uleb128
(
current_insn
,
&
frame
->
cfa_register
);
current_insn
+=
count
;
frame
->
flags
|=
DWARF_FRAME_CFA_REG_OFFSET
;
break
;
case
DW_CFA_def_cfa_offset
:
count
=
dwarf_read_uleb128
(
current_insn
,
&
offset
);
current_insn
+=
count
;
frame
->
cfa_offset
=
offset
;
break
;
case
DW_CFA_def_cfa_expression
:
count
=
dwarf_read_uleb128
(
current_insn
,
&
expr_len
);
current_insn
+=
count
;
frame
->
cfa_expr
=
current_insn
;
frame
->
cfa_expr_len
=
expr_len
;
current_insn
+=
expr_len
;
frame
->
flags
|=
DWARF_FRAME_CFA_REG_EXP
;
break
;
case
DW_CFA_offset_extended_sf
:
count
=
dwarf_read_uleb128
(
current_insn
,
&
reg
);
current_insn
+=
count
;
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
;
break
;
case
DW_CFA_val_offset
:
count
=
dwarf_read_uleb128
(
current_insn
,
&
reg
);
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
;
break
;
default:
pr_debug
(
"unhandled DWARF instruction 0x%x
\n
"
,
insn
);
break
;
}
}
return
0
;
}
/**
* dwarf_unwind_stack - recursively unwind the stack
* @pc: address of the function to unwind
* @prev: struct dwarf_frame of the previous stackframe on the callstack
*
* Return a struct dwarf_frame representing the most recent frame
* 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
*
frame
;
struct
dwarf_cie
*
cie
;
struct
dwarf_fde
*
fde
;
unsigned
long
addr
;
int
i
,
offset
;
bool
define_ra
=
false
;
/*
* 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.
*
* Setting "define_ra" to true indictates that we want
* dwarf_cfa_execute_insns() to continue executing instructions
* until we know how to calculate the value of DWARF_ARCH_RA_REG
* (which we need in order to kick off the whole unwinding
* process).
*
* NOTE: the return address is guaranteed to be setup by the
* time this function makes its first function call.
*/
if
(
!
pc
&&
!
prev
)
{
pc
=
(
unsigned
long
)
&
dwarf_unwind_stack
;
define_ra
=
true
;
}
frame
=
kzalloc
(
sizeof
(
*
frame
),
GFP_KERNEL
);
if
(
!
frame
)
return
NULL
;
frame
->
prev
=
prev
;
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,
*
* a) pc has no asscociated DWARF frame info and so
* we don't know how to unwind this frame. This is
* usually the case when we're trying to unwind a
* frame that was called from some assembly code
* that has no DWARF info, e.g. syscalls.
*
* b) the DEBUG info for pc is bogus. There's
* really no way to distinguish this case from the
* case above, which sucks because we could print a
* warning here.
*/
return
NULL
;
}
cie
=
dwarf_lookup_cie
(
fde
->
cie_pointer
);
frame
->
pc
=
fde
->
initial_location
;
/* CIE initial instructions */
dwarf_cfa_execute_insns
(
cie
->
initial_instructions
,
cie
->
instructions_end
,
cie
,
fde
,
frame
,
pc
,
false
);
/* FDE instructions */
dwarf_cfa_execute_insns
(
fde
->
instructions
,
fde
->
end
,
cie
,
fde
,
frame
,
pc
,
define_ra
);
/* Calculate the CFA */
switch
(
frame
->
flags
)
{
case
DWARF_FRAME_CFA_REG_OFFSET
:
if
(
prev
)
{
BUG_ON
(
!
prev
->
regs
[
frame
->
cfa_register
].
flags
);
addr
=
prev
->
cfa
;
addr
+=
prev
->
regs
[
frame
->
cfa_register
].
addr
;
frame
->
cfa
=
__raw_readl
(
addr
);
}
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
* function.
*/
frame
->
cfa
=
dwarf_read_arch_reg
(
frame
->
cfa_register
);
}
frame
->
cfa
+=
frame
->
cfa_offset
;
break
;
default:
BUG
();
}
/* 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
;
offset
=
reg
->
addr
;
offset
+=
frame
->
cfa
;
}
addr
=
frame
->
cfa
+
frame
->
regs
[
DWARF_ARCH_RA_REG
].
addr
;
frame
->
return_addr
=
__raw_readl
(
addr
);
frame
->
next
=
dwarf_unwind_stack
(
frame
->
return_addr
,
frame
);
return
frame
;
}
static
int
dwarf_parse_cie
(
void
*
entry
,
void
*
p
,
unsigned
long
len
,
unsigned
char
*
end
)
{
struct
dwarf_cie
*
cie
;
unsigned
long
flags
;
int
count
;
cie
=
kzalloc
(
sizeof
(
*
cie
),
GFP_KERNEL
);
if
(
!
cie
)
return
-
ENOMEM
;
cie
->
length
=
len
;
/*
* Record the offset into the .eh_frame section
* for this CIE. It allows this CIE to be
* quickly and easily looked up from the
* corresponding FDE.
*/
cie
->
cie_pointer
=
(
unsigned
long
)
entry
;
cie
->
version
=
*
(
char
*
)
p
++
;
BUG_ON
(
cie
->
version
!=
1
);
cie
->
augmentation
=
p
;
p
+=
strlen
(
cie
->
augmentation
)
+
1
;
count
=
dwarf_read_uleb128
(
p
,
&
cie
->
code_alignment_factor
);
p
+=
count
;
count
=
dwarf_read_leb128
(
p
,
&
cie
->
data_alignment_factor
);
p
+=
count
;
/*
* Which column in the rule table contains the
* return address?
*/
if
(
cie
->
version
==
1
)
{
cie
->
return_address_reg
=
__raw_readb
(
p
);
p
++
;
}
else
{
count
=
dwarf_read_uleb128
(
p
,
&
cie
->
return_address_reg
);
p
+=
count
;
}
if
(
cie
->
augmentation
[
0
]
==
'z'
)
{
unsigned
int
length
,
count
;
cie
->
flags
|=
DWARF_CIE_Z_AUGMENTATION
;
count
=
dwarf_read_uleb128
(
p
,
&
length
);
p
+=
count
;
BUG_ON
((
unsigned
char
*
)
p
>
end
);
cie
->
initial_instructions
=
p
+
length
;
cie
->
augmentation
++
;
}
while
(
*
cie
->
augmentation
)
{
/*
* "L" indicates a byte showing how the
* LSDA pointer is encoded. Skip it.
*/
if
(
*
cie
->
augmentation
==
'L'
)
{
p
++
;
cie
->
augmentation
++
;
}
else
if
(
*
cie
->
augmentation
==
'R'
)
{
/*
* "R" indicates a byte showing
* how FDE addresses are
* encoded.
*/
cie
->
encoding
=
*
(
char
*
)
p
++
;
cie
->
augmentation
++
;
}
else
if
(
*
cie
->
augmentation
==
'P'
)
{
/*
* "R" indicates a personality
* routine in the CIE
* augmentation.
*/
BUG
();
}
else
if
(
*
cie
->
augmentation
==
'S'
)
{
BUG
();
}
else
{
/*
* Unknown augmentation. Assume
* 'z' augmentation.
*/
p
=
cie
->
initial_instructions
;
BUG_ON
(
!
p
);
break
;
}
}
cie
->
initial_instructions
=
p
;
cie
->
instructions_end
=
end
;
/* Add to list */
spin_lock_irqsave
(
&
dwarf_cie_lock
,
flags
);
list_add_tail
(
&
cie
->
link
,
&
dwarf_cie_list
);
spin_unlock_irqrestore
(
&
dwarf_cie_lock
,
flags
);
return
0
;
}
static
int
dwarf_parse_fde
(
void
*
entry
,
u32
entry_type
,
void
*
start
,
unsigned
long
len
)
{
struct
dwarf_fde
*
fde
;
struct
dwarf_cie
*
cie
;
unsigned
long
flags
;
int
count
;
void
*
p
=
start
;
fde
=
kzalloc
(
sizeof
(
*
fde
),
GFP_KERNEL
);
if
(
!
fde
)
return
-
ENOMEM
;
fde
->
length
=
len
;
/*
* In a .eh_frame section the CIE pointer is the
* delta between the address within the FDE
*/
fde
->
cie_pointer
=
(
unsigned
long
)(
p
-
entry_type
-
4
);
cie
=
dwarf_lookup_cie
(
fde
->
cie_pointer
);
fde
->
cie
=
cie
;
if
(
cie
->
encoding
)
count
=
dwarf_read_encoded_value
(
p
,
&
fde
->
initial_location
,
cie
->
encoding
);
else
count
=
dwarf_read_addr
(
p
,
&
fde
->
initial_location
);
p
+=
count
;
if
(
cie
->
encoding
)
count
=
dwarf_read_encoded_value
(
p
,
&
fde
->
address_range
,
cie
->
encoding
&
0x0f
);
else
count
=
dwarf_read_addr
(
p
,
&
fde
->
address_range
);
p
+=
count
;
if
(
fde
->
cie
->
flags
&
DWARF_CIE_Z_AUGMENTATION
)
{
unsigned
int
length
;
count
=
dwarf_read_uleb128
(
p
,
&
length
);
p
+=
count
+
length
;
}
/* Call frame instructions. */
fde
->
instructions
=
p
;
fde
->
end
=
start
+
len
;
/* Add to list. */
spin_lock_irqsave
(
&
dwarf_fde_lock
,
flags
);
list_add_tail
(
&
fde
->
link
,
&
dwarf_fde_list
);
spin_unlock_irqrestore
(
&
dwarf_fde_lock
,
flags
);
return
0
;
}
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
;
frame
=
dwarf_unwind_stack
(
0
,
NULL
);
while
(
frame
&&
frame
->
return_addr
)
{
ops
->
address
(
data
,
frame
->
return_addr
,
1
);
frame
=
frame
->
next
;
}
}
static
struct
unwinder
dwarf_unwinder
=
{
.
name
=
"dwarf-unwinder"
,
.
dump
=
dwarf_unwinder_dump
,
.
rating
=
150
,
};
static
void
dwarf_unwinder_cleanup
(
void
)
{
struct
dwarf_cie
*
cie
,
*
m
;
struct
dwarf_fde
*
fde
,
*
n
;
unsigned
long
flags
;
/*
* 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
)
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
)
kfree
(
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.
*/
void
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
;
INIT_LIST_HEAD
(
&
dwarf_cie_list
);
INIT_LIST_HEAD
(
&
dwarf_fde_list
);
c_entries
=
0
;
f_entries
=
0
;
entry
=
&
__start_eh_frame
;
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
);
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
);
if
(
err
)
goto
out
;
return
;
out:
printk
(
KERN_ERR
"Failed to initialise DWARF unwinder: %d
\n
"
,
err
);
dwarf_unwinder_cleanup
();
}
arch/sh/kernel/entry-common.S
View file @
718dbf37
...
@@ -43,6 +43,7 @@
...
@@ -43,6 +43,7 @@
*
syscall
#
*
syscall
#
*
*
*/
*/
#include <asm/dwarf.h>
#if defined(CONFIG_PREEMPT)
#if defined(CONFIG_PREEMPT)
# define preempt_stop() cli ; TRACE_IRQS_OFF
# define preempt_stop() cli ; TRACE_IRQS_OFF
...
@@ -66,6 +67,11 @@ ENTRY(exception_error)
...
@@ -66,6 +67,11 @@ ENTRY(exception_error)
.
align
2
.
align
2
ret_from_exception
:
ret_from_exception
:
CFI_STARTPROC
simple
CFI_DEF_CFA
r14
,
0
CFI_REL_OFFSET
17
,
64
CFI_REL_OFFSET
15
,
0
CFI_REL_OFFSET
14
,
56
preempt_stop
()
preempt_stop
()
ENTRY
(
ret_from_irq
)
ENTRY
(
ret_from_irq
)
!
!
...
@@ -240,6 +246,7 @@ debug_trap:
...
@@ -240,6 +246,7 @@ debug_trap:
nop
nop
bra
__restore_all
bra
__restore_all
nop
nop
CFI_ENDPROC
.
align
2
.
align
2
1
:
.
long
debug_trap_table
1
:
.
long
debug_trap_table
...
@@ -285,6 +292,7 @@ ret_from_fork:
...
@@ -285,6 +292,7 @@ ret_from_fork:
*
system
calls
and
debug
traps
through
their
respective
jump
tables
.
*
system
calls
and
debug
traps
through
their
respective
jump
tables
.
*/
*/
ENTRY
(
system_call
)
ENTRY
(
system_call
)
setup_frame_reg
#if !defined(CONFIG_CPU_SH2)
#if !defined(CONFIG_CPU_SH2)
mov.l
1
f
,
r9
mov.l
1
f
,
r9
mov.l
@
r9
,
r8
!
Read
from
TRA
(
Trap
Address
)
Register
mov.l
@
r9
,
r8
!
Read
from
TRA
(
Trap
Address
)
Register
...
...
arch/sh/kernel/irq.c
View file @
718dbf37
...
@@ -14,6 +14,7 @@
...
@@ -14,6 +14,7 @@
#include <asm/processor.h>
#include <asm/processor.h>
#include <asm/machvec.h>
#include <asm/machvec.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
#include <asm/dwarf.h>
#include <asm/thread_info.h>
#include <asm/thread_info.h>
#include <cpu/mmu_context.h>
#include <cpu/mmu_context.h>
...
@@ -261,6 +262,9 @@ void __init init_IRQ(void)
...
@@ -261,6 +262,9 @@ void __init init_IRQ(void)
sh_mv
.
mv_init_irq
();
sh_mv
.
mv_init_irq
();
irq_ctx_init
(
smp_processor_id
());
irq_ctx_init
(
smp_processor_id
());
/* This needs to be early, but not too early.. */
dwarf_unwinder_init
();
}
}
#ifdef CONFIG_SPARSE_IRQ
#ifdef CONFIG_SPARSE_IRQ
...
...
arch/sh/kernel/vmlinux.lds.S
View file @
718dbf37
...
@@ -12,7 +12,7 @@ OUTPUT_ARCH(sh)
...
@@ -12,7 +12,7 @@ OUTPUT_ARCH(sh)
#include <asm/thread_info.h>
#include <asm/thread_info.h>
#include <asm/cache.h>
#include <asm/cache.h>
#include <asm
-generic
/vmlinux.lds.h>
#include <asm/vmlinux.lds.h>
ENTRY
(
_start
)
ENTRY
(
_start
)
SECTIONS
SECTIONS
...
@@ -70,6 +70,8 @@ SECTIONS
...
@@ -70,6 +70,8 @@ SECTIONS
_edata
=
.
; /* End of data section */
_edata
=
.
; /* End of data section */
DWARF_EH_FRAME
.
=
ALIGN
(
PAGE_SIZE
)
; /* Init code and data */
.
=
ALIGN
(
PAGE_SIZE
)
; /* Init code and data */
__init_begin
=
.
;
__init_begin
=
.
;
INIT_TEXT_SECTION
(
PAGE_SIZE
)
INIT_TEXT_SECTION
(
PAGE_SIZE
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment