gdb: fix vfork with multiple threads
[deliverable/binutils-gdb.git] / gdb / gmp-utils.c
CommitLineData
3666a048 1/* Copyright (C) 2019-2021 Free Software Foundation, Inc.
b34c74ab
JB
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
987b6703
JB
22std::string
23gmp_string_printf (const char *fmt, ...)
b34c74ab
JB
24{
25 va_list vp;
b34c74ab
JB
26
27 va_start (vp, fmt);
987b6703 28 int size = gmp_vsnprintf (NULL, 0, fmt, vp);
b34c74ab
JB
29 va_end (vp);
30
987b6703
JB
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;
b34c74ab
JB
40}
41
42/* See gmp-utils.h. */
43
44void
c9f0b43f 45gdb_mpz::read (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order,
b34c74ab
JB
46 bool unsigned_p)
47{
c9f0b43f 48 mpz_import (val, 1 /* count */, -1 /* order */, buf.size () /* size */,
b34c74ab 49 byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */,
c9f0b43f 50 0 /* nails */, buf.data () /* op */);
b34c74ab
JB
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
3c7ba803 59 mpz_ui_pow_ui (max.val, 2, buf.size () * HOST_CHAR_BIT - 1);
b34c74ab
JB
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
67void
c9f0b43f 68gdb_mpz::write (gdb::array_view<gdb_byte> buf, enum bfd_endian byte_order,
b34c74ab
JB
69 bool unsigned_p) const
70{
63c457b9
JB
71 this->safe_export
72 (buf, byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */, unsigned_p);
73}
74
75/* See gmp-utils.h. */
76
77void
78gdb_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
b34c74ab
JB
123 gdb_mpz exported_val (val);
124
63c457b9 125 if (mpz_cmp_ui (exported_val.val, 0) < 0)
b34c74ab
JB
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
3c7ba803 132 mpz_ui_pow_ui (neg_offset.val, 2, buf.size () * HOST_CHAR_BIT);
b34c74ab
JB
133 mpz_add (exported_val.val, exported_val.val, neg_offset.val);
134 }
135
63c457b9
JB
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 ());
b34c74ab
JB
154}
155
156/* See gmp-utils.h. */
157
158gdb_mpz
159gdb_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
191void
c9f0b43f 192gdb_mpq::read_fixed_point (gdb::array_view<const gdb_byte> buf,
b34c74ab
JB
193 enum bfd_endian byte_order, bool unsigned_p,
194 const gdb_mpq &scaling_factor)
195{
196 gdb_mpz vz;
c9f0b43f 197 vz.read (buf, byte_order, unsigned_p);
b34c74ab
JB
198
199 mpq_set_z (val, vz.val);
200 mpq_mul (val, val, scaling_factor.val);
201}
202
203/* See gmp-utils.h. */
204
205void
c9f0b43f 206gdb_mpq::write_fixed_point (gdb::array_view<gdb_byte> buf,
b34c74ab
JB
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 ();
c9f0b43f 215 unscaled_z.write (buf, byte_order, unsigned_p);
b34c74ab
JB
216}
217
218/* A wrapper around xrealloc that we can then register with GMP
219 as the "realloc" function. */
220
221static void *
222xrealloc_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
230static void
231xfree_for_gmp (void *ptr, size_t size)
232{
233 xfree (ptr);
234}
235
236void _initialize_gmp_utils ();
237
238void
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}
This page took 0.095867 seconds and 4 git commands to generate.