[PATCH v5 8/8] unwind: arm64: Use sframe to unwind interrupt frames
Jens Remus
jremus at linux.ibm.com
Tue May 5 08:52:06 PDT 2026
On 5/5/2026 12:29 PM, Mark Rutland wrote:
> On Mon, May 04, 2026 at 10:47:26AM +0200, Jens Remus wrote:
>> On 5/1/2026 6:46 PM, Mark Rutland wrote:
>>> Thanks for putting this together. I think this is looking pretty good.
>>> However, there are some things that aren't quite right and need some
>>> work, which I've commented on below.
>>
>>> (2) To make unwinding generally possible, we'll need to annotate some
>>> assembly functions as unwindable. We'll need to do that for string
>>> routines under lib/, and probably some crypto code, but we don't
>>> need to do that for most code in head.S, entry.S, etc.
>>>
>>> The vast majority of relevant assembly functions are leaf functions
>>> (where the return address is never moved out of the LR), so we can
>>> probably get away with a simple annotation for those that avoids the
>>> need for open-coded CFI directives everywhere.
>>
>> Wrapping them in .cfi_startproc ... .cfi_endproc should do. For instance
>> by extending SYM_FUNC_START() and SYM_FUNC_END() or introducing flavors
>> that do. Or where you thinking of something else?
>
> I was expecting we'd do something like that, either with distinct
> versions, or some entirely separate annotation.
>
> We can't override SYM_FUNC_START() or SYM_FUNC_END() since those are
> also used for non-leaf functions. The bulk of the work is going to be
> making sure we only annotate leaf functions specifically, which will
> require some human analysis.
Makes sense.
>>> On Tue, Apr 28, 2026 at 06:36:43PM +0000, Dylan Hatch wrote:
>>>> diff --git a/arch/arm64/include/asm/stacktrace/common.h b/arch/arm64/include/asm/stacktrace/common.h
>>
>>>> @@ -21,6 +21,8 @@ struct stack_info {
>>>> *
>>>> * @fp: The fp value in the frame record (or the real fp)
>>>> * @pc: The lr value in the frame record (or the real lr)
>>>> + * @sp: The sp value at the call site of the current function.
>>>> + * @unreliable: Stacktrace is unreliable.
>>>> *
>>>> * @stack: The stack currently being unwound.
>>>> * @stacks: An array of stacks which can be unwound.
>>>> @@ -29,7 +31,11 @@ struct stack_info {
>>>> struct unwind_state {
>>>> unsigned long fp;
>>>> unsigned long pc;
>>>> +#ifdef CONFIG_HAVE_UNWIND_KERNEL_SFRAME
>>>> + unsigned long sp;
>>>> +#endif
>>>
>>> As this is only used by the kernel unwinder (and not the hyp unwinder),
>>> this should live in struct kunwind_state in stacktrace.c.
>>>
>>> That said, for unwinding across exception boundaries we should not need
>>> this, as the SP value will be in the pt_regs. If we only use SFrame for
>>> the exception boundary case, we can remove this entirely. I would
>>> strongly prefer that we do that.
>
>>>> + /* Get the Canonical Frame Address (CFA) */
>>>> + switch (frame.cfa.rule) {
>>>> + case UNWIND_CFA_RULE_SP_OFFSET:
>>>> + cfa = state->common.sp;
>>
>> IIUC you suggest this to be changed as follows?
>>
>> cfa = regs->regs[31];
>
> I was suggesting:
>
> cfa = regs->sp;
>
> Note: arm64's struct pt_regs has:
>
> union {
> struct user_pt_regs user_regs;
> struct {
> u64 regs[31];
> u64 sp;
> u64 pc;
> u64 pstate;
> };
> };
>
> ... so regs->regs[31] would be an out-of-bounds array access.
Aww, my bad! Of course!
>
> [...]
>
>>>> + case UNWIND_CFA_RULE_REG_OFFSET:
>>>> + case UNWIND_CFA_RULE_REG_OFFSET_DEREF:
>>>> + /* regs only available in topmost/interrupt frame */
>>>> + if (!regs || frame.cfa.regnum > 30)
>>>> + return -EINVAL;
>>>> + cfa = regs->regs[frame.cfa.regnum];
>>>> + break;
>>>
>>> Do we ever expect to see UNWIND_CFA_RULE_REG_OFFSET or
>>> UNWIND_CFA_RULE_REG_OFFSET_DEREF in practice for kernel code?
>>
>> No. Those can only occur with SFrame V3 flexible FDE, which are
>> currently not generated by GNU assembler for arm64/aarch64, and thus
>> could be omitted in the arm64-specific kernel sframe unwinder:
>>
>> https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gas/config/tc-aarch64.h;hb=binutils-2_46#l342
>
> Ok.
>
> Do we know whether there are currently cases on aarch64 that cannot be
> encoded in SFrame (without flexible FDE), or whether SFrame without
> flexible FDE is sufficient for arm64 as-is? ... or do we have
> counter-examples today?
Not that I am aware of. IIUC this is why Indu, the SFrame maintainer,
did not enable SFrame V3 flexible FDE for arm64/aarch64 in the GNU
assembler.
> Looking at:
>
> https://sourceware.org/binutils/docs/sframe-spec.html#Flexible-FDE-Type-Interpretation-1
>
> For arm64 I'm not sure whether we'd encounter the DRAP or stack
> realignment cases within the kernel (perhaps with SVE?), nor whether the
> Register-based RA/FP Locations cases would apply if we assume that we
> continue to use frame records.
@Indu: Can you provide more insight?
> [... ]
>
>> I must admit that while reviewing I thought it would be future-proof to
>> have support for rules that can only be represented with SFrame V3
>> flexible FDE, even if they are currently not used on arm64. Ideally
>> kunwind_next_sframe() could be made common, if another architecture
>> would implement kernel unwinding using sframe.
>
> While I understand that principle, I think that for now it would be
> better to keep this arch-specific and minimal:
>
> * We have arch-specific concerns (e.g. the FRAME_META_TYPE_FINAL
> frames), and factoring that into generic code is going to be painful
> to adapt (which we're likely to need to do in the near future), and to
> maintain going forwards. Keeping that arch-specific for now will make
> it easier/quicker to get to a stable state.
>
> * Code which isn't used is liable to be wrong or made wrong by accident.
> For example, with all the SP cases I mentioned in my initial reply.
>
> We can certainly look at making that more generic in future, but for now
> I'd prefer to omit the code that cannot be used (and have some sort of
> build or boot/module-load time check that SFrame only has elements that
> we expect), and make sure that we thoroughly test the cases that do
> exist in practice.
Makes sense.
> Do we expect SFrame V3 flexible FDE to be generated by toolchains in the
> near future?
There is work in progress to implement SFrame V3 generation in LLVM.
Their implementation could be less restrictive regarding to which
SFrame V3 feature to enable on arm64/aarch64. But even then for
SFrame V3 flexible FDE to be generated there must be DWARF CFI patterns
that can only be represented using those. So if those do not exist
(see your previous question above), then they should not be generated.
@Indu: What are your thoughts on this?
> [...]
>
>>>> + /* CFA alignment 8 bytes */
>>>> + if (cfa & 0x7)
>>>> + return -EINVAL;
>>>
>>> If the CFA is the SP upon entry to the function, then per AAPCS64 rules
>>> it should be aligned to 16 bytes. Otherwise, where has this 8 byte
>>> alignment requirement come from? Does SFrame mandate that?
>>
>> That originates from the common unwind user logic (see
>> kernel/unwind/user.c, unwind_user_next_common()), which currently
>> assumes 8-byte/4-byte SP alignment for all 64-bit/32-bit architectures.
>>
>> So checking for 16-byte alignment here would make sense.
>
> Just to confirm, am I correct to understand that the SFrame definition
> of CFA is intended to be the same as the DWARF definition of CFA, and so
Correct.
> for arm64 the CFA is the SP when the function is called?
Correct.
>
> That's the case for DWARF on arm64:
>
> https://github.com/ARM-software/abi-aa/releases/download/2025Q4/aadwarf64.pdf
> https://github.com/ARM-software/abi-aa/blob/daa7a94ca55973736c0e434a67a6e4bbcd35d7fa/aadwarf64/aadwarf64.rst
>
> | The CFA is the value of the stack pointer (sp) at the call site in the
> | previous frame.
>
> I couldn't find an explciit statement to that effect in:
>
> https://sourceware.org/binutils/docs/sframe-spec.html
>
> ... but I guess that is implied, given the other bits inherited from
> DWARF.
I assume so.
>
> I see that the documented behaviour for CFA on AMD64 and s390x are
> consistent with their DWARF behaviour.
Yes.
>
>>>> +
>>>> + /* Get the Return Address (RA) */
>>>> + switch (frame.ra.rule) {
>>>> + case UNWIND_RULE_RETAIN:
>>>> + /* regs only available in topmost/interrupt frame */
>>>> + if (!regs)
>>>> + return -EINVAL;
>>>> + ra = regs->regs[30];
>>>> + source = KUNWIND_SOURCE_REGS_LR;
>>>> + break;
>>>> + /* UNWIND_USER_RULE_CFA_OFFSET not implemented on purpose */
>>
>> Nit: s/UNWIND_USER_RULE_CFA_OFFSET/UNWIND_RULE_CFA_OFFSET/
>>
>>>
>>> It would be better for the comment to say *why* that's not implemented.
>>>
>>> I assume that's because UNWIND_USER_RULE_CFA_OFFSET would mean that the return
>>> address is a stack address, and that's obviously not legitimate.
>>
>> That and SFrame V3 currently cannot represent FP/RA as CFA + offset
>> (i.e. UNWIND_RULE_CFA_OFFSET; .cfi_val_offset FP/RA).
>>
>> The comment originates from the common unwind user logic (see
>> kernel/unwind/user.c). I am open to improve that. What about:
>>
>> /*
>> * UNWIND_RULE_CFA_OFFSET not implemented on purpose, as a stack
>> * address cannot be a legitimate return address value. It is
>> * also not used (e.g. not represented in sframe).
>> */
>
> I'd go with something simpler, e.g.
>
> /*
> * UNWIND_RULE_CFA_OFFSET doesn't make sense for RA.
> * The return address cannot legitimately be a stack addres.
> */
Thanks! I have updated the comments accordingly in my latest unwind
user sframe patch series sent today:
[PATCH v14 14/19] unwind_user: Flexible FP/RA recovery rules
https://lore.kernel.org/all/20260505121718.3572346-15-jremus@linux.ibm.com/
[...]
>>>> + default:
>>>> + WARN_ON_ONCE(1);
>>>> + return -EINVAL;
>>>> + }
>>>> +
>>>> + /* Get the Frame Pointer (FP) */
>>>> + switch (frame.fp.rule) {
>>>> + case UNWIND_RULE_RETAIN:
>>>> + fp = state->common.fp;
>>>> + break;
>>>> + /* UNWIND_USER_RULE_CFA_OFFSET not implemented on purpose */
>>>
>>> As for RA, the comment should explain why that's not implemented.
>>
>> I am open to improve the comment in the the common unwind user logic.
>> What about:
>>
>> /*
>> * UNWIND_RULE_CFA_OFFSET not implemented on purpose, as it is
>> * not used (e.g. not represented in sframe).
>> */
>
> For me, this wording raises more questions, e.g.
>
> * Does 'not used' mean that toolchains don't use that, or that the spec
> doesn't permit that?
unwind user currently only supports frame pointer, with SFrame to be
hopefully added soon. Out of these only SFrame requires ("uses") these
elaborated rules.
>
> * Does 'not represented' mean that this is not represntable, or that
> toolchains currently don't generate SFrame with the appropriate
> elements.
In DWARF CFI it is representable using .cfi_val_offset <FP>, <offset>.
But SFrame V3 currently cannot represent this:
"Note that, using a value of 0 as padding data word, does mean that
currently, e.g., for RA [JR: FP likewise], the rule RA = CFA + 0 cannot
be encoded. NB: RA = CFA + 0 is distinct from RA = *(CFA + 0)."
https://sourceware.org/binutils/docs/sframe-spec.html#Flexible-FDE-Type-Interpretation-1
>
> IIUC you're saying that this *is* representable with flexible FDE, but
> current toolchains don't generate that.
Thanks for the feedback! I changed it as follows to clarify:
/*
* UNWIND_USER_RULE_CFA_OFFSET is currently not used for FP
* (e.g. SFrame cannot represent this rule).
*/
Regards,
Jens
--
Jens Remus
Linux on Z Development (D3303)
jremus at de.ibm.com / jremus at linux.ibm.com
IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Ehningen; Registergericht: Amtsgericht Stuttgart, HRB 243294
IBM Data Privacy Statement: https://www.ibm.com/privacy/
More information about the linux-arm-kernel
mailing list