diff options
Diffstat (limited to 'driver/gator_backtrace.c')
-rw-r--r-- | driver/gator_backtrace.c | 76 |
1 files changed, 49 insertions, 27 deletions
diff --git a/driver/gator_backtrace.c b/driver/gator_backtrace.c index 94f01e6..0670d6c 100644 --- a/driver/gator_backtrace.c +++ b/driver/gator_backtrace.c | |||
@@ -10,16 +10,22 @@ | |||
10 | /* | 10 | /* |
11 | * EABI backtrace stores {fp,lr} on the stack. | 11 | * EABI backtrace stores {fp,lr} on the stack. |
12 | */ | 12 | */ |
13 | struct frame_tail_eabi { | 13 | struct stack_frame_eabi { |
14 | union { | 14 | union { |
15 | struct { | 15 | struct { |
16 | unsigned long fp; // points to prev_lr | 16 | unsigned long fp; |
17 | // May be the fp in the case of a leaf function or clang | ||
17 | unsigned long lr; | 18 | unsigned long lr; |
19 | // If lr is really the fp, lr2 is the corresponding lr | ||
20 | unsigned long lr2; | ||
18 | }; | 21 | }; |
19 | // Used to read 32 bit fp/lr from a 64 bit kernel | 22 | // Used to read 32 bit fp/lr from a 64 bit kernel |
20 | struct { | 23 | struct { |
21 | u32 fp_32; | 24 | u32 fp_32; |
25 | // same as lr above | ||
22 | u32 lr_32; | 26 | u32 lr_32; |
27 | // same as lr2 above | ||
28 | u32 lr2_32; | ||
23 | }; | 29 | }; |
24 | }; | 30 | }; |
25 | }; | 31 | }; |
@@ -27,61 +33,76 @@ struct frame_tail_eabi { | |||
27 | static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int depth) | 33 | static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int depth) |
28 | { | 34 | { |
29 | #if defined(__arm__) || defined(__aarch64__) | 35 | #if defined(__arm__) || defined(__aarch64__) |
30 | struct frame_tail_eabi *tail; | 36 | struct stack_frame_eabi *curr; |
31 | struct frame_tail_eabi *next; | 37 | struct stack_frame_eabi bufcurr; |
32 | struct frame_tail_eabi buftail; | ||
33 | #if defined(__arm__) | 38 | #if defined(__arm__) |
34 | const bool is_compat = false; | 39 | const bool is_compat = false; |
35 | unsigned long fp = regs->ARM_fp; | 40 | unsigned long fp = regs->ARM_fp; |
36 | unsigned long sp = regs->ARM_sp; | 41 | unsigned long sp = regs->ARM_sp; |
37 | unsigned long lr = regs->ARM_lr; | 42 | unsigned long lr = regs->ARM_lr; |
38 | const int frame_offset = 4; | 43 | const int gcc_frame_offset = sizeof(unsigned long); |
39 | #else | 44 | #else |
40 | // Is userspace aarch32 (32 bit) | 45 | // Is userspace aarch32 (32 bit) |
41 | const bool is_compat = compat_user_mode(regs); | 46 | const bool is_compat = compat_user_mode(regs); |
42 | unsigned long fp = (is_compat ? regs->regs[11] : regs->regs[29]); | 47 | unsigned long fp = (is_compat ? regs->regs[11] : regs->regs[29]); |
43 | unsigned long sp = (is_compat ? regs->compat_sp : regs->sp); | 48 | unsigned long sp = (is_compat ? regs->compat_sp : regs->sp); |
44 | unsigned long lr = (is_compat ? regs->compat_lr : regs->regs[30]); | 49 | unsigned long lr = (is_compat ? regs->compat_lr : regs->regs[30]); |
45 | const int frame_offset = (is_compat ? 4 : 0); | 50 | const int gcc_frame_offset = (is_compat ? sizeof(u32) : 0); |
46 | #endif | 51 | #endif |
52 | // clang frame offset is always zero | ||
47 | int is_user_mode = user_mode(regs); | 53 | int is_user_mode = user_mode(regs); |
48 | 54 | ||
55 | // pc (current function) has already been added | ||
56 | |||
49 | if (!is_user_mode) { | 57 | if (!is_user_mode) { |
50 | return; | 58 | return; |
51 | } | 59 | } |
52 | 60 | ||
53 | /* entry preamble may not have executed */ | 61 | // Add the lr (parent function) |
62 | // entry preamble may not have executed | ||
54 | gator_add_trace(cpu, lr); | 63 | gator_add_trace(cpu, lr); |
55 | 64 | ||
56 | /* check tail is valid */ | 65 | // check fp is valid |
57 | if (fp == 0 || fp < sp) { | 66 | if (fp == 0 || fp < sp) { |
58 | return; | 67 | return; |
59 | } | 68 | } |
60 | 69 | ||
61 | tail = (struct frame_tail_eabi *)(fp - frame_offset); | 70 | // Get the current stack frame |
71 | curr = (struct stack_frame_eabi *)(fp - gcc_frame_offset); | ||
72 | if ((unsigned long)curr & 3) { | ||
73 | return; | ||
74 | } | ||
62 | 75 | ||
63 | while (depth-- && tail && !((unsigned long)tail & 3)) { | 76 | while (depth-- && curr) { |
64 | /* Also check accessibility of one struct frame_tail beyond */ | 77 | if (!access_ok(VERIFY_READ, curr, sizeof(struct stack_frame_eabi)) || |
65 | if (!access_ok(VERIFY_READ, tail, sizeof(struct frame_tail_eabi))) | 78 | __copy_from_user_inatomic(&bufcurr, curr, sizeof(struct stack_frame_eabi))) { |
66 | return; | ||
67 | if (__copy_from_user_inatomic(&buftail, tail, sizeof(struct frame_tail_eabi))) | ||
68 | return; | 79 | return; |
80 | } | ||
81 | |||
82 | fp = (is_compat ? bufcurr.fp_32 : bufcurr.fp); | ||
83 | lr = (is_compat ? bufcurr.lr_32 : bufcurr.lr); | ||
84 | |||
85 | #define calc_next(reg) ((reg) - gcc_frame_offset) | ||
86 | // Returns true if reg is a valid fp | ||
87 | #define validate_next(reg, curr) \ | ||
88 | ((reg) != 0 && (calc_next(reg) & 3) == 0 && (unsigned long)(curr) < calc_next(reg)) | ||
89 | |||
90 | // Try lr from the stack as the fp because gcc leaf functions do not push lr | ||
91 | // If gcc_frame_offset is non-zero, the lr will also be the clang fp | ||
92 | // This assumes code is at a lower address than the stack | ||
93 | if (validate_next(lr, curr)) { | ||
94 | fp = lr; | ||
95 | lr = (is_compat ? bufcurr.lr2_32 : bufcurr.lr2); | ||
96 | } | ||
69 | 97 | ||
70 | lr = (is_compat ? buftail.lr_32 : buftail.lr); | ||
71 | gator_add_trace(cpu, lr); | 98 | gator_add_trace(cpu, lr); |
72 | 99 | ||
73 | /* frame pointers should progress back up the stack, towards higher addresses */ | 100 | if (!validate_next(fp, curr)) { |
74 | next = (struct frame_tail_eabi *)(lr - frame_offset); | 101 | return; |
75 | if (tail >= next || lr == 0) { | ||
76 | fp = (is_compat ? buftail.fp_32 : buftail.fp); | ||
77 | next = (struct frame_tail_eabi *)(fp - frame_offset); | ||
78 | /* check tail is valid */ | ||
79 | if (tail >= next || fp == 0) { | ||
80 | return; | ||
81 | } | ||
82 | } | 102 | } |
83 | 103 | ||
84 | tail = next; | 104 | // Move to the next stack frame |
105 | curr = (struct stack_frame_eabi *)calc_next(fp); | ||
85 | } | 106 | } |
86 | #endif | 107 | #endif |
87 | } | 108 | } |
@@ -89,11 +110,12 @@ static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int | |||
89 | #if defined(__arm__) || defined(__aarch64__) | 110 | #if defined(__arm__) || defined(__aarch64__) |
90 | static int report_trace(struct stackframe *frame, void *d) | 111 | static int report_trace(struct stackframe *frame, void *d) |
91 | { | 112 | { |
92 | unsigned int *depth = d, cookie = NO_COOKIE, cpu = get_physical_cpu(); | 113 | unsigned int *depth = d, cookie = NO_COOKIE; |
93 | unsigned long addr = frame->pc; | 114 | unsigned long addr = frame->pc; |
94 | 115 | ||
95 | if (*depth) { | 116 | if (*depth) { |
96 | #if defined(MODULE) | 117 | #if defined(MODULE) |
118 | unsigned int cpu = get_physical_cpu(); | ||
97 | struct module *mod = __module_address(addr); | 119 | struct module *mod = __module_address(addr); |
98 | if (mod) { | 120 | if (mod) { |
99 | cookie = get_cookie(cpu, current, mod->name, false); | 121 | cookie = get_cookie(cpu, current, mod->name, false); |