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