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) { |
