Skip to content

[Linux] SIGILL in eh_parse_frame_hdr on .eh_frame_hdr with omitted fde_count/table encoding (Zig 0.17 static binary) #1006

Open

Description

@iacore

Summary

On Linux, running a target whose .eh_frame_hdr uses the "omitted count / omitted table" encoding makes raddbg Trap() inside eh_parse_frame_hdr (__builtin_trap → SIGILL). The debugger dies immediately on Run.

Environment

  • raddbg 0.9.30 ALPHA, built from 98467d0 (Oct 7 2026).
  • Checked origin/master (b6d8c3f): src/eh_frame/ is untouched, so this is still present on current master.
  • Linux x86_64, Mesa software GL under Xvfb (headless).
  • Target: 64-bit, statically linked ELF produced by zig 0.17.0.

Reproduction

  1. raddbg ./debuggee
  2. Press Run (▶, or F5).

→ immediate crash, window disappears.

Crash output

A fatal signal was received: Illegal instruction (4). The process is terminating.
Create a new issue with this report at https://github.com/EpicGames/raddebugger/issues.

Callstack:
1. [0x000055781893f622] raddbg, lnx_signal_handler linux_base.c:1837:21
2. [0x00007f762f43ee50] libc.so.6
3. [0x00005578189a1e28] raddbg, eh_parse_frame_hdr eh_frame.c:0:0
4. [0x00005578189a0835] raddbg, d_ctrl_thread__next_dmn_event dbg_engine_ctrl.c:3234:35
5. [0x0000557818979152] raddbg, d_ctrl_thread__run dbg_engine_ctrl.c:4990:26
6. [0x0000557818974313] raddbg, d_ctrl_thread__entry_point dbg_engine_ctrl.c:0:46

(dbg_engine_ctrl.c:3234 is the eh_parse_frame_hdr(...) call; the crash is inside the callee.)

Root cause

readelf -x .eh_frame_hdr debuggee — the section is 8 bytes:

01 1b ff ff 48 29 fe ff
  • 01 version
  • 1b eh_frame_ptr_enc = pcrel|sdata4
  • ff fde_count_enc = DW_EH_PE_omit
  • ff table_enc = DW_EH_PE_omit
  • 48 29 fe ff eh_frame_ptr (sdata4, pcrel)

i.e. no fde_count field and no binary search table.

In src/eh_frame/eh_frame.c, eh_parse_frame_hdr():

switch (header.table_enc & EH_PtrEnc_TypeMask) {   // 0xff & 0x0f == 0x0f
  case EH_PtrEnc_Ptr:     { ... } break;
  ...
  default: { InvalidPath; } break;
}

0xff & EH_PtrEnc_TypeMask == 0x0f matches no case, so it hits default: InvalidPath; InvalidPath is AssertAlways(!"Invalid Path!"), and AssertAlways is Trap() = __builtin_trap() → SIGILL.

Note eh_read_ptr() already treats EH_PtrEnc_Omit as "field absent", so the parser tolerates omit for the two pointer reads but not in the field_byte_size switch. The following AssertAlways(header.table.size == header.entry_byte_size * header.fde_count) would also be wrong whenever the count/table are omitted.

Distinguishing evidence

Same harness (raddbg ./<exe> + Run), three targets:

target .eh_frame_hdr result
gcc -g -static absent no crash
gcc -g -static -Wl,--eh-frame-hdr full header 01 1b 03 3b ... + table no crash
zig 0.17.0 static 8-byte omit form above crash

So it is not "static executables" in general (cf. #902); it is specifically this .eh_frame_hdr encoding.

Suggested fix

Handle DW_EH_PE_omit (0xff) for table_enc / fde_count_enc as "no binary search table" — field_byte_size = 0, entry_byte_size = 0, fde_count = 0, empty table — and relax/guard the trailing AssertAlways, rather than falling into InvalidPath.

Activity

  1. added
    BugA report of unintended or broken behavior.
    DebuggerPertains to the debugger.
    on Oct 8, 2026
  2. gurulove80594-cyber commented on Oct 9, 2026

    @gurulove80594-cyber

    Xxx full sarvice

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA report of unintended or broken behavior.DebuggerPertains to the debugger.Linux

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions