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().