Skip to content

Comments

StackTrace: AArch64: Add Frame pointer based stack trace support for GCC compiled PE binaries#1317

Open
vineelko wants to merge 1 commit intoOpenDevicePartnership:mainfrom
vineelko:users/vineelko/stacktrace_fp_chain_0213
Open

StackTrace: AArch64: Add Frame pointer based stack trace support for GCC compiled PE binaries#1317
vineelko wants to merge 1 commit intoOpenDevicePartnership:mainfrom
vineelko:users/vineelko/stacktrace_fp_chain_0213

Conversation

@vineelko
Copy link
Contributor

@vineelko vineelko commented Feb 13, 2026

Description

Dumps the stack trace by walking the FP/LR registers, without relying on unwind information. This is an AArch64-only fallback mechanism.

For GCC built PE images, .pdata/.xdata sections are not generated, causing stack trace dumping to fail. In this case, we attempt to dump the stack trace using an FP/LR register walk with the following limitations:

  1. Patina binaries produced with LLVM almost always do not save FP/LR register pairs as part of the function prologue for non-leaf functions, even though the ABI mandates it. https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#646the-frame-pointer

  2. Forcing this with the -C force-frame-pointers=yes compiler flag can produce strange results. In some cases, instead of saving fp/lr using stp x29, x30, [sp, #16]!, it saves lr/fp using stp x30, x29, [sp, #16]!, completely breaking the stack walk. https://godbolt.org/z/7s9fG9vWe

  3. Due to the above reasons, the stack walk cannot be reliably terminated.

The only reason this is being introduced is to identify the driver/app causing the exception. For example, a Shell app built with GCC that triggers an assertion can still produce a reasonable stack trace.

Dumping stack trace with PC: 000001007AB72ED0, SP: 0000010078885D50, FP: 0000010078885D50
    # Child-SP              Return Address         Call Site
    0 0000010078885D50      000001007AB12770       Shell+66ED0
    1 0000010078885E90      0000010007B98DCC       Shell+6770
    2 0000010078885FF0      0000010007B98E54       qemu_sbsa_dxe_core+18DCC
    3 0000010007FFF4C0      0000010007B98F48       qemu_sbsa_dxe_core+18E54
    4 0000010007FFF800      000001007AF54D08       qemu_sbsa_dxe_core+18F48
    5 0000010007FFFA90      0000010007BAC388       BdsDxe+8D08
    6 0000010007FFFF80      0000000010008878       qemu_sbsa_dxe_core+2C388 --.
                                                                              |
    0:000> u qemu_sbsa_dxe_core!patina_dxe_core::call_bds                     |
    00000000`1002c1b0 f81f0ff3 str x19,[sp,#-0x10]!                           |
    00000000`1002c1b4 f90007fe str lr,[sp,#8]     <---------------------------'
    00000000`1002c1b8 d10183ff sub sp,sp,#0x60

    The FP is not saved, so the return address in frame #6 is garbage.

Symbol to source file resolution(Resolving frame 2): Since some modules in the stack trace are built with GCC and do not generate PDB files, their symbols must be resolved manually as shown below.

$ addr2line -e Shell.debug -f -C 0x6770
UefiMain
~/repos/patina-qemu/MU_BASECORE/ShellPkg/Application/Shell/Shell.c:372

   371:  ASSERT (FALSE);
 > 372:  Status = gST->ConOut->ClearScreen (gST->ConOut);
   373:  if (EFI_ERROR (Status)) {
   374:     return (Status);
   375:  }

  • Impacts functionality?
  • Impacts security?
  • Breaking change?
  • Includes tests?
  • Includes documentation?

How This Was Tested

Validated on SBSA Q35.

Integration Instructions

The change is already hooked into the exception handler for AArch64.

@vineelko vineelko changed the title StackTrace: Add Frame pointer based stack trace support for GCC compiled PE binaries StackTrace: AArch64: Add Frame pointer based stack trace support for GCC compiled PE binaries Feb 13, 2026
@vineelko vineelko force-pushed the users/vineelko/stacktrace_fp_chain_0213 branch 2 times, most recently from bb77c2c to 85e1f94 Compare February 13, 2026 17:55
@codecov
Copy link

codecov bot commented Feb 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@vineelko vineelko force-pushed the users/vineelko/stacktrace_fp_chain_0213 branch from 85e1f94 to 5d03bad Compare February 13, 2026 18:14
…led PE binaries

Dumps the stack trace by walking the FP/LR registers, without relying on unwind
information. This is an AArch64-only fallback mechanism.

For GCC built PE images, .pdata/.xdata sections are not generated, causing stack
trace dumping to fail. In this case, we attempt to dump the stack trace using an
FP/LR register walk with the following limitations:

1. Patina binaries produced with LLVM almost always do not save FP/LR register
   pairs as part of the function prologue for non-leaf functions, even though
   the ABI mandates it.
   https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#646the-frame-pointer

2. Forcing this with the `-C force-frame-pointers=yes` compiler flag can produce
   strange results. In some cases, instead of saving fp/lr using `stp x29, x30,
   [sp, OpenDevicePartnership#16]!`, it saves lr/fp using `stp x30, x29, [sp, OpenDevicePartnership#16]!`, completely
   breaking the stack walk. https://godbolt.org/z/7s9fG9vWe

3. Due to the above reasons, the stack walk cannot be reliably terminated.

The only reason this is being introduced is to identify the driver/app causing
the exception. For example, a Shell app built with GCC that triggers an
assertion can still produce a reasonable stack trace.

```text
Dumping stack trace with PC: 000001007AB72ED0, SP: 0000010078885D50, FP: 0000010078885D50
    # Child-SP              Return Address         Call Site
    0 0000010078885D50      000001007AB12770       Shell+66ED0
    1 0000010078885E90      0000010007B98DCC       Shell+6770
    2 0000010078885FF0      0000010007B98E54       qemu_sbsa_dxe_core+18DCC
    3 0000010007FFF4C0      0000010007B98F48       qemu_sbsa_dxe_core+18E54
    4 0000010007FFF800      000001007AF54D08       qemu_sbsa_dxe_core+18F48
    5 0000010007FFFA90      0000010007BAC388       BdsDxe+8D08
    6 0000010007FFFF80      0000000010008878       qemu_sbsa_dxe_core+2C388 --.
                                                                              |
    0:000> u qemu_sbsa_dxe_core!patina_dxe_core::call_bds                     |
    00000000`1002c1b0 f81f0ff3 str x19,[sp,#-0x10]!                           |
    00000000`1002c1b4 f90007fe str lr,[sp,OpenDevicePartnership#8]     <---------------------------'
    00000000`1002c1b8 d10183ff sub sp,sp,#0x60

    The FP is not saved, so the return address in frame OpenDevicePartnership#6 is garbage.
```
Symbol to source file resolution(Resolving OpenDevicePartnership#2 frame):
Since some modules in the stack trace are built with GCC and do not generate PDB
files, their symbols must be resolved manually as shown below.
```text
$ addr2line -e Shell.debug -f -C 0x6770
UefiMain
~/repos/patina-qemu/MU_BASECORE/ShellPkg/Application/Shell/Shell.c:372
```

Signed-off-by: Vineel Kovvuri[MSFT] <vineelko@microsoft.com>
@vineelko vineelko force-pushed the users/vineelko/stacktrace_fp_chain_0213 branch from 5d03bad to 73899a0 Compare February 14, 2026 02:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants