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

            Bug ID: 120714
           Summary: RISC-V: incorrect frame pointer CFA address for
                    stack-clash protection loops
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: middle-end
          Assignee: unassigned at gcc dot gnu.org
          Reporter: alexey.merzlyakov at samsung dot com
  Target Milestone: ---

Created attachment 61666
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=61666&action=edit
repro-testcase for running with GDB

The following test-case, when compiling for rv64gc with -O1 -g2
-fstack-clash-protection produces incorrect DWARF information:

  #include <stdio.h>

  #define MAX 4000

  void goo ()
  {
    printf("goo()\n");
  }

  int foo (int a)
  {
    long long A[MAX];
    for (int i = 0; i < MAX; i++)
      A[i] = i;

    goo ();

    return A[a % MAX];
  }

  int main () {
    printf("ret = %i\n", foo(20));
    return 0;
  }

When running produced binary under GDB or backtracing with libunwind, it can't
get the right frame after foo():

  ~$ gdb stack7.sc.x
  ...
  (gdb) b goo
  Breakpoint 1 at 0x10584: file stack7.c, line 6.
  (gdb) r
  ...
  (gdb) bt
  #0  goo () at stack7.c:6
  #1  0x00000000000105da in foo (a=<optimized out>) at stack7.c:16
  #2  0x0000000000000f9f in ?? ()   <- should be main()

This appears due to incorrect .cfi_def_cfa_offset stack pointer addresses after
stack-clash checking loop:

  foo:
          addi    sp,sp,-16
          .cfi_def_cfa_offset 16
          sd      ra,8(sp)
          sd      s0,0(sp)
          li      t1,28672
          sub     t1,sp,t1
          .cfi_def_cfa 5, 28672
          .cfi_offset 1, -8
          .cfi_offset 8, -16
          li      t0,4096
  .L6:
          sub     sp,sp,t0
          sd      zero,1024(sp)
          bne     sp,t1,.L6
          .cfi_def_cfa_register 2
          li      t0,4096
          addi    t0,t0,-768
          sub     sp,sp,t0
          .cfi_def_cfa_offset 32000   <- wrong value

SP at this point is shifted on 32016 value instead of 32000, which confirms by
assembler made w/o stack-clash protection option:

  foo:
        addi    sp,sp,-16
        .cfi_def_cfa_offset 16
        sd      ra,8(sp)
        sd      s0,0(sp)
        li      t0,-32768
        addi    t0,t0,768
        add     sp,sp,t0
        .cfi_def_cfa_offset 32016   <- correct

Uncounted difference in 16 bytes is seem to have in
riscv_allocate_and_probe_stack_space() function. It uses the "remaining_size"
as basis for calculation the stack shift value, when adding reg note with
REG_CFA_DEF_CFA. However, the remaining_size represents stack frame size that
might be previously reduced in riscv_expand_prologue().

Real stack pointer shift value might be corrected with
"cfa_offset = frame.total_size - remaining_size" value when adding
REG_CFA_DEF_CFA notes for stack clash prologue handling in
riscv_allocate_and_probe_stack_space().

Reply via email to