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

Ard Biesheuvel ard.biesheuvel at linaro.org
Tue Jul 25 03:07:40 PDT 2017


On 25 July 2017 at 10:53, Mark Rutland <mark.rutland at arm.com> wrote:
> 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.
>

True.



More information about the linux-arm-kernel mailing list