diff options
author | Simon Marchi <simon.marchi@efficios.com> | 2024-04-22 16:10:14 -0400 |
---|---|---|
committer | Simon Marchi <simon.marchi@polymtl.ca> | 2024-04-22 21:34:19 -0400 |
commit | ec4525259262492b61fb9bd2f9acdf156ab037fc (patch) | |
tree | 73e3cf9b3f4049db32037f6ede09abd6dd08ec71 /gdb/extract-store-integer.c | |
parent | 15afb100ea6bc04d8944a919bf58c5c3fb8c20ec (diff) | |
download | binutils-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.c | 308 |
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 +} |