https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128026

            Bug ID: 128026
           Summary: [DebugInfo] Invalid CFI register encoding in
                    output_cfa_loc and output_cfa_loc_raw
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: debug
          Assignee: unassigned at gcc dot gnu.org
          Reporter: wujielun402 at gmail dot com
  Target Milestone: ---

GCC emits invalid CFI for x86-64 APX registers r16 and r17 (DWARF register
numbers 130 and 131). The register operand of DW_CFA_expression requires
ULEB128 encoding, but GCC writes it as a single raw byte.

Reproducer source (repro.c):

        typedef unsigned long long u64;

        __attribute__ ((noinline, noclone)) u64
        save_extended_reg_with_realign (u64 input)
        {
          register unsigned long value __asm__ ("r16") = input;
          register unsigned long other __asm__ ("r17") = input + 1;
          volatile unsigned char aligned[64] __attribute__ ((aligned (64)));
          volatile unsigned char fixed[256];

          aligned[0] = (unsigned char) input;
          fixed[0] = aligned[0];
          __asm__ volatile ("" : "+r" (value), "+r" (other), "+m" (aligned)
                           : : "memory");
          return value + other + fixed[0];
        }

Compile to assembly:

        gcc -O2 -g -gdwarf-5 -mapx-features=egpr -mtune=generic \
          -mforce-drap -fcall-saved-r16 -fcall-saved-r17 -S repro.c -o repro.s

Compiler Explorer example: https://godbolt.org/z/1jG66b1nn

For example, GCC emits:

        .cfi_escape 0x10,0x82,0x2,0x76,0x70

.cfi_escape inserts raw CFI bytes. GCC intends the following fields:

        0x10        DW_CFA_expression
        0x82        Register 130 (r16), incorrectly encoded
        0x02        Length of the following DWARF expression: 2 bytes
        0x76,0x70   DW_OP_breg6 (rbp), with SLEB128 offset -16

The intended rule locates the saved r16 at memory address rbp - 16.
However, ULEB128 uses seven payload bits per byte, with the high bit
indicating continuation. Register 130 therefore requires 0x82,0x01.
A decoder instead reads the emitted 0x82,0x02 as register 258, then
0x76 as an expression length of 118 bytes, misparsing subsequent bytes.

The correct encoding is:

        .cfi_escape 0x10,0x82,0x1,0x2,0x76,0x70

Likewise, r17 requires 0x83,0x01 rather than just 0x83.

The example above exercises output_cfa_loc_raw. Source review identifies
at least two affected functions in gcc/dwarf2cfi.cc:

        output_cfa_loc uses dw2_asm_output_data (1, r, NULL).
        output_cfa_loc_raw uses fprintf (asm_out_file, "%#x,", r).

Both functions emit the register number without ULEB128 encoding. Both
need correction for DW_CFA_expression and DW_CFA_val_expression.

Reply via email to