[PATCH 3/4] arm64: unwind: reference pt_regs via embedded stack frame

Mark Rutland mark.rutland at arm.com
Tue Jul 25 02:53:55 PDT 2017


On Mon, Jul 24, 2017 at 08:09:08PM +0100, Ard Biesheuvel wrote:
> On 24 July 2017 at 19:34, Ard Biesheuvel <ard.biesheuvel at linaro.org> wrote:
> > On 24 July 2017 at 18:54, Mark Rutland <mark.rutland at arm.com> wrote:
> >> So here, we're using information from three records (where the last
> >> might have been faked from the current FP + LR). We look at the previous
> >> frame's LR value to determine whether the current frame's fp points to a
> >> next frame that's associated with some regs:
> >>
> >>          +----+----------------+
> >>          | fp | interrupted PC | /* embedded in pt_regs */
> >>          +----+----------------+
> >>            /\
> >>            ||
> >>          +----+----------------+
> >> where => | fp | < entry PC >   |
> >>          +----+----------------+
> >>            /\
> >>            ||
> >>          +----+----------------+
> >>          | fp | where          | /* "where" is in exception text */
> >>          +----+----------------+
> >>
> >> Which (IIUC) has three problems, inherited from the existing approach.

> >> 3) The entry assembly can enable exceptions before calling C code, so if
> >>    an exception is taken at the right time, these will eb in the
> >>    backtrace without a surrounding frame in the exception text.

[...]

> > I guess that's an improvement, yes. At least we won't have false
> > positives where a stack frame is misidentified as being on embedded in
> > pt_regs. But it does rely on the bl instruction being used even in
> > cases where we know we won't be returning, i.e., jumps to bad_mode,
> > do_sp_pc_abort and do_undef_instr from .entry.text

Good point, yes.

This would be good for backtracing, regardless.

> I guess we could do something along the lines of
> 
> """
> diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
> index 45947b36f1ff..37f67c2080dc 100644
> --- a/arch/arm64/kernel/entry.S
> +++ b/arch/arm64/kernel/entry.S
> @@ -60,6 +60,22 @@
>  #endif
>         .endm
> 
> +       .macro          b_e, target, b=b
> +       .pushsection    ".rodata.entrycalls", "a", @progbits
> +       .align          2
> +       .long           6767f - .
> +       .popsection
> +6767:  \b              \target
> +       .endm
> +
> +       .macro          bl_e, target
> +       b_e             \target, bl
> +       .endm
> +
> +       .macro          blr_e, target
> +       b_e             \target, blr
> +       .endm
> +
>  /*
>   * Bad Abort numbers
>   *-----------------
> @@ -352,7 +368,7 @@ tsk .req    x28             // current thread_info
>         ldr_l   x1, handle_arch_irq
>         mov     x0, sp
>         irq_stack_entry
> -       blr     x1
> +       blr_e   x1
>         irq_stack_exit
>         .endm
> 
> @@ -381,7 +397,7 @@ __bad_stack:
>         mov     x0, sp
> 
>         /* Time to die */
> -       bl      handle_bad_stack
> +       bl_e    handle_bad_stack
>         nop                             // ensure lr points somewhere sane
>  ENDPROC(__bad_stack)
>  #endif /* CONFIG_VMAP_STACK */
> @@ -429,7 +445,7 @@ END(vectors)
>         mov     x0, sp
>         mov     x1, #\reason
>         mrs     x2, esr_el1
> -       b       bad_mode
> +       b_e     bad_mode
>         .endm
> 
>  el0_sync_invalid:
> """
> 
> etc to explicitly record the places where we call out of the entry
> code with a pt_regs struct on the top of the stack.
> 
> Then, we can use
> 
> static bool is_entry_call(unsigned long pc)
> {
>     extern s32 __entrycalls_start[], __entrycalls_end[];
>     s32 *p;
> 
>     for (p = __entrycalls_start; p < __entrycalls_end; p++)
>         if ((unsigned long)p + *p == pc - 4)
>             return true;
>     return false;
> }
> 
> to identify values of frame.pc that coincide with such calls.

Unfortunately, I believe that fails for case (3), where entry code is
interrupted before calling C code. e.g. if an interrupt was taken during
data abort handling:

	kernel_entry 1
	...
el1_da:
	...
	enable_irq
	< interrupted here >
	...
	bl      do_mem_abort

... as we'd dump the regs for the interrupt, but not those for the data
abort.

Thanks,
Mark.



More information about the linux-arm-kernel mailing list