|  | /* Copyright (C) 2019-2022 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "gmp-utils.h" | 
|  |  | 
|  | /* See gmp-utils.h.  */ | 
|  |  | 
|  | std::string | 
|  | gmp_string_printf (const char *fmt, ...) | 
|  | { | 
|  | va_list vp; | 
|  |  | 
|  | va_start (vp, fmt); | 
|  | int size = gmp_vsnprintf (NULL, 0, fmt, vp); | 
|  | va_end (vp); | 
|  |  | 
|  | std::string str (size, '\0'); | 
|  |  | 
|  | /* C++11 and later guarantee std::string uses contiguous memory and | 
|  | always includes the terminating '\0'.  */ | 
|  | va_start (vp, fmt); | 
|  | gmp_vsprintf (&str[0], fmt, vp); | 
|  | va_end (vp); | 
|  |  | 
|  | return str; | 
|  | } | 
|  |  | 
|  | /* See gmp-utils.h.  */ | 
|  |  | 
|  | void | 
|  | gdb_mpz::read (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order, | 
|  | bool unsigned_p) | 
|  | { | 
|  | mpz_import (val, 1 /* count */, -1 /* order */, buf.size () /* size */, | 
|  | byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */, | 
|  | 0 /* nails */, buf.data () /* op */); | 
|  |  | 
|  | if (!unsigned_p) | 
|  | { | 
|  | /* The value was imported as if it was a positive value, | 
|  | as mpz_import does not handle signs. If the original value | 
|  | was in fact negative, we need to adjust VAL accordingly.  */ | 
|  | gdb_mpz max; | 
|  |  | 
|  | mpz_ui_pow_ui (max.val, 2, buf.size () * HOST_CHAR_BIT - 1); | 
|  | if (mpz_cmp (val, max.val) >= 0) | 
|  | mpz_submul_ui (val, max.val, 2); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* See gmp-utils.h.  */ | 
|  |  | 
|  | void | 
|  | gdb_mpz::write (gdb::array_view<gdb_byte> buf, enum bfd_endian byte_order, | 
|  | bool unsigned_p) const | 
|  | { | 
|  | this->safe_export | 
|  | (buf, byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */, unsigned_p); | 
|  | } | 
|  |  | 
|  | /* See gmp-utils.h.  */ | 
|  |  | 
|  | void | 
|  | gdb_mpz::safe_export (gdb::array_view<gdb_byte> buf, | 
|  | int endian, bool unsigned_p) const | 
|  | { | 
|  | gdb_assert (buf.size () > 0); | 
|  |  | 
|  | if (mpz_sgn (val) == 0) | 
|  | { | 
|  | /* Our value is zero, so no need to call mpz_export to do the work, | 
|  | especially since mpz_export's documentation explicitly says | 
|  | that the function is a noop in this case.  Just write zero to | 
|  | BUF ourselves.  */ | 
|  | memset (buf.data (), 0, buf.size ()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Determine the maximum range of values that our buffer can hold, | 
|  | and verify that VAL is within that range.  */ | 
|  |  | 
|  | gdb_mpz lo, hi; | 
|  | const size_t max_usable_bits = buf.size () * HOST_CHAR_BIT; | 
|  | if (unsigned_p) | 
|  | { | 
|  | lo = 0; | 
|  |  | 
|  | mpz_ui_pow_ui (hi.val, 2, max_usable_bits); | 
|  | mpz_sub_ui (hi.val, hi.val, 1); | 
|  | } | 
|  | else | 
|  | { | 
|  | mpz_ui_pow_ui (lo.val, 2, max_usable_bits - 1); | 
|  | mpz_neg (lo.val, lo.val); | 
|  |  | 
|  | mpz_ui_pow_ui (hi.val, 2, max_usable_bits - 1); | 
|  | mpz_sub_ui (hi.val, hi.val, 1); | 
|  | } | 
|  |  | 
|  | if (mpz_cmp (val, lo.val) < 0 || mpz_cmp (val, hi.val) > 0) | 
|  | error (_("Cannot export value %s as %zu-bits %s integer" | 
|  | " (must be between %s and %s)"), | 
|  | this->str ().c_str (), | 
|  | max_usable_bits, | 
|  | unsigned_p ? _("unsigned") : _("signed"), | 
|  | lo.str ().c_str (), | 
|  | hi.str ().c_str ()); | 
|  |  | 
|  | gdb_mpz exported_val (val); | 
|  |  | 
|  | if (mpz_cmp_ui (exported_val.val, 0) < 0) | 
|  | { | 
|  | /* mpz_export does not handle signed values, so create a positive | 
|  | value whose bit representation as an unsigned of the same length | 
|  | would be the same as our negative value.  */ | 
|  | gdb_mpz neg_offset; | 
|  |  | 
|  | mpz_ui_pow_ui (neg_offset.val, 2, buf.size () * HOST_CHAR_BIT); | 
|  | mpz_add (exported_val.val, exported_val.val, neg_offset.val); | 
|  | } | 
|  |  | 
|  | /* Do the export into a buffer allocated by GMP itself; that way, | 
|  | we can detect cases where BUF is not large enough to export | 
|  | our value, and thus avoid a buffer overlow.  Normally, this should | 
|  | never happen, since we verified earlier that the buffer is large | 
|  | enough to accomodate our value, but doing this allows us to be | 
|  | extra safe with the export. | 
|  |  | 
|  | After verification that the export behaved as expected, we will | 
|  | copy the data over to BUF.  */ | 
|  |  | 
|  | size_t word_countp; | 
|  | gdb::unique_xmalloc_ptr<void> exported | 
|  | (mpz_export (NULL, &word_countp, -1 /* order */, buf.size () /* size */, | 
|  | endian, 0 /* nails */, exported_val.val)); | 
|  |  | 
|  | gdb_assert (word_countp == 1); | 
|  |  | 
|  | memcpy (buf.data (), exported.get (), buf.size ()); | 
|  | } | 
|  |  | 
|  | /* See gmp-utils.h.  */ | 
|  |  | 
|  | gdb_mpz | 
|  | gdb_mpq::get_rounded () const | 
|  | { | 
|  | /* Work with a positive number so as to make the "floor" rounding | 
|  | always round towards zero.  */ | 
|  |  | 
|  | gdb_mpq abs_val (val); | 
|  | mpq_abs (abs_val.val, abs_val.val); | 
|  |  | 
|  | /* Convert our rational number into a quotient and remainder, | 
|  | with "floor" rounding, which in our case means rounding | 
|  | towards zero.  */ | 
|  |  | 
|  | gdb_mpz quotient, remainder; | 
|  | mpz_fdiv_qr (quotient.val, remainder.val, | 
|  | mpq_numref (abs_val.val), mpq_denref (abs_val.val)); | 
|  |  | 
|  | /* Multiply the remainder by 2, and see if it is greater or equal | 
|  | to abs_val's denominator.  If yes, round to the next integer.  */ | 
|  |  | 
|  | mpz_mul_ui (remainder.val, remainder.val, 2); | 
|  | if (mpz_cmp (remainder.val, mpq_denref (abs_val.val)) >= 0) | 
|  | mpz_add_ui (quotient.val, quotient.val, 1); | 
|  |  | 
|  | /* Re-apply the sign if needed.  */ | 
|  | if (mpq_sgn (val) < 0) | 
|  | mpz_neg (quotient.val, quotient.val); | 
|  |  | 
|  | return quotient; | 
|  | } | 
|  |  | 
|  | /* See gmp-utils.h.  */ | 
|  |  | 
|  | void | 
|  | gdb_mpq::read_fixed_point (gdb::array_view<const gdb_byte> buf, | 
|  | enum bfd_endian byte_order, bool unsigned_p, | 
|  | const gdb_mpq &scaling_factor) | 
|  | { | 
|  | gdb_mpz vz; | 
|  | vz.read (buf, byte_order, unsigned_p); | 
|  |  | 
|  | mpq_set_z (val, vz.val); | 
|  | mpq_mul (val, val, scaling_factor.val); | 
|  | } | 
|  |  | 
|  | /* See gmp-utils.h.  */ | 
|  |  | 
|  | void | 
|  | gdb_mpq::write_fixed_point (gdb::array_view<gdb_byte> buf, | 
|  | enum bfd_endian byte_order, bool unsigned_p, | 
|  | const gdb_mpq &scaling_factor) const | 
|  | { | 
|  | gdb_mpq unscaled (val); | 
|  |  | 
|  | mpq_div (unscaled.val, unscaled.val, scaling_factor.val); | 
|  |  | 
|  | gdb_mpz unscaled_z = unscaled.get_rounded (); | 
|  | unscaled_z.write (buf, byte_order, unsigned_p); | 
|  | } | 
|  |  | 
|  | /* A wrapper around xrealloc that we can then register with GMP | 
|  | as the "realloc" function.  */ | 
|  |  | 
|  | static void * | 
|  | xrealloc_for_gmp (void *ptr, size_t old_size, size_t new_size) | 
|  | { | 
|  | return xrealloc (ptr, new_size); | 
|  | } | 
|  |  | 
|  | /* A wrapper around xfree that we can then register with GMP | 
|  | as the "free" function.  */ | 
|  |  | 
|  | static void | 
|  | xfree_for_gmp (void *ptr, size_t size) | 
|  | { | 
|  | xfree (ptr); | 
|  | } | 
|  |  | 
|  | void _initialize_gmp_utils (); | 
|  |  | 
|  | void | 
|  | _initialize_gmp_utils () | 
|  | { | 
|  | /* Tell GMP to use GDB's memory management routines.  */ | 
|  | mp_set_memory_functions (xmalloc, xrealloc_for_gmp, xfree_for_gmp); | 
|  | } |