summaryrefslogtreecommitdiff
path: root/pp_pack.c
diff options
context:
space:
mode:
authorTony Cook <tony@develop-help.com>2010-01-20 01:07:21 +1100
committerVincent Pit <vince@profvince.com>2010-01-19 18:26:56 +0100
commit275663fa076b91276d12d914795b38d0f554c9e4 (patch)
treebf1790a4e8d9c43a9d2d6ea248694d4beebff665 /pp_pack.c
parentbd12309b58376140a53f4d708bb325f48df1ff70 (diff)
downloadperl-275663fa076b91276d12d914795b38d0f554c9e4.tar.gz
Fix for #71506: work around possible gcc bug
When memcpy() is used on a long double pointer with gcc 4.4 in some cases it seems to treat it as a long double assignment, copying only the first 12 bytes. Use unions so the types we're copying into the pack output or from the unpack input are unsigned char[], to avoid the apparent bug.
Diffstat (limited to 'pp_pack.c')
-rw-r--r--pp_pack.c55
1 files changed, 35 insertions, 20 deletions
diff --git a/pp_pack.c b/pp_pack.c
index 03eaf7f886..0670548272 100644
--- a/pp_pack.c
+++ b/pp_pack.c
@@ -70,6 +70,18 @@ typedef struct tempsym {
(symptr)->previous = NULL; \
} STMT_END
+typedef union {
+ NV nv;
+ U8 bytes[sizeof(NV)];
+} NV_bytes;
+
+#if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
+typedef union {
+ long double ld;
+ U8 bytes[sizeof(long double)];
+} ld_bytes;
+#endif
+
#if PERL_VERSION >= 9
# define PERL_PACK_CAN_BYTEORDER
# define PERL_PACK_CAN_SHRIEKSIGN
@@ -146,17 +158,20 @@ typedef struct tempsym {
#define PUSH32(utf8, cur, p) PUSH_BYTES(utf8, cur, OFF32(p), SIZE32)
/* Only to be used inside a loop (see the break) */
-#define SHIFT_VAR(utf8, s, strend, var, datumtype) \
+#define SHIFT_BYTES(utf8, s, strend, buf, len, datumtype) \
STMT_START { \
if (utf8) { \
if (!uni_to_bytes(aTHX_ &s, strend, \
- (char *) &var, sizeof(var), datumtype)) break;\
+ (char *) (buf), len, datumtype)) break; \
} else { \
- Copy(s, (char *) &var, sizeof(var), char); \
- s += sizeof(var); \
+ Copy(s, (char *) (buf), len, char); \
+ s += len; \
} \
} STMT_END
+#define SHIFT_VAR(utf8, s, strend, var, datumtype) \
+ SHIFT_BYTES(utf8, s, strend, &(var), sizeof(var), datumtype)
+
#define PUSH_VAR(utf8, aptr, var) \
PUSH_BYTES(utf8, aptr, &(var), sizeof(var))
@@ -2085,25 +2100,25 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, const char *s, const char *strbeg, const c
break;
case 'F':
while (len-- > 0) {
- NV anv;
- SHIFT_VAR(utf8, s, strend, anv, datumtype);
- DO_BO_UNPACK_N(anv, NV);
+ NV_bytes anv;
+ SHIFT_BYTES(utf8, s, strend, anv.bytes, sizeof(anv.bytes), datumtype);
+ DO_BO_UNPACK_N(anv.nv, NV);
if (!checksum)
- mPUSHn(anv);
+ mPUSHn(anv.nv);
else
- cdouble += anv;
+ cdouble += anv.nv;
}
break;
#if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
case 'D':
while (len-- > 0) {
- long double aldouble;
- SHIFT_VAR(utf8, s, strend, aldouble, datumtype);
- DO_BO_UNPACK_N(aldouble, long double);
+ ld_bytes aldouble;
+ SHIFT_BYTES(utf8, s, strend, aldouble.bytes, sizeof(aldouble.bytes), datumtype);
+ DO_BO_UNPACK_N(aldouble.ld, long double);
if (!checksum)
- mPUSHn(aldouble);
+ mPUSHn(aldouble.ld);
else
- cdouble += aldouble;
+ cdouble += aldouble.ld;
}
break;
#endif
@@ -3156,26 +3171,26 @@ extern const double _double_constants[];
}
break;
case 'F': {
- NV anv;
+ NV_bytes anv;
Zero(&anv, 1, NV); /* can be long double with unused bits */
while (len-- > 0) {
fromstr = NEXTFROM;
- anv = SvNV(fromstr);
+ anv.nv = SvNV(fromstr);
DO_BO_PACK_N(anv, NV);
- PUSH_VAR(utf8, cur, anv);
+ PUSH_BYTES(utf8, cur, anv.bytes, sizeof(anv.bytes));
}
break;
}
#if defined(HAS_LONG_DOUBLE) && defined(USE_LONG_DOUBLE)
case 'D': {
- long double aldouble;
+ ld_bytes aldouble;
/* long doubles can have unused bits, which may be nonzero */
Zero(&aldouble, 1, long double);
while (len-- > 0) {
fromstr = NEXTFROM;
- aldouble = (long double)SvNV(fromstr);
+ aldouble.ld = (long double)SvNV(fromstr);
DO_BO_PACK_N(aldouble, long double);
- PUSH_VAR(utf8, cur, aldouble);
+ PUSH_BYTES(utf8, cur, aldouble.bytes, sizeof(aldouble.bytes));
}
break;
}