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