Patch: Linux PPC64 libffi -vs- long double return
Tom Tromey
tromey@redhat.com
Mon Sep 26 16:11:00 GMT 2005
This patch fixes a bug found when running libgcj on a Linux PPC64
machine.
The problem is that the return value processing for 'double' would
actually write 2 double values, potentially smashing the stack. The
fix is to add some special handling for 'long double', as is done in
the Darwin port.
I've also included a patch to a test case which shows this failure.
Tested on Linux PPC64.
Ok?
Tom
:ADDPATCH libffi:
Index: ChangeLog
from Tom Tromey <tromey@redhat.com>
* testsuite/libffi.call/float1.c (value_type): New typedef.
(CANARY): New define.
(main): Check for result buffer overflow.
* src/powerpc/linux64.S: Handle linux64 long double returns.
* src/powerpc/ffi.c (FLAG_RETURNS_128BITS): New constant.
(ffi_prep_cif_machdep): Handle linux64 long double returns.
Index: src/powerpc/ffi.c
===================================================================
RCS file: /cvs/gcc/gcc/libffi/src/powerpc/ffi.c,v
retrieving revision 1.15
diff -u -r1.15 ffi.c
--- src/powerpc/ffi.c 25 Aug 2005 00:54:54 -0000 1.15
+++ src/powerpc/ffi.c 26 Sep 2005 16:08:18 -0000
@@ -39,6 +39,7 @@
FLAG_RETURNS_NOTHING = 1 << (31-30), /* These go in cr7 */
FLAG_RETURNS_FP = 1 << (31-29),
FLAG_RETURNS_64BITS = 1 << (31-28),
+ FLAG_RETURNS_128BITS = 1 << (31-27),
FLAG_ARG_NEEDS_COPY = 1 << (31- 7),
FLAG_FP_ARGUMENTS = 1 << (31- 6), /* cr1.eq; specified by ABI */
@@ -543,6 +544,12 @@
/* else fall through. */
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
case FFI_TYPE_LONGDOUBLE:
+ if (type == FFI_TYPE_LONGDOUBLE && cif->abi == FFI_LINUX64)
+ {
+ flags |= FLAG_RETURNS_128BITS;
+ flags |= FLAG_RETURNS_FP;
+ break;
+ }
#endif
intarg_count++;
flags |= FLAG_RETVAL_REFERENCE;
Index: src/powerpc/linux64.S
===================================================================
RCS file: /cvs/gcc/gcc/libffi/src/powerpc/linux64.S,v
retrieving revision 1.7
diff -u -r1.7 linux64.S
--- src/powerpc/linux64.S 2 Sep 2004 21:07:21 -0000 1.7
+++ src/powerpc/linux64.S 26 Sep 2005 16:08:18 -0000
@@ -120,9 +120,13 @@
blr
.Lfp_return_value:
+ bt 27, .Lfd_return_value
bf 28, .Lfloat_return_value
stfd %f1, 0(%r30)
- stfd %f2, 8(%r30) /* It might be a long double */
+ b .Ldone_return_value
+.Lfd_return_value:
+ stfd %f1, 0(%r30)
+ stfd %f2, 8(%r30)
b .Ldone_return_value
.Lfloat_return_value:
stfs %f1, 0(%r30)
Index: testsuite/libffi.call/float1.c
===================================================================
RCS file: /cvs/gcc/gcc/libffi/testsuite/libffi.call/float1.c,v
retrieving revision 1.1
diff -u -r1.1 float1.c
--- testsuite/libffi.call/float1.c 4 Sep 2003 14:47:48 -0000 1.1
+++ testsuite/libffi.call/float1.c 26 Sep 2005 16:08:18 -0000
@@ -8,6 +8,14 @@
#include "ffitest.h"
#include "float.h"
+typedef union
+{
+ double d;
+ unsigned char c[sizeof (double)];
+} value_type;
+
+#define CANARY 0xba
+
static double dblit(float f)
{
return f/3.0;
@@ -19,8 +27,8 @@
ffi_type *args[MAX_ARGS];
void *values[MAX_ARGS];
float f;
- double d;
-
+ value_type result[2];
+ int i;
args[0] = &ffi_type_float;
values[0] = &f;
@@ -31,11 +39,19 @@
f = 3.14159;
- ffi_call(&cif, FFI_FN(dblit), &d, values);
+ /* Put a canary in the return array. This is a regression test for
+ a buffer overrun. */
+ memset(result[1].c, CANARY, sizeof (double));
+
+ ffi_call(&cif, FFI_FN(dblit), &result[0].d, values);
/* These are not always the same!! Check for a reasonable delta */
- CHECK(d - dblit(f) < DBL_EPSILON);
+ CHECK(result[0].d - dblit(f) < DBL_EPSILON);
+
+ /* Check the canary. */
+ for (i = 0; i < sizeof (double); ++i)
+ CHECK(result[1].c[i] == CANARY);
exit(0);
More information about the Java-patches
mailing list