]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commit
gdb/dwarf: fix internal error when FDEs do not describe the CFA
authorSimon Marchi <simon.marchi@polymtl.ca>
Wed, 18 Mar 2026 20:27:23 +0000 (16:27 -0400)
committerSimon Marchi <simon.marchi@polymtl.ca>
Sat, 11 Apr 2026 02:46:37 +0000 (22:46 -0400)
commita910478f65a596817fde3ad4ee727e41ec8e0513
treeddd7231106538ab7818ac65f7f7fce559a321373
parent3c60c45271cb8fcecc4192074e0b6c71654d5f05
gdb/dwarf: fix internal error when FDEs do not describe the CFA

New in v2: change how undefined_retaddr is set, to avoid regressions on
AArch64 (among possibly others).

This patch fixes an internal error problem that happens when a frame
description entry does not define the Canonical Frame Address (CFA).
This problem was initially reported downstream as a ROCgdb issue (see
Bug trailer below), but I wrote a reproducer that uses the .debug_frame
functionality added to the DWARF assembler in the previous patch.

The error is:

    /home/smarchi/src/binutils-gdb/gdb/dwarf2/frame.c:1046: internal-error: Unknown CFA rule.

The original bug was encountered while debugging a GPU kernel written
with Triton [1].  From what I understand, the generated kernel does not
really use a stack, so the .debug_frame contents generated is quite
bare:

    $ readelf --debug-dump=frames k
    Contents of the .debug_frame section:

    00000000 000000000000000c ffffffff CIE
      Version:               4
      Augmentation:          ""
      Pointer Size:          8
      Segment Size:          0
      Code alignment factor: 4
      Data alignment factor: 4
      Return address column: 16

      DW_CFA_nop

    00000010 0000000000000014 00000000 FDE cie=00000000 pc=0000000000001600..0000000000001704

For those who don't speak fluent .debug_frame, what we see here is a
Frame Description Entry (FDE) that doesn't define any register rule,
referring to a Common Information Entry (CIE) that also doesn't define
any initial register rule.  This is equivalent to having no unwind
information at all.  One question is: why generate these at all?  I
suppose that this is an edge case, that the compiler is written in a way
that that presumes there will always be some unwind info.  That there is
no "if unwind info is empty, skip emitting the FDE" check.  Anyway, the
important thing for us is that these can be found in the wild, so GDB
shouldn't crash.

The fix consists of handling CFA_UNSET in the dwarf2_frame_cache switch.
CFA_UNSET is the initial state when we start interpreting a CFA program,
meaning that we don't know yet how the CFA is defined.  In our case, it
remains unset after interpreting the CFA program.

With just the fix above, we get:

    (gdb) bt
    #0  0x000055555555511d in main ()
    Backtrace stopped: previous frame identical to this frame (corrupt stack?)

Which is good (better than crashing), but it would be good to avoid the
error.  To do so, set the undefined_retaddr flag to true.  This has
two effects:

 - dwarf2_frame_this_id won't try to build a frame id from the CFA
   (which is good, we don't have a CFA)
 - dwarf2_frame_unwind_stop_reason will return UNWIND_OUTERMOST, which
   is the most accurate thing we can return here (there is no outer
   frame)

The result is the expected:

    (gdb) bt
    #0  0x000055555555511d in main ()

My initial implementation changed this condition:

  if (fs.retaddr_column < fs.regs.reg.size ()
      && fs.regs.reg[fs.retaddr_column].how == DWARF2_FRAME_REG_UNDEFINED)
    cache->undefined_retaddr = true;

such that we would enter it if

    fs.retaddr_column <+ fs.regs.reg.size ()

However, this broke the unwinding on AArch64 (and possibly others).

Add a test case written using the DWARF assembler that reproduces the
issue.

[1] https://triton-lang.org/

Change-Id: I67c717ff03a41c0630a73ce9549d88ff363e8cea
Bug: https://github.com/ROCm/ROCgdb/issues/47
Approved-By: Tom Tromey <tom@tromey.com>
gdb/dwarf2/frame.c
gdb/testsuite/gdb.dwarf2/debug-frame-no-cfa.exp [new file with mode: 0644]