|  | /* SPDX-License-Identifier: GPL-2.0-only */ | 
|  | /* | 
|  | *  linux/arch/arm/lib/backtrace-clang.S | 
|  | * | 
|  | *  Copyright (C) 2019 Nathan Huckleberry | 
|  | * | 
|  | */ | 
|  | #include <linux/kern_levels.h> | 
|  | #include <linux/linkage.h> | 
|  | #include <asm/assembler.h> | 
|  | .text | 
|  |  | 
|  | /* fp is 0 or stack frame */ | 
|  |  | 
|  | #define frame	r4 | 
|  | #define sv_fp	r5 | 
|  | #define sv_pc	r6 | 
|  | #define mask	r7 | 
|  | #define sv_lr	r8 | 
|  |  | 
|  | ENTRY(c_backtrace) | 
|  |  | 
|  | #if !defined(CONFIG_FRAME_POINTER) || !defined(CONFIG_PRINTK) | 
|  | ret	lr | 
|  | ENDPROC(c_backtrace) | 
|  | #else | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Clang does not store pc or sp in function prologues so we don't know exactly | 
|  | * where the function starts. | 
|  | * | 
|  | * We can treat the current frame's lr as the saved pc and the preceding | 
|  | * frame's lr as the current frame's lr, but we can't trace the most recent | 
|  | * call.  Inserting a false stack frame allows us to reference the function | 
|  | * called last in the stacktrace. | 
|  | * | 
|  | * If the call instruction was a bl we can look at the callers branch | 
|  | * instruction to calculate the saved pc.  We can recover the pc in most cases, | 
|  | * but in cases such as calling function pointers we cannot. In this case, | 
|  | * default to using the lr. This will be some address in the function, but will | 
|  | * not be the function start. | 
|  | * | 
|  | * Unfortunately due to the stack frame layout we can't dump r0 - r3, but these | 
|  | * are less frequently saved. | 
|  | * | 
|  | * Stack frame layout: | 
|  | * 		<larger addresses> | 
|  | * 		saved lr | 
|  | * 	frame=> saved fp | 
|  | * 		optionally saved caller registers (r4 - r10) | 
|  | * 		optionally saved arguments (r0 - r3) | 
|  | * 		<top of stack frame> | 
|  | * 		<smaller addresses> | 
|  | * | 
|  | * Functions start with the following code sequence: | 
|  | * corrected pc =>  stmfd sp!, {..., fp, lr} | 
|  | *		add fp, sp, #x | 
|  | *		stmfd sp!, {r0 - r3} (optional) | 
|  | * | 
|  | * | 
|  | * | 
|  | * | 
|  | * | 
|  | * | 
|  | * The diagram below shows an example stack setup for dump_stack. | 
|  | * | 
|  | * The frame for c_backtrace has pointers to the code of dump_stack. This is | 
|  | * why the frame of c_backtrace is used to for the pc calculation of | 
|  | * dump_stack. This is why we must move back a frame to print dump_stack. | 
|  | * | 
|  | * The stored locals for dump_stack are in dump_stack's frame. This means that | 
|  | * to fully print dump_stack's frame we need both the frame for dump_stack (for | 
|  | * locals) and the frame that was called by dump_stack (for pc). | 
|  | * | 
|  | * To print locals we must know where the function start is. If we read the | 
|  | * function prologue opcodes we can determine which variables are stored in the | 
|  | * stack frame. | 
|  | * | 
|  | * To find the function start of dump_stack we can look at the stored LR of | 
|  | * show_stack. It points at the instruction directly after the bl dump_stack. | 
|  | * We can then read the offset from the bl opcode to determine where the branch | 
|  | * takes us.  The address calculated must be the start of dump_stack. | 
|  | * | 
|  | * c_backtrace frame           dump_stack: | 
|  | * {[LR]    }  ============|   ... | 
|  | * {[FP]    }  =======|    |   bl c_backtrace | 
|  | *                    |    |=> ... | 
|  | * {[R4-R10]}         | | 
|  | * {[R0-R3] }         |        show_stack: | 
|  | * dump_stack frame   |        ... | 
|  | * {[LR]    } =============|   bl dump_stack | 
|  | * {[FP]    } <=======|    |=> ... | 
|  | * {[R4-R10]} | 
|  | * {[R0-R3] } | 
|  | */ | 
|  |  | 
|  | stmfd	sp!, {r4 - r9, fp, lr}	@ Save an extra register | 
|  | @ to ensure 8 byte alignment | 
|  | movs	frame, r0		@ if frame pointer is zero | 
|  | beq	no_frame		@ we have no stack frames | 
|  | tst	r1, #0x10		@ 26 or 32-bit mode? | 
|  | moveq	mask, #0xfc000003 | 
|  | movne	mask, #0		@ mask for 32-bit | 
|  |  | 
|  | /* | 
|  | * Switches the current frame to be the frame for dump_stack. | 
|  | */ | 
|  | add	frame, sp, #24		@ switch to false frame | 
|  | for_each_frame:	tst	frame, mask		@ Check for address exceptions | 
|  | bne	no_frame | 
|  |  | 
|  | /* | 
|  | * sv_fp is the stack frame with the locals for the current considered | 
|  | * function. | 
|  | * | 
|  | * sv_pc is the saved lr frame the frame above. This is a pointer to a code | 
|  | * address within the current considered function, but it is not the function | 
|  | * start. This value gets updated to be the function start later if it is | 
|  | * possible. | 
|  | */ | 
|  | 1001:		ldr	sv_pc, [frame, #4]	@ get saved 'pc' | 
|  | 1002:		ldr	sv_fp, [frame, #0]	@ get saved fp | 
|  |  | 
|  | teq	sv_fp, mask		@ make sure next frame exists | 
|  | beq	no_frame | 
|  |  | 
|  | /* | 
|  | * sv_lr is the lr from the function that called the current function. This is | 
|  | * a pointer to a code address in the current function's caller.  sv_lr-4 is | 
|  | * the instruction used to call the current function. | 
|  | * | 
|  | * This sv_lr can be used to calculate the function start if the function was | 
|  | * called using a bl instruction. If the function start can be recovered sv_pc | 
|  | * is overwritten with the function start. | 
|  | * | 
|  | * If the current function was called using a function pointer we cannot | 
|  | * recover the function start and instead continue with sv_pc as an arbitrary | 
|  | * value within the current function. If this is the case we cannot print | 
|  | * registers for the current function, but the stacktrace is still printed | 
|  | * properly. | 
|  | */ | 
|  | 1003:		ldr	sv_lr, [sv_fp, #4]	@ get saved lr from next frame | 
|  |  | 
|  | ldr	r0, [sv_lr, #-4]	@ get call instruction | 
|  | ldr	r3, .Lopcode+4 | 
|  | and	r2, r3, r0		@ is this a bl call | 
|  | teq	r2, r3 | 
|  | bne	finished_setup		@ give up if it's not | 
|  | and	r0, #0xffffff		@ get call offset 24-bit int | 
|  | lsl	r0, r0, #8		@ sign extend offset | 
|  | asr	r0, r0, #8 | 
|  | ldr	sv_pc, [sv_fp, #4]	@ get lr address | 
|  | add	sv_pc, sv_pc, #-4	@ get call instruction address | 
|  | add	sv_pc, sv_pc, #8	@ take care of prefetch | 
|  | add	sv_pc, sv_pc, r0, lsl #2@ find function start | 
|  |  | 
|  | finished_setup: | 
|  |  | 
|  | bic	sv_pc, sv_pc, mask	@ mask PC/LR for the mode | 
|  |  | 
|  | /* | 
|  | * Print the function (sv_pc) and where it was called from (sv_lr). | 
|  | */ | 
|  | 1004:		mov	r0, sv_pc | 
|  |  | 
|  | mov	r1, sv_lr | 
|  | mov	r2, frame | 
|  | bic	r1, r1, mask		@ mask PC/LR for the mode | 
|  | bl	dump_backtrace_entry | 
|  |  | 
|  | /* | 
|  | * Test if the function start is a stmfd instruction to determine which | 
|  | * registers were stored in the function prologue. | 
|  | * | 
|  | * If we could not recover the sv_pc because we were called through a function | 
|  | * pointer the comparison will fail and no registers will print. Unwinding will | 
|  | * continue as if there had been no registers stored in this frame. | 
|  | */ | 
|  | 1005:		ldr	r1, [sv_pc, #0]		@ if stmfd sp!, {..., fp, lr} | 
|  | ldr	r3, .Lopcode		@ instruction exists, | 
|  | teq	r3, r1, lsr #11 | 
|  | ldr	r0, [frame]		@ locals are stored in | 
|  | @ the preceding frame | 
|  | subeq	r0, r0, #4 | 
|  | bleq	dump_backtrace_stm	@ dump saved registers | 
|  |  | 
|  | /* | 
|  | * If we are out of frames or if the next frame is invalid. | 
|  | */ | 
|  | teq	sv_fp, #0		@ zero saved fp means | 
|  | beq	no_frame		@ no further frames | 
|  |  | 
|  | cmp	sv_fp, frame		@ next frame must be | 
|  | mov	frame, sv_fp		@ above the current frame | 
|  | bhi	for_each_frame | 
|  |  | 
|  | 1006:		adr	r0, .Lbad | 
|  | mov	r1, frame | 
|  | bl	printk | 
|  | no_frame:	ldmfd	sp!, {r4 - r9, fp, pc} | 
|  | ENDPROC(c_backtrace) | 
|  | .pushsection __ex_table,"a" | 
|  | .align	3 | 
|  | .long	1001b, 1006b | 
|  | .long	1002b, 1006b | 
|  | .long	1003b, 1006b | 
|  | .long	1004b, 1006b | 
|  | .long   1005b, 1006b | 
|  | .popsection | 
|  |  | 
|  | .Lbad:		.asciz	"Backtrace aborted due to bad frame pointer <%p>\n" | 
|  | .align | 
|  | .Lopcode:	.word	0xe92d4800 >> 11	@ stmfd sp!, {... fp, lr} | 
|  | .word	0x0b000000		@ bl if these bits are set | 
|  |  | 
|  | #endif |