123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- /* 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
- #define loglvl r9
- 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
- mov loglvl, r2
- 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
- 1004: 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).
- */
- mov r0, sv_pc
- mov r1, sv_lr
- mov r2, frame
- bic r1, r1, mask @ mask PC/LR for the mode
- mov r3, loglvl
- 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
- mov r2, loglvl
- 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
- #ifdef CONFIG_IRQSTACKS
- @
- @ Kernel stacks may be discontiguous in memory. If the next
- @ frame is below the previous frame, accept it as long as it
- @ lives in kernel memory.
- @
- cmpls sv_fp, #PAGE_OFFSET
- #endif
- bhi for_each_frame
- 1006: adr r0, .Lbad
- mov r1, loglvl
- mov r2, 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, finished_setup
- .long 1005b, 1006b
- .popsection
- .Lbad: .asciz "%sBacktrace 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
|