This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] Incorrect code for __builtin_frame_address(0)
- From: "Maciej W. Rozycki" <macro at linux-mips dot org>
- To: gcc-patches at gcc dot gnu dot org
- Date: Fri, 3 Jun 2005 13:51:14 +0100 (BST)
- Subject: [PATCH] Incorrect code for __builtin_frame_address(0)
Hello,
Tracking down an obscure segmentation fault in glibc, I've discovered we
get the return value of __builtin_frame_address(0) wrong if
-fomit-frame-pointer is used. It happens even for platforms for which the
flag is the default when optimizing!
Example code:
$ cat getfp.c
void *getfp(void)
{
return __builtin_frame_address(0);
}
$ mipsel-linux-gcc -O2 -fomit-frame-pointer -c getfp.c -o
mipsel-linux-getfp.o
$ mipsel-linux-objdump -d mipsel-linux-getfp.o
mipsel-linux-getfp.o: file format elf32-tradlittlemips
Disassembly of section .text:
00000000 <getfp>:
0: 27bdfff8 addiu sp,sp,-8
4: afbe0000 sw s8,0(sp)
8: 03c01021 move v0,s8
c: 8fbe0000 lw s8,0(sp)
10: 03e00008 jr ra
14: 27bd0008 addiu sp,sp,8
...
$ i386-linux-gcc -O2 -fomit-frame-pointer -c getfp.c -o i386-linux-getfp.o
$ i386-linux-objdump -d i386-linux-getfp.o
i386-linux-getfp.o: file format elf32-i386
Disassembly of section .text:
00000000 <getfp>:
0: 55 push %ebp
1: 89 e8 mov %ebp,%eax
3: 5d pop %ebp
4: c3 ret
$ alpha-linux-gcc -O2 -fomit-frame-pointer -c getfp.c -o
alpha-linux-getfp.o
$ alpha-linux-objdump -d alpha-linux-getfp.o
alpha-linux-getfp.o: file format elf64-alpha
Disassembly of section .text:
0000000000000000 <getfp>:
0: f0 ff de 23 lda sp,-16(sp)
4: 00 04 ef 47 mov fp,v0
8: 00 00 5e b7 stq ra,0(sp)
c: 08 00 fe b5 stq fp,8(sp)
10: 00 00 5e a7 ldq ra,0(sp)
14: 08 00 fe a5 ldq fp,8(sp)
18: 10 00 de 23 lda sp,16(sp)
1c: 01 80 fa 6b ret
Obviously wrong...
I've narrowed it down to expand_builtin_return_addr() referring to
"hard_frame_pointer_rtx", which may not hold the actual frame pointer if
-fomit-frame-pointer is used. An obvious fix is below, prepared and
tested with GCC 4.0.0 for platforms I have tools immediately available
for, that is: alpha-linux, i386-linux and mipsel-linux (I've skipped other
MIPS variants as obviously the same), by using the code as quoted above
and proof-reading the output:
$ mipsel-linux-objdump -d mipsel-linux-getfp.o
mipsel-linux-getfp.o: file format elf32-tradlittlemips
Disassembly of section .text:
00000000 <getfp>:
0: 03e00008 jr ra
4: 03a01021 move v0,sp
...
$ i386-linux-objdump -d i386-linux-getfp.o
i386-linux-getfp-1.o: file format elf32-i386
Disassembly of section .text:
00000000 <getfp>:
0: 89 e0 mov %esp,%eax
2: c3 ret
$ alpha-linux-objdump -d alpha-linux-getfp.o
alpha-linux-getfp-1.o: file format elf64-alpha
Disassembly of section .text:
0000000000000000 <getfp>:
0: 00 04 fe 47 mov sp,v0
4: 01 80 fa 6b ret
8: 1f 04 ff 47 nop
c: 00 00 fe 2f unop
I've verified code is still OK if -fno-omit-frame-pointer is in effect.
I've done a quick check with GCC 3.2.3, 3.3.5 and 3.4.4 and they appear
to suffer from this bug as well; the lack of differences in the affected
functions makes me believe the bug is present in the HEAD, too.
2005-06-03 Maciej W. Rozycki <macro@linux-mips.org>
* config/alpha/alpha.h (INITIAL_FRAME_ADDRESS_RTX): New macro.
* config/i386/i386.h (INITIAL_FRAME_ADDRESS_RTX): Likewise.
* config/mips/mips.h (INITIAL_FRAME_ADDRESS_RTX): Likewise.
Please apply. Though for the HEAD, using "frame_pointer_rtx" as the
default in place of "hard_frame_pointer_rtx" might be a reasonable
alternative. Other platforms that implement -fomit-frame-pointer may need
a similar fix -- currently only s390.h defines INITIAL_FRAME_ADDRESS_RTX.
Maciej
gcc-4.0.0-frame_address.patch
diff -up --recursive --new-file gcc-4.0.0.macro/gcc/config/alpha/alpha.h gcc-4.0.0/gcc/config/alpha/alpha.h
--- gcc-4.0.0.macro/gcc/config/alpha/alpha.h 2005-04-17 06:39:08.000000000 +0000
+++ gcc-4.0.0/gcc/config/alpha/alpha.h 2005-06-03 00:25:43.000000000 +0000
@@ -936,6 +936,10 @@ extern int alpha_memory_latency;
#define FIRST_PARM_OFFSET(FNDECL) 0
+/* Defining this macro makes __builtin_frame_address(0) work
+ with -fomit-frame-pointer. */
+#define INITIAL_FRAME_ADDRESS_RTX frame_pointer_rtx
+
/* Definitions for register eliminations.
We have two registers that can be eliminated on the Alpha. First, the
diff -up --recursive --new-file gcc-4.0.0.macro/gcc/config/i386/i386.h gcc-4.0.0/gcc/config/i386/i386.h
--- gcc-4.0.0.macro/gcc/config/i386/i386.h 2005-04-05 22:59:21.000000000 +0000
+++ gcc-4.0.0/gcc/config/i386/i386.h 2005-06-03 00:26:10.000000000 +0000
@@ -1655,6 +1655,10 @@ enum reg_class
/* Offset of first parameter from the argument pointer register value. */
#define FIRST_PARM_OFFSET(FNDECL) 0
+/* Defining this macro makes __builtin_frame_address(0) work
+ with -fomit-frame-pointer. */
+#define INITIAL_FRAME_ADDRESS_RTX frame_pointer_rtx
+
/* Define this macro if functions should assume that stack space has been
allocated for arguments even when their values are passed in registers.
diff -up --recursive --new-file gcc-4.0.0.macro/gcc/config/mips/mips.h gcc-4.0.0/gcc/config/mips/mips.h
--- gcc-4.0.0.macro/gcc/config/mips/mips.h 2005-04-15 07:00:18.000000000 +0000
+++ gcc-4.0.0/gcc/config/mips/mips.h 2005-06-03 00:26:34.000000000 +0000
@@ -2149,6 +2149,10 @@ extern enum reg_class mips_char_to_class
/* The argument pointer always points to the first argument. */
#define FIRST_PARM_OFFSET(FNDECL) 0
+/* Defining this macro makes __builtin_frame_address(0) work
+ with -fomit-frame-pointer. */
+#define INITIAL_FRAME_ADDRESS_RTX frame_pointer_rtx
+
/* o32 and o64 reserve stack space for all argument registers. */
#define REG_PARM_STACK_SPACE(FNDECL) \
(TARGET_OLDABI \