Home | History | Annotate | Line # | Download | only in gdb
      1 /* Copyright (C) 2019-2024 Free Software Foundation, Inc.
      2 
      3    This file is part of GDB.
      4 
      5    This program is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by
      7    the Free Software Foundation; either version 3 of the License, or
      8    (at your option) any later version.
      9 
     10    This program is distributed in the hope that it will be useful,
     11    but WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13    GNU General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License
     16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     17 
     18 #include "gmp-utils.h"
     19 
     20 /* See gmp-utils.h.  */
     21 
     22 std::string
     23 gmp_string_printf (const char *fmt, ...)
     24 {
     25   va_list vp;
     26 
     27   va_start (vp, fmt);
     28   int size = gmp_vsnprintf (NULL, 0, fmt, vp);
     29   va_end (vp);
     30 
     31   std::string str (size, '\0');
     32 
     33   /* C++11 and later guarantee std::string uses contiguous memory and
     34      always includes the terminating '\0'.  */
     35   va_start (vp, fmt);
     36   gmp_vsprintf (&str[0], fmt, vp);
     37   va_end (vp);
     38 
     39   return str;
     40 }
     41 
     42 /* See gmp-utils.h.  */
     43 
     44 void
     45 gdb_mpz::read (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order,
     46 	       bool unsigned_p)
     47 {
     48   mpz_import (m_val, 1 /* count */, -1 /* order */, buf.size () /* size */,
     49 	      byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */,
     50 	      0 /* nails */, buf.data () /* op */);
     51 
     52   if (!unsigned_p)
     53     {
     54       /* The value was imported as if it was a positive value,
     55 	 as mpz_import does not handle signs. If the original value
     56 	 was in fact negative, we need to adjust VAL accordingly.  */
     57       gdb_mpz max;
     58 
     59       mpz_ui_pow_ui (max.m_val, 2, buf.size () * HOST_CHAR_BIT - 1);
     60       if (mpz_cmp (m_val, max.m_val) >= 0)
     61 	mpz_submul_ui (m_val, max.m_val, 2);
     62     }
     63 }
     64 
     65 /* See gmp-utils.h.  */
     66 
     67 void
     68 gdb_mpz::export_bits (gdb::array_view<gdb_byte> buf, int endian, bool unsigned_p,
     69 		      bool safe) const
     70 {
     71   int sign = mpz_sgn (m_val);
     72   if (sign == 0)
     73     {
     74       /* Our value is zero, so no need to call mpz_export to do the work,
     75 	 especially since mpz_export's documentation explicitly says
     76 	 that the function is a noop in this case.  Just write zero to
     77 	 BUF ourselves, if it is non-empty.  In some languages, a
     78 	 zero-bit type can exist and this is also fine.  */
     79       if (buf.size () > 0)
     80 	memset (buf.data (), 0, buf.size ());
     81       return;
     82     }
     83 
     84   gdb_assert (buf.size () > 0);
     85 
     86   if (safe)
     87     {
     88       /* Determine the maximum range of values that our buffer can
     89 	 hold, and verify that VAL is within that range.  */
     90 
     91       gdb_mpz lo, hi;
     92       const size_t max_usable_bits = buf.size () * HOST_CHAR_BIT;
     93       if (unsigned_p)
     94 	{
     95 	  lo = 0;
     96 
     97 	  mpz_ui_pow_ui (hi.m_val, 2, max_usable_bits);
     98 	  mpz_sub_ui (hi.m_val, hi.m_val, 1);
     99 	}
    100       else
    101 	{
    102 	  mpz_ui_pow_ui (lo.m_val, 2, max_usable_bits - 1);
    103 	  mpz_neg (lo.m_val, lo.m_val);
    104 
    105 	  mpz_ui_pow_ui (hi.m_val, 2, max_usable_bits - 1);
    106 	  mpz_sub_ui (hi.m_val, hi.m_val, 1);
    107 	}
    108 
    109       if (mpz_cmp (m_val, lo.m_val) < 0 || mpz_cmp (m_val, hi.m_val) > 0)
    110 	error (_("Cannot export value %s as %zu-bits %s integer"
    111 		 " (must be between %s and %s)"),
    112 	       this->str ().c_str (),
    113 	       max_usable_bits,
    114 	       unsigned_p ? _("unsigned") : _("signed"),
    115 	       lo.str ().c_str (),
    116 	       hi.str ().c_str ());
    117     }
    118 
    119   const gdb_mpz *exported_val = this;
    120   gdb_mpz un_signed;
    121   if (sign < 0)
    122     {
    123       /* mpz_export does not handle signed values, so create a positive
    124 	 value whose bit representation as an unsigned of the same length
    125 	 would be the same as our negative value.  */
    126       gdb_mpz neg_offset = gdb_mpz::pow (2, buf.size () * HOST_CHAR_BIT);
    127       un_signed = *exported_val + neg_offset;
    128       exported_val = &un_signed;
    129     }
    130 
    131   /* If the value is too large, truncate it.  */
    132   if (!safe
    133       && mpz_sizeinbase (exported_val->m_val, 2) > buf.size () * HOST_CHAR_BIT)
    134     {
    135       /* If we don't already have a copy, make it now.  */
    136       if (exported_val != &un_signed)
    137 	{
    138 	  un_signed = *exported_val;
    139 	  exported_val = &un_signed;
    140 	}
    141 
    142       un_signed.mask (buf.size () * HOST_CHAR_BIT);
    143     }
    144 
    145   /* It's possible that one of the above results in zero, which has to
    146      be handled specially.  */
    147   if (exported_val->sgn () == 0)
    148     {
    149       memset (buf.data (), 0, buf.size ());
    150       return;
    151     }
    152 
    153   /* Do the export into a buffer allocated by GMP itself; that way,
    154      we can detect cases where BUF is not large enough to export
    155      our value, and thus avoid a buffer overflow.  Normally, this should
    156      never happen, since we verified earlier that the buffer is large
    157      enough to accommodate our value, but doing this allows us to be
    158      extra safe with the export.
    159 
    160      After verification that the export behaved as expected, we will
    161      copy the data over to BUF.  */
    162 
    163   size_t word_countp;
    164   gdb::unique_xmalloc_ptr<void> exported
    165     (mpz_export (NULL, &word_countp, -1 /* order */, buf.size () /* size */,
    166 		 endian, 0 /* nails */, exported_val->m_val));
    167 
    168   gdb_assert (word_countp == 1);
    169 
    170   memcpy (buf.data (), exported.get (), buf.size ());
    171 }
    172 
    173 /* See gmp-utils.h.  */
    174 
    175 gdb_mpz
    176 gdb_mpq::get_rounded () const
    177 {
    178   /* Work with a positive number so as to make the "floor" rounding
    179      always round towards zero.  */
    180 
    181   gdb_mpq abs_val (m_val);
    182   mpq_abs (abs_val.m_val, abs_val.m_val);
    183 
    184   /* Convert our rational number into a quotient and remainder,
    185      with "floor" rounding, which in our case means rounding
    186      towards zero.  */
    187 
    188   gdb_mpz quotient, remainder;
    189   mpz_fdiv_qr (quotient.m_val, remainder.m_val,
    190 	       mpq_numref (abs_val.m_val), mpq_denref (abs_val.m_val));
    191 
    192   /* Multiply the remainder by 2, and see if it is greater or equal
    193      to abs_val's denominator.  If yes, round to the next integer.  */
    194 
    195   mpz_mul_ui (remainder.m_val, remainder.m_val, 2);
    196   if (mpz_cmp (remainder.m_val, mpq_denref (abs_val.m_val)) >= 0)
    197     mpz_add_ui (quotient.m_val, quotient.m_val, 1);
    198 
    199   /* Re-apply the sign if needed.  */
    200   if (mpq_sgn (m_val) < 0)
    201     mpz_neg (quotient.m_val, quotient.m_val);
    202 
    203   return quotient;
    204 }
    205 
    206 /* See gmp-utils.h.  */
    207 
    208 void
    209 gdb_mpq::read_fixed_point (gdb::array_view<const gdb_byte> buf,
    210 			   enum bfd_endian byte_order, bool unsigned_p,
    211 			   const gdb_mpq &scaling_factor)
    212 {
    213   gdb_mpz vz;
    214   vz.read (buf, byte_order, unsigned_p);
    215 
    216   mpq_set_z (m_val, vz.m_val);
    217   mpq_mul (m_val, m_val, scaling_factor.m_val);
    218 }
    219 
    220 /* See gmp-utils.h.  */
    221 
    222 void
    223 gdb_mpq::write_fixed_point (gdb::array_view<gdb_byte> buf,
    224 			    enum bfd_endian byte_order, bool unsigned_p,
    225 			    const gdb_mpq &scaling_factor) const
    226 {
    227   gdb_mpq unscaled (m_val);
    228 
    229   mpq_div (unscaled.m_val, unscaled.m_val, scaling_factor.m_val);
    230 
    231   gdb_mpz unscaled_z = unscaled.get_rounded ();
    232   unscaled_z.write (buf, byte_order, unsigned_p);
    233 }
    234 
    235 /* A wrapper around xrealloc that we can then register with GMP
    236    as the "realloc" function.  */
    237 
    238 static void *
    239 xrealloc_for_gmp (void *ptr, size_t old_size, size_t new_size)
    240 {
    241   return xrealloc (ptr, new_size);
    242 }
    243 
    244 /* A wrapper around xfree that we can then register with GMP
    245    as the "free" function.  */
    246 
    247 static void
    248 xfree_for_gmp (void *ptr, size_t size)
    249 {
    250   xfree (ptr);
    251 }
    252 
    253 void _initialize_gmp_utils ();
    254 
    255 void
    256 _initialize_gmp_utils ()
    257 {
    258   /* Tell GMP to use GDB's memory management routines.  */
    259   mp_set_memory_functions (xmalloc, xrealloc_for_gmp, xfree_for_gmp);
    260 }
    261