Josh Poimboeuf | 7c7900f | 2016-09-16 14:18:12 -0500 | [diff] [blame] | 1 | #include <linux/sched.h> |
| 2 | #include <linux/ftrace.h> |
| 3 | #include <asm/ptrace.h> |
| 4 | #include <asm/bitops.h> |
| 5 | #include <asm/stacktrace.h> |
| 6 | #include <asm/unwind.h> |
| 7 | |
Josh Poimboeuf | cfee9ed | 2016-10-06 00:28:40 -0500 | [diff] [blame] | 8 | unsigned long unwind_get_return_address(struct unwind_state *state) |
| 9 | { |
Josh Poimboeuf | 55f856e | 2016-11-27 23:13:07 -0600 | [diff] [blame] | 10 | unsigned long addr; |
Josh Poimboeuf | c2d75e0 | 2016-11-17 09:57:23 -0600 | [diff] [blame] | 11 | |
Josh Poimboeuf | cfee9ed | 2016-10-06 00:28:40 -0500 | [diff] [blame] | 12 | if (unwind_done(state)) |
| 13 | return 0; |
| 14 | |
Josh Poimboeuf | 55f856e | 2016-11-27 23:13:07 -0600 | [diff] [blame] | 15 | addr = READ_ONCE_NOCHECK(*state->sp); |
| 16 | |
Josh Poimboeuf | cfee9ed | 2016-10-06 00:28:40 -0500 | [diff] [blame] | 17 | return ftrace_graph_ret_addr(state->task, &state->graph_idx, |
Josh Poimboeuf | c2d75e0 | 2016-11-17 09:57:23 -0600 | [diff] [blame] | 18 | addr, state->sp); |
Josh Poimboeuf | cfee9ed | 2016-10-06 00:28:40 -0500 | [diff] [blame] | 19 | } |
| 20 | EXPORT_SYMBOL_GPL(unwind_get_return_address); |
| 21 | |
Josh Poimboeuf | ee9f8fc | 2017-07-24 18:36:57 -0500 | [diff] [blame] | 22 | unsigned long *unwind_get_return_address_ptr(struct unwind_state *state) |
| 23 | { |
| 24 | return NULL; |
| 25 | } |
| 26 | |
Josh Poimboeuf | 7c7900f | 2016-09-16 14:18:12 -0500 | [diff] [blame] | 27 | bool unwind_next_frame(struct unwind_state *state) |
| 28 | { |
| 29 | struct stack_info *info = &state->stack_info; |
| 30 | |
| 31 | if (unwind_done(state)) |
| 32 | return false; |
| 33 | |
| 34 | do { |
Josh Poimboeuf | 55f856e | 2016-11-27 23:13:07 -0600 | [diff] [blame] | 35 | for (state->sp++; state->sp < info->end; state->sp++) { |
| 36 | unsigned long addr = READ_ONCE_NOCHECK(*state->sp); |
Josh Poimboeuf | c2d75e0 | 2016-11-17 09:57:23 -0600 | [diff] [blame] | 37 | |
Josh Poimboeuf | c2d75e0 | 2016-11-17 09:57:23 -0600 | [diff] [blame] | 38 | if (__kernel_text_address(addr)) |
Josh Poimboeuf | 7c7900f | 2016-09-16 14:18:12 -0500 | [diff] [blame] | 39 | return true; |
Josh Poimboeuf | 55f856e | 2016-11-27 23:13:07 -0600 | [diff] [blame] | 40 | } |
Josh Poimboeuf | 7c7900f | 2016-09-16 14:18:12 -0500 | [diff] [blame] | 41 | |
Josh Poimboeuf | e335bb5 | 2017-04-17 08:44:00 -0500 | [diff] [blame] | 42 | state->sp = PTR_ALIGN(info->next_sp, sizeof(long)); |
Josh Poimboeuf | 7c7900f | 2016-09-16 14:18:12 -0500 | [diff] [blame] | 43 | |
| 44 | } while (!get_stack_info(state->sp, state->task, info, |
| 45 | &state->stack_mask)); |
| 46 | |
| 47 | return false; |
| 48 | } |
| 49 | EXPORT_SYMBOL_GPL(unwind_next_frame); |
| 50 | |
| 51 | void __unwind_start(struct unwind_state *state, struct task_struct *task, |
| 52 | struct pt_regs *regs, unsigned long *first_frame) |
| 53 | { |
| 54 | memset(state, 0, sizeof(*state)); |
| 55 | |
| 56 | state->task = task; |
Josh Poimboeuf | e335bb5 | 2017-04-17 08:44:00 -0500 | [diff] [blame] | 57 | state->sp = PTR_ALIGN(first_frame, sizeof(long)); |
Josh Poimboeuf | 7c7900f | 2016-09-16 14:18:12 -0500 | [diff] [blame] | 58 | |
| 59 | get_stack_info(first_frame, state->task, &state->stack_info, |
| 60 | &state->stack_mask); |
| 61 | |
Josh Poimboeuf | 7fbe6ac | 2016-10-24 08:31:27 -0500 | [diff] [blame] | 62 | /* |
| 63 | * The caller can provide the address of the first frame directly |
| 64 | * (first_frame) or indirectly (regs->sp) to indicate which stack frame |
| 65 | * to start unwinding at. Skip ahead until we reach it. |
| 66 | */ |
| 67 | if (!unwind_done(state) && |
| 68 | (!on_stack(&state->stack_info, first_frame, sizeof(long)) || |
| 69 | !__kernel_text_address(*first_frame))) |
Josh Poimboeuf | 7c7900f | 2016-09-16 14:18:12 -0500 | [diff] [blame] | 70 | unwind_next_frame(state); |
| 71 | } |
| 72 | EXPORT_SYMBOL_GPL(__unwind_start); |