| 1 | /* Copyright (C) 2019-2021 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 (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.val, 2, buf.size () * HOST_CHAR_BIT - 1); |
| 60 | if (mpz_cmp (val, max.val) >= 0) |
| 61 | mpz_submul_ui (val, max.val, 2); |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | /* See gmp-utils.h. */ |
| 66 | |
| 67 | void |
| 68 | gdb_mpz::write (gdb::array_view<gdb_byte> buf, enum bfd_endian byte_order, |
| 69 | bool unsigned_p) const |
| 70 | { |
| 71 | this->safe_export |
| 72 | (buf, byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */, unsigned_p); |
| 73 | } |
| 74 | |
| 75 | /* See gmp-utils.h. */ |
| 76 | |
| 77 | void |
| 78 | gdb_mpz::safe_export (gdb::array_view<gdb_byte> buf, |
| 79 | int endian, bool unsigned_p) const |
| 80 | { |
| 81 | gdb_assert (buf.size () > 0); |
| 82 | |
| 83 | if (mpz_sgn (val) == 0) |
| 84 | { |
| 85 | /* Our value is zero, so no need to call mpz_export to do the work, |
| 86 | especially since mpz_export's documentation explicitly says |
| 87 | that the function is a noop in this case. Just write zero to |
| 88 | BUF ourselves. */ |
| 89 | memset (buf.data (), 0, buf.size ()); |
| 90 | return; |
| 91 | } |
| 92 | |
| 93 | /* Determine the maximum range of values that our buffer can hold, |
| 94 | and verify that VAL is within that range. */ |
| 95 | |
| 96 | gdb_mpz lo, hi; |
| 97 | const size_t max_usable_bits = buf.size () * HOST_CHAR_BIT; |
| 98 | if (unsigned_p) |
| 99 | { |
| 100 | lo = 0; |
| 101 | |
| 102 | mpz_ui_pow_ui (hi.val, 2, max_usable_bits); |
| 103 | mpz_sub_ui (hi.val, hi.val, 1); |
| 104 | } |
| 105 | else |
| 106 | { |
| 107 | mpz_ui_pow_ui (lo.val, 2, max_usable_bits - 1); |
| 108 | mpz_neg (lo.val, lo.val); |
| 109 | |
| 110 | mpz_ui_pow_ui (hi.val, 2, max_usable_bits - 1); |
| 111 | mpz_sub_ui (hi.val, hi.val, 1); |
| 112 | } |
| 113 | |
| 114 | if (mpz_cmp (val, lo.val) < 0 || mpz_cmp (val, hi.val) > 0) |
| 115 | error (_("Cannot export value %s as %zu-bits %s integer" |
| 116 | " (must be between %s and %s)"), |
| 117 | this->str ().c_str (), |
| 118 | max_usable_bits, |
| 119 | unsigned_p ? _("unsigned") : _("signed"), |
| 120 | lo.str ().c_str (), |
| 121 | hi.str ().c_str ()); |
| 122 | |
| 123 | gdb_mpz exported_val (val); |
| 124 | |
| 125 | if (mpz_cmp_ui (exported_val.val, 0) < 0) |
| 126 | { |
| 127 | /* mpz_export does not handle signed values, so create a positive |
| 128 | value whose bit representation as an unsigned of the same length |
| 129 | would be the same as our negative value. */ |
| 130 | gdb_mpz neg_offset; |
| 131 | |
| 132 | mpz_ui_pow_ui (neg_offset.val, 2, buf.size () * HOST_CHAR_BIT); |
| 133 | mpz_add (exported_val.val, exported_val.val, neg_offset.val); |
| 134 | } |
| 135 | |
| 136 | /* Do the export into a buffer allocated by GMP itself; that way, |
| 137 | we can detect cases where BUF is not large enough to export |
| 138 | our value, and thus avoid a buffer overlow. Normally, this should |
| 139 | never happen, since we verified earlier that the buffer is large |
| 140 | enough to accomodate our value, but doing this allows us to be |
| 141 | extra safe with the export. |
| 142 | |
| 143 | After verification that the export behaved as expected, we will |
| 144 | copy the data over to BUF. */ |
| 145 | |
| 146 | size_t word_countp; |
| 147 | gdb::unique_xmalloc_ptr<void> exported |
| 148 | (mpz_export (NULL, &word_countp, -1 /* order */, buf.size () /* size */, |
| 149 | endian, 0 /* nails */, exported_val.val)); |
| 150 | |
| 151 | gdb_assert (word_countp == 1); |
| 152 | |
| 153 | memcpy (buf.data (), exported.get (), buf.size ()); |
| 154 | } |
| 155 | |
| 156 | /* See gmp-utils.h. */ |
| 157 | |
| 158 | gdb_mpz |
| 159 | gdb_mpq::get_rounded () const |
| 160 | { |
| 161 | /* Work with a positive number so as to make the "floor" rounding |
| 162 | always round towards zero. */ |
| 163 | |
| 164 | gdb_mpq abs_val (val); |
| 165 | mpq_abs (abs_val.val, abs_val.val); |
| 166 | |
| 167 | /* Convert our rational number into a quotient and remainder, |
| 168 | with "floor" rounding, which in our case means rounding |
| 169 | towards zero. */ |
| 170 | |
| 171 | gdb_mpz quotient, remainder; |
| 172 | mpz_fdiv_qr (quotient.val, remainder.val, |
| 173 | mpq_numref (abs_val.val), mpq_denref (abs_val.val)); |
| 174 | |
| 175 | /* Multiply the remainder by 2, and see if it is greater or equal |
| 176 | to abs_val's denominator. If yes, round to the next integer. */ |
| 177 | |
| 178 | mpz_mul_ui (remainder.val, remainder.val, 2); |
| 179 | if (mpz_cmp (remainder.val, mpq_denref (abs_val.val)) >= 0) |
| 180 | mpz_add_ui (quotient.val, quotient.val, 1); |
| 181 | |
| 182 | /* Re-apply the sign if needed. */ |
| 183 | if (mpq_sgn (val) < 0) |
| 184 | mpz_neg (quotient.val, quotient.val); |
| 185 | |
| 186 | return quotient; |
| 187 | } |
| 188 | |
| 189 | /* See gmp-utils.h. */ |
| 190 | |
| 191 | void |
| 192 | gdb_mpq::read_fixed_point (gdb::array_view<const gdb_byte> buf, |
| 193 | enum bfd_endian byte_order, bool unsigned_p, |
| 194 | const gdb_mpq &scaling_factor) |
| 195 | { |
| 196 | gdb_mpz vz; |
| 197 | vz.read (buf, byte_order, unsigned_p); |
| 198 | |
| 199 | mpq_set_z (val, vz.val); |
| 200 | mpq_mul (val, val, scaling_factor.val); |
| 201 | } |
| 202 | |
| 203 | /* See gmp-utils.h. */ |
| 204 | |
| 205 | void |
| 206 | gdb_mpq::write_fixed_point (gdb::array_view<gdb_byte> buf, |
| 207 | enum bfd_endian byte_order, bool unsigned_p, |
| 208 | const gdb_mpq &scaling_factor) const |
| 209 | { |
| 210 | gdb_mpq unscaled (val); |
| 211 | |
| 212 | mpq_div (unscaled.val, unscaled.val, scaling_factor.val); |
| 213 | |
| 214 | gdb_mpz unscaled_z = unscaled.get_rounded (); |
| 215 | unscaled_z.write (buf, byte_order, unsigned_p); |
| 216 | } |
| 217 | |
| 218 | /* A wrapper around xrealloc that we can then register with GMP |
| 219 | as the "realloc" function. */ |
| 220 | |
| 221 | static void * |
| 222 | xrealloc_for_gmp (void *ptr, size_t old_size, size_t new_size) |
| 223 | { |
| 224 | return xrealloc (ptr, new_size); |
| 225 | } |
| 226 | |
| 227 | /* A wrapper around xfree that we can then register with GMP |
| 228 | as the "free" function. */ |
| 229 | |
| 230 | static void |
| 231 | xfree_for_gmp (void *ptr, size_t size) |
| 232 | { |
| 233 | xfree (ptr); |
| 234 | } |
| 235 | |
| 236 | void _initialize_gmp_utils (); |
| 237 | |
| 238 | void |
| 239 | _initialize_gmp_utils () |
| 240 | { |
| 241 | /* Tell GMP to use GDB's memory management routines. */ |
| 242 | mp_set_memory_functions (xmalloc, xrealloc_for_gmp, xfree_for_gmp); |
| 243 | } |