diff options
-rw-r--r-- | arch/arm/include/asm/ftrace.h | 34 | ||||
-rw-r--r-- | arch/arm/kernel/Makefile | 4 | ||||
-rw-r--r-- | arch/arm/kernel/return_address.c | 71 | ||||
-rw-r--r-- | arch/arm/kernel/stacktrace.c | 4 |
4 files changed, 110 insertions, 3 deletions
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index 39c8bc1a006a..d74265cffd86 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h | |||
@@ -11,4 +11,38 @@ extern void mcount(void); | |||
11 | 11 | ||
12 | #endif | 12 | #endif |
13 | 13 | ||
14 | #ifndef __ASSEMBLY__ | ||
15 | |||
16 | #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) | ||
17 | /* | ||
18 | * return_address uses walk_stackframe to do it's work. If both | ||
19 | * CONFIG_FRAME_POINTER=y and CONFIG_ARM_UNWIND=y walk_stackframe uses unwind | ||
20 | * information. For this to work in the function tracer many functions would | ||
21 | * have to be marked with __notrace. So for now just depend on | ||
22 | * !CONFIG_ARM_UNWIND. | ||
23 | */ | ||
24 | |||
25 | void *return_address(unsigned int); | ||
26 | |||
27 | #else | ||
28 | |||
29 | extern inline void *return_address(unsigned int level) | ||
30 | { | ||
31 | return NULL; | ||
32 | } | ||
33 | |||
34 | #endif | ||
35 | |||
36 | #define HAVE_ARCH_CALLER_ADDR | ||
37 | |||
38 | #define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0)) | ||
39 | #define CALLER_ADDR1 ((unsigned long)return_address(1)) | ||
40 | #define CALLER_ADDR2 ((unsigned long)return_address(2)) | ||
41 | #define CALLER_ADDR3 ((unsigned long)return_address(3)) | ||
42 | #define CALLER_ADDR4 ((unsigned long)return_address(4)) | ||
43 | #define CALLER_ADDR5 ((unsigned long)return_address(5)) | ||
44 | #define CALLER_ADDR6 ((unsigned long)return_address(6)) | ||
45 | |||
46 | #endif /* ifndef __ASSEMBLY__ */ | ||
47 | |||
14 | #endif /* _ASM_ARM_FTRACE */ | 48 | #endif /* _ASM_ARM_FTRACE */ |
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index ff89d0b3abc5..3213c9382b17 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
@@ -8,10 +8,12 @@ ifdef CONFIG_DYNAMIC_FTRACE | |||
8 | CFLAGS_REMOVE_ftrace.o = -pg | 8 | CFLAGS_REMOVE_ftrace.o = -pg |
9 | endif | 9 | endif |
10 | 10 | ||
11 | CFLAGS_REMOVE_return_address.o = -pg | ||
12 | |||
11 | # Object file lists. | 13 | # Object file lists. |
12 | 14 | ||
13 | obj-y := compat.o elf.o entry-armv.o entry-common.o irq.o \ | 15 | obj-y := compat.o elf.o entry-armv.o entry-common.o irq.o \ |
14 | process.o ptrace.o setup.o signal.o \ | 16 | process.o ptrace.o return_address.o setup.o signal.o \ |
15 | sys_arm.o stacktrace.o time.o traps.o | 17 | sys_arm.o stacktrace.o time.o traps.o |
16 | 18 | ||
17 | obj-$(CONFIG_ISA_DMA_API) += dma.o | 19 | obj-$(CONFIG_ISA_DMA_API) += dma.o |
diff --git a/arch/arm/kernel/return_address.c b/arch/arm/kernel/return_address.c new file mode 100644 index 000000000000..df246da4ceca --- /dev/null +++ b/arch/arm/kernel/return_address.c | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | * arch/arm/kernel/return_address.c | ||
3 | * | ||
4 | * Copyright (C) 2009 Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de> | ||
5 | * for Pengutronix | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | |||
13 | #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) | ||
14 | #include <linux/sched.h> | ||
15 | |||
16 | #include <asm/stacktrace.h> | ||
17 | |||
18 | struct return_address_data { | ||
19 | unsigned int level; | ||
20 | void *addr; | ||
21 | }; | ||
22 | |||
23 | static int save_return_addr(struct stackframe *frame, void *d) | ||
24 | { | ||
25 | struct return_address_data *data = d; | ||
26 | |||
27 | if (!data->level) { | ||
28 | data->addr = (void *)frame->lr; | ||
29 | |||
30 | return 1; | ||
31 | } else { | ||
32 | --data->level; | ||
33 | return 0; | ||
34 | } | ||
35 | } | ||
36 | |||
37 | void *return_address(unsigned int level) | ||
38 | { | ||
39 | struct return_address_data data; | ||
40 | struct stackframe frame; | ||
41 | register unsigned long current_sp asm ("sp"); | ||
42 | |||
43 | data.level = level + 1; | ||
44 | |||
45 | frame.fp = (unsigned long)__builtin_frame_address(0); | ||
46 | frame.sp = current_sp; | ||
47 | frame.lr = (unsigned long)__builtin_return_address(0); | ||
48 | frame.pc = (unsigned long)return_address; | ||
49 | |||
50 | walk_stackframe(&frame, save_return_addr, &data); | ||
51 | |||
52 | if (!data.level) | ||
53 | return data.addr; | ||
54 | else | ||
55 | return NULL; | ||
56 | } | ||
57 | |||
58 | #else /* if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) */ | ||
59 | |||
60 | #if defined(CONFIG_ARM_UNWIND) | ||
61 | #warning "TODO: return_address should use unwind tables" | ||
62 | #endif | ||
63 | |||
64 | void *return_address(unsigned int level) | ||
65 | { | ||
66 | return NULL; | ||
67 | } | ||
68 | |||
69 | #endif /* if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) / else */ | ||
70 | |||
71 | EXPORT_SYMBOL_GPL(return_address); | ||
diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c index 9f444e5cc165..20b7411e47fd 100644 --- a/arch/arm/kernel/stacktrace.c +++ b/arch/arm/kernel/stacktrace.c | |||
@@ -21,7 +21,7 @@ | |||
21 | * Note that with framepointer enabled, even the leaf functions have the same | 21 | * Note that with framepointer enabled, even the leaf functions have the same |
22 | * prologue and epilogue, therefore we can ignore the LR value in this case. | 22 | * prologue and epilogue, therefore we can ignore the LR value in this case. |
23 | */ | 23 | */ |
24 | int unwind_frame(struct stackframe *frame) | 24 | int notrace unwind_frame(struct stackframe *frame) |
25 | { | 25 | { |
26 | unsigned long high, low; | 26 | unsigned long high, low; |
27 | unsigned long fp = frame->fp; | 27 | unsigned long fp = frame->fp; |
@@ -43,7 +43,7 @@ int unwind_frame(struct stackframe *frame) | |||
43 | } | 43 | } |
44 | #endif | 44 | #endif |
45 | 45 | ||
46 | void walk_stackframe(struct stackframe *frame, | 46 | void notrace walk_stackframe(struct stackframe *frame, |
47 | int (*fn)(struct stackframe *, void *), void *data) | 47 | int (*fn)(struct stackframe *, void *), void *data) |
48 | { | 48 | { |
49 | while (1) { | 49 | while (1) { |