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
gmp_string_printf(const char * fmt,...)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
read(gdb::array_view<const gdb_byte> buf,enum bfd_endian byte_order,bool unsigned_p)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
write(gdb::array_view<gdb_byte> buf,enum bfd_endian byte_order,bool unsigned_p)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
safe_export(gdb::array_view<gdb_byte> buf,int endian,bool unsigned_p)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
get_rounded()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
read_fixed_point(gdb::array_view<const gdb_byte> buf,enum bfd_endian byte_order,bool unsigned_p,const gdb_mpq & scaling_factor)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
write_fixed_point(gdb::array_view<gdb_byte> buf,enum bfd_endian byte_order,bool unsigned_p,const gdb_mpq & scaling_factor)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 *
xrealloc_for_gmp(void * ptr,size_t old_size,size_t new_size)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
xfree_for_gmp(void * ptr,size_t size)231 xfree_for_gmp (void *ptr, size_t size)
232 {
233   xfree (ptr);
234 }
235 
236 void _initialize_gmp_utils ();
237 
238 void
_initialize_gmp_utils()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 }
244