This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH, libiberty] Fix segfault in floatformat.c:get_field on 64-bit hosts
- From: Julian Brown <julian at codesourcery dot com>
- To: binutils at sourceware dot org
- Cc: GCC Patches <gcc-patches at gcc dot gnu dot org>, DJ Delorie <dj at redhat dot com>
- Date: Fri, 09 Jun 2006 17:53:13 +0100
- Subject: [PATCH, libiberty] Fix segfault in floatformat.c:get_field on 64-bit hosts
Hi,
This patch fixes a problem with floatformat.c:get_field on 64-bit (on at
least x86_64), when cross-assembling to arm-none-eabi. The line which reads:
result = *(data + cur_byte) >> (-cur_bitshift);
was executed with cur_byte = -1 (start + len == 0 and order ==
floatformat_little), which happily segfaulted (during printing of FP
immediates).
I had a little trouble following the logic of the function, so this is
basically a rewrite. I hope that's OK.
Tested with cross (binutils) to arm-none-eabi from
powerpc64-unknown-linux-gnu (i.e. big-endian) and
x86_64-unknown-linux-gnu (i.e. little-endian). OK to apply?
Cheers,
Julian
ChangeLog:
* floatformat.c (get_field): Fix segfault with little-endian word
order on 64-bit hosts.
(min): Move definition.
Index: libiberty/floatformat.c
===================================================================
RCS file: /cvs/src/src/libiberty/floatformat.c,v
retrieving revision 1.19.6.1
diff -c -p -r1.19.6.1 floatformat.c
*** libiberty/floatformat.c 24 Apr 2006 21:37:24 -0000 1.19.6.1
--- libiberty/floatformat.c 24 May 2006 12:51:20 -0000
*************** const struct floatformat floatformat_ia6
*** 249,301 ****
floatformat_always_valid
};
/* Extract a field which starts at START and is LEN bits long. DATA and
TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */
static unsigned long
get_field (const unsigned char *data, enum floatformat_byteorders order,
unsigned int total_len, unsigned int start, unsigned int len)
{
! unsigned long result;
unsigned int cur_byte;
! int cur_bitshift;
/* Start at the least significant part of the field. */
- cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT;
if (order == floatformat_little)
! cur_byte = (total_len / FLOATFORMAT_CHAR_BIT) - cur_byte - 1;
! cur_bitshift =
! ((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT;
! result = *(data + cur_byte) >> (-cur_bitshift);
! cur_bitshift += FLOATFORMAT_CHAR_BIT;
! if (order == floatformat_little)
! ++cur_byte;
else
! --cur_byte;
! /* Move towards the most significant part of the field. */
! while ((unsigned int) cur_bitshift < len)
{
! if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT)
! /* This is the last byte; zero out the bits which are not part of
! this field. */
! result |=
! (*(data + cur_byte) & ((1 << (len - cur_bitshift)) - 1))
! << cur_bitshift;
! else
! result |= *(data + cur_byte) << cur_bitshift;
! cur_bitshift += FLOATFORMAT_CHAR_BIT;
! if (order == floatformat_little)
! ++cur_byte;
! else
! --cur_byte;
}
! return result;
}
- #ifndef min
- #define min(a, b) ((a) < (b) ? (a) : (b))
- #endif
-
/* Convert from FMT to a double.
FROM is the address of the extended float.
Store the double in *TO. */
--- 249,299 ----
floatformat_always_valid
};
+
+ #ifndef min
+ #define min(a, b) ((a) < (b) ? (a) : (b))
+ #endif
+
/* Extract a field which starts at START and is LEN bits long. DATA and
TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */
static unsigned long
get_field (const unsigned char *data, enum floatformat_byteorders order,
unsigned int total_len, unsigned int start, unsigned int len)
{
! unsigned long result = 0;
unsigned int cur_byte;
! int lo_bit, hi_bit, cur_bitshift = 0;
! int nextbyte = (order == floatformat_little) ? 1 : -1;
!
! /* Start is in big-endian bit order! Fix that first. */
! start = total_len - (start + len);
/* Start at the least significant part of the field. */
if (order == floatformat_little)
! cur_byte = start / FLOATFORMAT_CHAR_BIT;
else
! cur_byte = (total_len - start - 1) / FLOATFORMAT_CHAR_BIT;
! lo_bit = start % FLOATFORMAT_CHAR_BIT;
! hi_bit = min (lo_bit + len, FLOATFORMAT_CHAR_BIT);
!
! do
{
! unsigned int shifted = *(data + cur_byte) >> lo_bit;
! unsigned int bits = hi_bit - lo_bit;
! unsigned int mask = (1 << bits) - 1;
! result |= (shifted & mask) << cur_bitshift;
! len -= bits;
! cur_bitshift += bits;
! cur_byte += nextbyte;
! lo_bit = 0;
! hi_bit = min (len, FLOATFORMAT_CHAR_BIT);
}
! while (len != 0);
!
! return result & ((2 << (total_len - 1)) - 1);
}
/* Convert from FMT to a double.
FROM is the address of the extended float.
Store the double in *TO. */