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 #define loglvl r9 21 22 ENTRY(c_backtrace) 23 24 #if !defined(CONFIG_FRAME_POINTER) || !defined 25 ret lr 26 ENDPROC(c_backtrace) 27 #else 28 29 30 /* 31 * Clang does not store pc or sp in function p 32 * where the function starts. 33 * 34 * We can treat the current frame's lr as the 35 * frame's lr as the current frame's lr, but w 36 * call. Inserting a false stack frame allows 37 * called last in the stacktrace. 38 * 39 * If the call instruction was a bl we can loo 40 * instruction to calculate the saved pc. We 41 * but in cases such as calling function point 42 * default to using the lr. This will be some 43 * not be the function start. 44 * 45 * Unfortunately due to the stack frame layout 46 * are less frequently saved. 47 * 48 * Stack frame layout: 49 * <larger addresses> 50 * saved lr 51 * frame=> saved fp 52 * optionally saved caller regist 53 * optionally saved arguments (r0 54 * <top of stack frame> 55 * <smaller addresses> 56 * 57 * Functions start with the following code seq 58 * corrected pc => stmfd sp!, {..., fp, lr} 59 * add fp, sp, #x 60 * stmfd sp!, {r0 - r3} (optional 61 * 62 * 63 * 64 * 65 * 66 * 67 * The diagram below shows an example stack se 68 * 69 * The frame for c_backtrace has pointers to t 70 * why the frame of c_backtrace is used to for 71 * dump_stack. This is why we must move back a 72 * 73 * The stored locals for dump_stack are in dum 74 * to fully print dump_stack's frame we need b 75 * locals) and the frame that was called by du 76 * 77 * To print locals we must know where the func 78 * function prologue opcodes we can determine 79 * stack frame. 80 * 81 * To find the function start of dump_stack we 82 * show_stack. It points at the instruction di 83 * We can then read the offset from the bl opc 84 * takes us. The address calculated must be t 85 * 86 * c_backtrace frame dump_stack: 87 * {[LR] } ============| ... 88 * {[FP] } =======| | bl c_backtrace 89 * | |=> ... 90 * {[R4-R10]} | 91 * {[R0-R3] } | show_stack: 92 * dump_stack frame | ... 93 * {[LR] } =============| bl dump_stack 94 * {[FP] } <=======| |=> ... 95 * {[R4-R10]} 96 * {[R0-R3] } 97 */ 98 99 stmfd sp!, {r4 - r9, fp, lr} 100 101 movs frame, r0 102 beq no_frame 103 mov loglvl, r2 104 tst r1, #0x10 105 moveq mask, #0xfc000003 106 movne mask, #0 107 108 /* 109 * Switches the current frame to be the frame 110 */ 111 add frame, sp, #24 112 for_each_frame: tst frame, mask 113 bne no_frame 114 115 /* 116 * sv_fp is the stack frame with the locals fo 117 * function. 118 * 119 * sv_pc is the saved lr frame the frame above 120 * address within the current considered funct 121 * start. This value gets updated to be the fu 122 * possible. 123 */ 124 1001: ldr sv_pc, [frame, #4] 125 1002: ldr sv_fp, [frame, #0] 126 127 teq sv_fp, mask 128 beq no_frame 129 130 /* 131 * sv_lr is the lr from the function that call 132 * a pointer to a code address in the current 133 * the instruction used to call the current fu 134 * 135 * This sv_lr can be used to calculate the fun 136 * called using a bl instruction. If the funct 137 * is overwritten with the function start. 138 * 139 * If the current function was called using a 140 * recover the function start and instead cont 141 * value within the current function. If this 142 * registers for the current function, but the 143 * properly. 144 */ 145 1003: ldr sv_lr, [sv_fp, #4] 146 147 1004: ldr r0, [sv_lr, #-4] 148 ldr r3, .Lopcode+4 149 and r2, r3, r0 150 teq r2, r3 151 bne finished_setup 152 and r0, #0xffffff 153 lsl r0, r0, #8 154 asr r0, r0, #8 155 ldr sv_pc, [sv_fp, #4] 156 add sv_pc, sv_pc, #-4 157 add sv_pc, sv_pc, #8 158 add sv_pc, sv_pc, r0, lsl 159 160 finished_setup: 161 162 bic sv_pc, sv_pc, mask 163 164 /* 165 * Print the function (sv_pc) and where it was 166 */ 167 mov r0, sv_pc 168 169 mov r1, sv_lr 170 mov r2, frame 171 bic r1, r1, mask 172 mov r3, loglvl 173 bl dump_backtrace_entry 174 175 /* 176 * Test if the function start is a stmfd instr 177 * registers were stored in the function prolo 178 * 179 * If we could not recover the sv_pc because w 180 * pointer the comparison will fail and no reg 181 * continue as if there had been no registers 182 */ 183 1005: ldr r1, [sv_pc, #0] 184 ldr r3, .Lopcode 185 teq r3, r1, lsr #11 186 ldr r0, [frame] 187 188 subeq r0, r0, #4 189 mov r2, loglvl 190 bleq dump_backtrace_stm 191 192 /* 193 * If we are out of frames or if the next fram 194 */ 195 teq sv_fp, #0 196 beq no_frame 197 198 cmp sv_fp, frame 199 mov frame, sv_fp 200 #ifdef CONFIG_IRQSTACKS 201 @ 202 @ Kernel stacks may be discont 203 @ frame is below the previous 204 @ lives in kernel memory. 205 @ 206 cmpls sv_fp, #PAGE_OFFSET 207 #endif 208 bhi for_each_frame 209 210 1006: adr r0, .Lbad 211 mov r1, loglvl 212 mov r2, frame 213 bl _printk 214 no_frame: ldmfd sp!, {r4 - r9, fp, pc} 215 ENDPROC(c_backtrace) 216 .pushsection __ex_table,"a" 217 .align 3 218 .long 1001b, 1006b 219 .long 1002b, 1006b 220 .long 1003b, 1006b 221 .long 1004b, finished_setup 222 .long 1005b, 1006b 223 .popsection 224 225 .Lbad: .asciz "%sBacktrace aborted d 226 .align 227 .Lopcode: .word 0xe92d4800 >> 11 228 .word 0x0b000000 229 230 #endif
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.