~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm/lib/backtrace-clang.S

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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(CONFIG_PRINTK)
 25                 ret     lr
 26 ENDPROC(c_backtrace)
 27 #else
 28 
 29 
 30 /*
 31  * Clang does not store pc or sp in function prologues so we don't know exactly
 32  * where the function starts.
 33  *
 34  * We can treat the current frame's lr as the saved pc and the preceding
 35  * frame's lr as the current frame's lr, but we can't trace the most recent
 36  * call.  Inserting a false stack frame allows us to reference the function
 37  * called last in the stacktrace.
 38  *
 39  * If the call instruction was a bl we can look at the callers branch
 40  * instruction to calculate the saved pc.  We can recover the pc in most cases,
 41  * but in cases such as calling function pointers we cannot. In this case,
 42  * default to using the lr. This will be some address in the function, but will
 43  * not be the function start.
 44  *
 45  * Unfortunately due to the stack frame layout we can't dump r0 - r3, but these
 46  * are less frequently saved.
 47  *
 48  * Stack frame layout:
 49  *              <larger addresses>
 50  *              saved lr
 51  *      frame=> saved fp
 52  *              optionally saved caller registers (r4 - r10)
 53  *              optionally saved arguments (r0 - r3)
 54  *              <top of stack frame>
 55  *              <smaller addresses>
 56  *
 57  * Functions start with the following code sequence:
 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 setup for dump_stack.
 68  *
 69  * The frame for c_backtrace has pointers to the code of dump_stack. This is
 70  * why the frame of c_backtrace is used to for the pc calculation of
 71  * dump_stack. This is why we must move back a frame to print dump_stack.
 72  *
 73  * The stored locals for dump_stack are in dump_stack's frame. This means that
 74  * to fully print dump_stack's frame we need both the frame for dump_stack (for
 75  * locals) and the frame that was called by dump_stack (for pc).
 76  *
 77  * To print locals we must know where the function start is. If we read the
 78  * function prologue opcodes we can determine which variables are stored in the
 79  * stack frame.
 80  *
 81  * To find the function start of dump_stack we can look at the stored LR of
 82  * show_stack. It points at the instruction directly after the bl dump_stack.
 83  * We can then read the offset from the bl opcode to determine where the branch
 84  * takes us.  The address calculated must be the start of dump_stack.
 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}  @ Save an extra register
100                                                 @ to ensure 8 byte alignment
101                 movs    frame, r0               @ if frame pointer is zero
102                 beq     no_frame                @ we have no stack frames
103                 mov     loglvl, r2
104                 tst     r1, #0x10               @ 26 or 32-bit mode?
105                 moveq   mask, #0xfc000003
106                 movne   mask, #0                @ mask for 32-bit
107 
108 /*
109  * Switches the current frame to be the frame for dump_stack.
110  */
111                 add     frame, sp, #24          @ switch to false frame
112 for_each_frame: tst     frame, mask             @ Check for address exceptions
113                 bne     no_frame
114 
115 /*
116  * sv_fp is the stack frame with the locals for the current considered
117  * function.
118  *
119  * sv_pc is the saved lr frame the frame above. This is a pointer to a code
120  * address within the current considered function, but it is not the function
121  * start. This value gets updated to be the function start later if it is
122  * possible.
123  */
124 1001:           ldr     sv_pc, [frame, #4]      @ get saved 'pc'
125 1002:           ldr     sv_fp, [frame, #0]      @ get saved fp
126 
127                 teq     sv_fp, mask             @ make sure next frame exists
128                 beq     no_frame
129 
130 /*
131  * sv_lr is the lr from the function that called the current function. This is
132  * a pointer to a code address in the current function's caller.  sv_lr-4 is
133  * the instruction used to call the current function.
134  *
135  * This sv_lr can be used to calculate the function start if the function was
136  * called using a bl instruction. If the function start can be recovered sv_pc
137  * is overwritten with the function start.
138  *
139  * If the current function was called using a function pointer we cannot
140  * recover the function start and instead continue with sv_pc as an arbitrary
141  * value within the current function. If this is the case we cannot print
142  * registers for the current function, but the stacktrace is still printed
143  * properly.
144  */
145 1003:           ldr     sv_lr, [sv_fp, #4]      @ get saved lr from next frame
146 
147 1004:           ldr     r0, [sv_lr, #-4]        @ get call instruction
148                 ldr     r3, .Lopcode+4
149                 and     r2, r3, r0              @ is this a bl call
150                 teq     r2, r3
151                 bne     finished_setup          @ give up if it's not
152                 and     r0, #0xffffff           @ get call offset 24-bit int
153                 lsl     r0, r0, #8              @ sign extend offset
154                 asr     r0, r0, #8
155                 ldr     sv_pc, [sv_fp, #4]      @ get lr address
156                 add     sv_pc, sv_pc, #-4       @ get call instruction address
157                 add     sv_pc, sv_pc, #8        @ take care of prefetch
158                 add     sv_pc, sv_pc, r0, lsl #2@ find function start
159 
160 finished_setup:
161 
162                 bic     sv_pc, sv_pc, mask      @ mask PC/LR for the mode
163 
164 /*
165  * Print the function (sv_pc) and where it was called from (sv_lr).
166  */
167                 mov     r0, sv_pc
168 
169                 mov     r1, sv_lr
170                 mov     r2, frame
171                 bic     r1, r1, mask            @ mask PC/LR for the mode
172                 mov     r3, loglvl
173                 bl      dump_backtrace_entry
174 
175 /*
176  * Test if the function start is a stmfd instruction to determine which
177  * registers were stored in the function prologue.
178  *
179  * If we could not recover the sv_pc because we were called through a function
180  * pointer the comparison will fail and no registers will print. Unwinding will
181  * continue as if there had been no registers stored in this frame.
182  */
183 1005:           ldr     r1, [sv_pc, #0]         @ if stmfd sp!, {..., fp, lr}
184                 ldr     r3, .Lopcode            @ instruction exists,
185                 teq     r3, r1, lsr #11
186                 ldr     r0, [frame]             @ locals are stored in
187                                                 @ the preceding frame
188                 subeq   r0, r0, #4
189                 mov     r2, loglvl
190                 bleq    dump_backtrace_stm      @ dump saved registers
191 
192 /*
193  * If we are out of frames or if the next frame is invalid.
194  */
195                 teq     sv_fp, #0               @ zero saved fp means
196                 beq     no_frame                @ no further frames
197 
198                 cmp     sv_fp, frame            @ next frame must be
199                 mov     frame, sv_fp            @ above the current frame
200 #ifdef CONFIG_IRQSTACKS
201                 @
202                 @ Kernel stacks may be discontiguous in memory. If the next
203                 @ frame is below the previous frame, accept it as long as it
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 due to bad frame pointer <%p>\n"
226                 .align
227 .Lopcode:       .word   0xe92d4800 >> 11        @ stmfd sp!, {... fp, lr}
228                 .word   0x0b000000              @ bl if these bits are set
229 
230 #endif

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php