/* Copyright (C) 1986-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 . */ #include "extract-store-integer.h" #include "gdbtypes.h" #include "gdbarch.h" #include "gdbsupport/selftest.h" template T extract_integer (gdb::array_view buf, enum bfd_endian byte_order) { typename std::make_unsigned::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::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::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 (gdb::array_view buf, enum bfd_endian byte_order); template ULONGEST extract_integer (gdb::array_view 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 void store_integer (gdb::array_view 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 dst, bfd_endian byte_order, LONGEST val); template void store_integer (gdb::array_view 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 }