David Brazdil | 0f672f6 | 2019-12-10 10:32:29 +0000 | [diff] [blame^] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | /* |
| 3 | * linux/arch/arm/lib/backtrace-clang.S |
| 4 | * |
| 5 | * Copyright (C) 2019 Nathan Huckleberry |
| 6 | * |
| 7 | */ |
| 8 | #include <linux/kern_levels.h> |
| 9 | #include <linux/linkage.h> |
| 10 | #include <asm/assembler.h> |
| 11 | .text |
| 12 | |
| 13 | /* fp is 0 or stack frame */ |
| 14 | |
| 15 | #define frame r4 |
| 16 | #define sv_fp r5 |
| 17 | #define sv_pc r6 |
| 18 | #define mask r7 |
| 19 | #define sv_lr r8 |
| 20 | |
| 21 | ENTRY(c_backtrace) |
| 22 | |
| 23 | #if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK) |
| 24 | ret lr |
| 25 | ENDPROC(c_backtrace) |
| 26 | #else |
| 27 | |
| 28 | |
| 29 | /* |
| 30 | * Clang does not store pc or sp in function prologues so we don't know exactly |
| 31 | * where the function starts. |
| 32 | * |
| 33 | * We can treat the current frame's lr as the saved pc and the preceding |
| 34 | * frame's lr as the current frame's lr, but we can't trace the most recent |
| 35 | * call. Inserting a false stack frame allows us to reference the function |
| 36 | * called last in the stacktrace. |
| 37 | * |
| 38 | * If the call instruction was a bl we can look at the callers branch |
| 39 | * instruction to calculate the saved pc. We can recover the pc in most cases, |
| 40 | * but in cases such as calling function pointers we cannot. In this case, |
| 41 | * default to using the lr. This will be some address in the function, but will |
| 42 | * not be the function start. |
| 43 | * |
| 44 | * Unfortunately due to the stack frame layout we can't dump r0 - r3, but these |
| 45 | * are less frequently saved. |
| 46 | * |
| 47 | * Stack frame layout: |
| 48 | * <larger addresses> |
| 49 | * saved lr |
| 50 | * frame=> saved fp |
| 51 | * optionally saved caller registers (r4 - r10) |
| 52 | * optionally saved arguments (r0 - r3) |
| 53 | * <top of stack frame> |
| 54 | * <smaller addresses> |
| 55 | * |
| 56 | * Functions start with the following code sequence: |
| 57 | * corrected pc => stmfd sp!, {..., fp, lr} |
| 58 | * add fp, sp, #x |
| 59 | * stmfd sp!, {r0 - r3} (optional) |
| 60 | * |
| 61 | * |
| 62 | * |
| 63 | * |
| 64 | * |
| 65 | * |
| 66 | * The diagram below shows an example stack setup for dump_stack. |
| 67 | * |
| 68 | * The frame for c_backtrace has pointers to the code of dump_stack. This is |
| 69 | * why the frame of c_backtrace is used to for the pc calculation of |
| 70 | * dump_stack. This is why we must move back a frame to print dump_stack. |
| 71 | * |
| 72 | * The stored locals for dump_stack are in dump_stack's frame. This means that |
| 73 | * to fully print dump_stack's frame we need both the frame for dump_stack (for |
| 74 | * locals) and the frame that was called by dump_stack (for pc). |
| 75 | * |
| 76 | * To print locals we must know where the function start is. If we read the |
| 77 | * function prologue opcodes we can determine which variables are stored in the |
| 78 | * stack frame. |
| 79 | * |
| 80 | * To find the function start of dump_stack we can look at the stored LR of |
| 81 | * show_stack. It points at the instruction directly after the bl dump_stack. |
| 82 | * We can then read the offset from the bl opcode to determine where the branch |
| 83 | * takes us. The address calculated must be the start of dump_stack. |
| 84 | * |
| 85 | * c_backtrace frame dump_stack: |
| 86 | * {[LR] } ============| ... |
| 87 | * {[FP] } =======| | bl c_backtrace |
| 88 | * | |=> ... |
| 89 | * {[R4-R10]} | |
| 90 | * {[R0-R3] } | show_stack: |
| 91 | * dump_stack frame | ... |
| 92 | * {[LR] } =============| bl dump_stack |
| 93 | * {[FP] } <=======| |=> ... |
| 94 | * {[R4-R10]} |
| 95 | * {[R0-R3] } |
| 96 | */ |
| 97 | |
| 98 | stmfd sp!, {r4 - r9, fp, lr} @ Save an extra register |
| 99 | @ to ensure 8 byte alignment |
| 100 | movs frame, r0 @ if frame pointer is zero |
| 101 | beq no_frame @ we have no stack frames |
| 102 | tst r1, #0x10 @ 26 or 32-bit mode? |
| 103 | moveq mask, #0xfc000003 |
| 104 | movne mask, #0 @ mask for 32-bit |
| 105 | |
| 106 | /* |
| 107 | * Switches the current frame to be the frame for dump_stack. |
| 108 | */ |
| 109 | add frame, sp, #24 @ switch to false frame |
| 110 | for_each_frame: tst frame, mask @ Check for address exceptions |
| 111 | bne no_frame |
| 112 | |
| 113 | /* |
| 114 | * sv_fp is the stack frame with the locals for the current considered |
| 115 | * function. |
| 116 | * |
| 117 | * sv_pc is the saved lr frame the frame above. This is a pointer to a code |
| 118 | * address within the current considered function, but it is not the function |
| 119 | * start. This value gets updated to be the function start later if it is |
| 120 | * possible. |
| 121 | */ |
| 122 | 1001: ldr sv_pc, [frame, #4] @ get saved 'pc' |
| 123 | 1002: ldr sv_fp, [frame, #0] @ get saved fp |
| 124 | |
| 125 | teq sv_fp, mask @ make sure next frame exists |
| 126 | beq no_frame |
| 127 | |
| 128 | /* |
| 129 | * sv_lr is the lr from the function that called the current function. This is |
| 130 | * a pointer to a code address in the current function's caller. sv_lr-4 is |
| 131 | * the instruction used to call the current function. |
| 132 | * |
| 133 | * This sv_lr can be used to calculate the function start if the function was |
| 134 | * called using a bl instruction. If the function start can be recovered sv_pc |
| 135 | * is overwritten with the function start. |
| 136 | * |
| 137 | * If the current function was called using a function pointer we cannot |
| 138 | * recover the function start and instead continue with sv_pc as an arbitrary |
| 139 | * value within the current function. If this is the case we cannot print |
| 140 | * registers for the current function, but the stacktrace is still printed |
| 141 | * properly. |
| 142 | */ |
| 143 | 1003: ldr sv_lr, [sv_fp, #4] @ get saved lr from next frame |
| 144 | |
| 145 | ldr r0, [sv_lr, #-4] @ get call instruction |
| 146 | ldr r3, .Lopcode+4 |
| 147 | and r2, r3, r0 @ is this a bl call |
| 148 | teq r2, r3 |
| 149 | bne finished_setup @ give up if it's not |
| 150 | and r0, #0xffffff @ get call offset 24-bit int |
| 151 | lsl r0, r0, #8 @ sign extend offset |
| 152 | asr r0, r0, #8 |
| 153 | ldr sv_pc, [sv_fp, #4] @ get lr address |
| 154 | add sv_pc, sv_pc, #-4 @ get call instruction address |
| 155 | add sv_pc, sv_pc, #8 @ take care of prefetch |
| 156 | add sv_pc, sv_pc, r0, lsl #2@ find function start |
| 157 | |
| 158 | finished_setup: |
| 159 | |
| 160 | bic sv_pc, sv_pc, mask @ mask PC/LR for the mode |
| 161 | |
| 162 | /* |
| 163 | * Print the function (sv_pc) and where it was called from (sv_lr). |
| 164 | */ |
| 165 | 1004: mov r0, sv_pc |
| 166 | |
| 167 | mov r1, sv_lr |
| 168 | mov r2, frame |
| 169 | bic r1, r1, mask @ mask PC/LR for the mode |
| 170 | bl dump_backtrace_entry |
| 171 | |
| 172 | /* |
| 173 | * Test if the function start is a stmfd instruction to determine which |
| 174 | * registers were stored in the function prologue. |
| 175 | * |
| 176 | * If we could not recover the sv_pc because we were called through a function |
| 177 | * pointer the comparison will fail and no registers will print. Unwinding will |
| 178 | * continue as if there had been no registers stored in this frame. |
| 179 | */ |
| 180 | 1005: ldr r1, [sv_pc, #0] @ if stmfd sp!, {..., fp, lr} |
| 181 | ldr r3, .Lopcode @ instruction exists, |
| 182 | teq r3, r1, lsr #11 |
| 183 | ldr r0, [frame] @ locals are stored in |
| 184 | @ the preceding frame |
| 185 | subeq r0, r0, #4 |
| 186 | bleq dump_backtrace_stm @ dump saved registers |
| 187 | |
| 188 | /* |
| 189 | * If we are out of frames or if the next frame is invalid. |
| 190 | */ |
| 191 | teq sv_fp, #0 @ zero saved fp means |
| 192 | beq no_frame @ no further frames |
| 193 | |
| 194 | cmp sv_fp, frame @ next frame must be |
| 195 | mov frame, sv_fp @ above the current frame |
| 196 | bhi for_each_frame |
| 197 | |
| 198 | 1006: adr r0, .Lbad |
| 199 | mov r1, frame |
| 200 | bl printk |
| 201 | no_frame: ldmfd sp!, {r4 - r9, fp, pc} |
| 202 | ENDPROC(c_backtrace) |
| 203 | .pushsection __ex_table,"a" |
| 204 | .align 3 |
| 205 | .long 1001b, 1006b |
| 206 | .long 1002b, 1006b |
| 207 | .long 1003b, 1006b |
| 208 | .long 1004b, 1006b |
| 209 | .long 1005b, 1006b |
| 210 | .popsection |
| 211 | |
| 212 | .Lbad: .asciz "Backtrace aborted due to bad frame pointer <%p>\n" |
| 213 | .align |
| 214 | .Lopcode: .word 0xe92d4800 >> 11 @ stmfd sp!, {... fp, lr} |
| 215 | .word 0x0b000000 @ bl if these bits are set |
| 216 | |
| 217 | #endif |