aboutsummaryrefslogtreecommitdiff
path: root/gdb/extract-store-integer.c
diff options
context:
space:
mode:
authorSimon Marchi <simon.marchi@efficios.com>2024-04-22 16:10:14 -0400
committerSimon Marchi <simon.marchi@polymtl.ca>2024-04-22 21:34:19 -0400
commitec4525259262492b61fb9bd2f9acdf156ab037fc (patch)
tree73e3cf9b3f4049db32037f6ede09abd6dd08ec71 /gdb/extract-store-integer.c
parent15afb100ea6bc04d8944a919bf58c5c3fb8c20ec (diff)
downloadbinutils-ec4525259262492b61fb9bd2f9acdf156ab037fc.zip
binutils-ec4525259262492b61fb9bd2f9acdf156ab037fc.tar.gz
binutils-ec4525259262492b61fb9bd2f9acdf156ab037fc.tar.bz2
gdb: move store/extract integer functions to extract-store-integer.{c,h}
Move the declarations out of defs.h, and the implementations out of findvar.c. I opted for a new file, because this functionality of converting integers to bytes and vice-versa seems a bit to generic to live in findvar.c. Change-Id: I524858fca33901ee2150c582bac16042148d2251 Approved-By: John Baldwin <jhb@FreeBSD.org>
Diffstat (limited to 'gdb/extract-store-integer.c')
-rw-r--r--gdb/extract-store-integer.c308
1 files changed, 308 insertions, 0 deletions
diff --git a/gdb/extract-store-integer.c b/gdb/extract-store-integer.c
new file mode 100644
index 0000000..a3b7e40
--- /dev/null
+++ b/gdb/extract-store-integer.c
@@ -0,0 +1,308 @@
+/* Copyright (C) 2024 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 "extract-store-integer.h"
+#include "gdbtypes.h"
+#include "gdbarch.h"
+#include "gdbsupport/selftest.h"
+
+template<typename T, typename>
+T
+extract_integer (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order)
+{
+ typename std::make_unsigned<T>::type retval = 0;
+
+ if (buf.size () > (int) sizeof (T))
+ error (_("\
+That operation is not available on integers of more than %d bytes."),
+ (int) sizeof (T));
+
+ /* Start at the most significant end of the integer, and work towards
+ the least significant. */
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ size_t i = 0;
+
+ if (std::is_signed<T>::value)
+ {
+ /* Do the sign extension once at the start. */
+ retval = ((LONGEST) buf[i] ^ 0x80) - 0x80;
+ ++i;
+ }
+ for (; i < buf.size (); ++i)
+ retval = (retval << 8) | buf[i];
+ }
+ else
+ {
+ ssize_t i = buf.size () - 1;
+
+ if (std::is_signed<T>::value)
+ {
+ /* Do the sign extension once at the start. */
+ retval = ((LONGEST) buf[i] ^ 0x80) - 0x80;
+ --i;
+ }
+ for (; i >= 0; --i)
+ retval = (retval << 8) | buf[i];
+ }
+ return retval;
+}
+
+/* Explicit instantiations. */
+template LONGEST extract_integer<LONGEST> (gdb::array_view<const gdb_byte> buf,
+ enum bfd_endian byte_order);
+template ULONGEST extract_integer<ULONGEST>
+ (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order);
+
+/* Sometimes a long long unsigned integer can be extracted as a
+ LONGEST value. This is done so that we can print these values
+ better. If this integer can be converted to a LONGEST, this
+ function returns 1 and sets *PVAL. Otherwise it returns 0. */
+
+int
+extract_long_unsigned_integer (const gdb_byte *addr, int orig_len,
+ enum bfd_endian byte_order, LONGEST *pval)
+{
+ const gdb_byte *p;
+ const gdb_byte *first_addr;
+ int len;
+
+ len = orig_len;
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ for (p = addr;
+ len > (int) sizeof (LONGEST) && p < addr + orig_len;
+ p++)
+ {
+ if (*p == 0)
+ len--;
+ else
+ break;
+ }
+ first_addr = p;
+ }
+ else
+ {
+ first_addr = addr;
+ for (p = addr + orig_len - 1;
+ len > (int) sizeof (LONGEST) && p >= addr;
+ p--)
+ {
+ if (*p == 0)
+ len--;
+ else
+ break;
+ }
+ }
+
+ if (len <= (int) sizeof (LONGEST))
+ {
+ *pval = (LONGEST) extract_unsigned_integer (first_addr,
+ sizeof (LONGEST),
+ byte_order);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* Treat the bytes at BUF as a pointer of type TYPE, and return the
+ address it represents. */
+CORE_ADDR
+extract_typed_address (const gdb_byte *buf, struct type *type)
+{
+ gdb_assert (type->is_pointer_or_reference ());
+ return gdbarch_pointer_to_address (type->arch (), type, buf);
+}
+
+/* All 'store' functions accept a host-format integer and store a
+ target-format integer at ADDR which is LEN bytes long. */
+template<typename T, typename>
+void
+store_integer (gdb::array_view<gdb_byte> dst, enum bfd_endian byte_order,
+ T val)
+{
+ gdb_byte *p;
+ gdb_byte *startaddr = dst.data ();
+ gdb_byte *endaddr = startaddr + dst.size ();
+
+ /* Start at the least significant end of the integer, and work towards
+ the most significant. */
+ if (byte_order == BFD_ENDIAN_BIG)
+ {
+ for (p = endaddr - 1; p >= startaddr; --p)
+ {
+ *p = val & 0xff;
+ val >>= 8;
+ }
+ }
+ else
+ {
+ for (p = startaddr; p < endaddr; ++p)
+ {
+ *p = val & 0xff;
+ val >>= 8;
+ }
+ }
+}
+
+/* Explicit instantiations. */
+template void store_integer (gdb::array_view<gdb_byte> dst,
+ bfd_endian byte_order, LONGEST val);
+
+template void store_integer (gdb::array_view<gdb_byte> dst,
+ bfd_endian byte_order, ULONGEST val);
+
+/* Store the address ADDR as a pointer of type TYPE at BUF, in target
+ form. */
+void
+store_typed_address (gdb_byte *buf, struct type *type, CORE_ADDR addr)
+{
+ gdb_assert (type->is_pointer_or_reference ());
+ gdbarch_address_to_pointer (type->arch (), type, buf, addr);
+}
+
+/* Copy a value from SOURCE of size SOURCE_SIZE bytes to DEST of size DEST_SIZE
+ bytes. If SOURCE_SIZE is greater than DEST_SIZE, then truncate the most
+ significant bytes. If SOURCE_SIZE is less than DEST_SIZE then either sign
+ or zero extended according to IS_SIGNED. Values are stored in memory with
+ endianness BYTE_ORDER. */
+
+void
+copy_integer_to_size (gdb_byte *dest, int dest_size, const gdb_byte *source,
+ int source_size, bool is_signed,
+ enum bfd_endian byte_order)
+{
+ signed int size_diff = dest_size - source_size;
+
+ /* Copy across everything from SOURCE that can fit into DEST. */
+
+ if (byte_order == BFD_ENDIAN_BIG && size_diff > 0)
+ memcpy (dest + size_diff, source, source_size);
+ else if (byte_order == BFD_ENDIAN_BIG && size_diff < 0)
+ memcpy (dest, source - size_diff, dest_size);
+ else
+ memcpy (dest, source, std::min (source_size, dest_size));
+
+ /* Fill the remaining space in DEST by either zero extending or sign
+ extending. */
+
+ if (size_diff > 0)
+ {
+ gdb_byte extension = 0;
+ if (is_signed
+ && ((byte_order != BFD_ENDIAN_BIG && source[source_size - 1] & 0x80)
+ || (byte_order == BFD_ENDIAN_BIG && source[0] & 0x80)))
+ extension = 0xff;
+
+ /* Extend into MSBs of SOURCE. */
+ if (byte_order == BFD_ENDIAN_BIG)
+ memset (dest, extension, size_diff);
+ else
+ memset (dest + source_size, extension, size_diff);
+ }
+}
+
+#if GDB_SELF_TEST
+namespace selftests {
+
+/* Function to test copy_integer_to_size. Store SOURCE_VAL with size
+ SOURCE_SIZE to a buffer, making sure no sign extending happens at this
+ stage. Copy buffer to a new buffer using copy_integer_to_size. Extract
+ copied value and compare to DEST_VALU. Copy again with a signed
+ copy_integer_to_size and compare to DEST_VALS. Do everything for both
+ LITTLE and BIG target endians. Use unsigned values throughout to make
+ sure there are no implicit sign extensions. */
+
+static void
+do_cint_test (ULONGEST dest_valu, ULONGEST dest_vals, int dest_size,
+ ULONGEST src_val, int src_size)
+{
+ for (int i = 0; i < 2 ; i++)
+ {
+ gdb_byte srcbuf[sizeof (ULONGEST)] = {};
+ gdb_byte destbuf[sizeof (ULONGEST)] = {};
+ enum bfd_endian byte_order = i ? BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE;
+
+ /* Fill the src buffer (and later the dest buffer) with non-zero junk,
+ to ensure zero extensions aren't hidden. */
+ memset (srcbuf, 0xaa, sizeof (srcbuf));
+
+ /* Store (and later extract) using unsigned to ensure there are no sign
+ extensions. */
+ store_unsigned_integer (srcbuf, src_size, byte_order, src_val);
+
+ /* Test unsigned. */
+ memset (destbuf, 0xaa, sizeof (destbuf));
+ copy_integer_to_size (destbuf, dest_size, srcbuf, src_size, false,
+ byte_order);
+ SELF_CHECK (dest_valu == extract_unsigned_integer (destbuf, dest_size,
+ byte_order));
+
+ /* Test signed. */
+ memset (destbuf, 0xaa, sizeof (destbuf));
+ copy_integer_to_size (destbuf, dest_size, srcbuf, src_size, true,
+ byte_order);
+ SELF_CHECK (dest_vals == extract_unsigned_integer (destbuf, dest_size,
+ byte_order));
+ }
+}
+
+static void
+copy_integer_to_size_test ()
+{
+ /* Destination is bigger than the source, which has the signed bit unset. */
+ do_cint_test (0x12345678, 0x12345678, 8, 0x12345678, 4);
+ do_cint_test (0x345678, 0x345678, 8, 0x12345678, 3);
+
+ /* Destination is bigger than the source, which has the signed bit set. */
+ do_cint_test (0xdeadbeef, 0xffffffffdeadbeef, 8, 0xdeadbeef, 4);
+ do_cint_test (0xadbeef, 0xffffffffffadbeef, 8, 0xdeadbeef, 3);
+
+ /* Destination is smaller than the source. */
+ do_cint_test (0x5678, 0x5678, 2, 0x12345678, 3);
+ do_cint_test (0xbeef, 0xbeef, 2, 0xdeadbeef, 3);
+
+ /* Destination and source are the same size. */
+ do_cint_test (0x8765432112345678, 0x8765432112345678, 8, 0x8765432112345678,
+ 8);
+ do_cint_test (0x432112345678, 0x432112345678, 6, 0x8765432112345678, 6);
+ do_cint_test (0xfeedbeaddeadbeef, 0xfeedbeaddeadbeef, 8, 0xfeedbeaddeadbeef,
+ 8);
+ do_cint_test (0xbeaddeadbeef, 0xbeaddeadbeef, 6, 0xfeedbeaddeadbeef, 6);
+
+ /* Destination is bigger than the source. Source is bigger than 32bits. */
+ do_cint_test (0x3412345678, 0x3412345678, 8, 0x3412345678, 6);
+ do_cint_test (0xff12345678, 0xff12345678, 8, 0xff12345678, 6);
+ do_cint_test (0x432112345678, 0x432112345678, 8, 0x8765432112345678, 6);
+ do_cint_test (0xff2112345678, 0xffffff2112345678, 8, 0xffffff2112345678, 6);
+}
+
+} // namespace selftests
+
+#endif
+
+void _initialize_extract_store_integer ();
+void
+_initialize_extract_store_integer ()
+{
+#if GDB_SELF_TEST
+ selftests::register_test ("copy_integer_to_size",
+ selftests::copy_integer_to_size_test);
+#endif
+}