This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[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.  */

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]