Browse Source
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>master
committed by
Simon Marchi
141 changed files with 559 additions and 327 deletions
@ -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 |
|||
} |
|||
@ -0,0 +1,111 @@ |
|||
/* 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/>. */
|
|||
|
|||
#ifndef GDB_EXTRACT_STORE_INTEGER_H |
|||
#define GDB_EXTRACT_STORE_INTEGER_H |
|||
|
|||
template<typename T, typename = RequireLongest<T>> |
|||
T extract_integer (gdb::array_view<const gdb_byte>, enum bfd_endian byte_order); |
|||
|
|||
static inline LONGEST |
|||
extract_signed_integer (gdb::array_view<const gdb_byte> buf, |
|||
enum bfd_endian byte_order) |
|||
{ |
|||
return extract_integer<LONGEST> (buf, byte_order); |
|||
} |
|||
|
|||
static inline LONGEST |
|||
extract_signed_integer (const gdb_byte *addr, int len, |
|||
enum bfd_endian byte_order) |
|||
{ |
|||
return extract_signed_integer (gdb::array_view<const gdb_byte> (addr, len), |
|||
byte_order); |
|||
} |
|||
|
|||
static inline ULONGEST |
|||
extract_unsigned_integer (gdb::array_view<const gdb_byte> buf, |
|||
enum bfd_endian byte_order) |
|||
{ |
|||
return extract_integer<ULONGEST> (buf, byte_order); |
|||
} |
|||
|
|||
static inline ULONGEST |
|||
extract_unsigned_integer (const gdb_byte *addr, int len, |
|||
enum bfd_endian byte_order) |
|||
{ |
|||
return extract_unsigned_integer (gdb::array_view<const gdb_byte> (addr, len), |
|||
byte_order); |
|||
} |
|||
|
|||
extern int extract_long_unsigned_integer (const gdb_byte *, int, |
|||
enum bfd_endian, LONGEST *); |
|||
|
|||
extern CORE_ADDR extract_typed_address (const gdb_byte *buf, |
|||
struct type *type); |
|||
|
|||
/* 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 = RequireLongest<T>> |
|||
extern void store_integer (gdb::array_view<gdb_byte> dst, |
|||
bfd_endian byte_order, T val); |
|||
|
|||
template<typename T> |
|||
static inline void |
|||
store_integer (gdb_byte *addr, int len, bfd_endian byte_order, T val) |
|||
{ |
|||
return store_integer (gdb::make_array_view (addr, len), byte_order, val); |
|||
} |
|||
|
|||
static inline void |
|||
store_signed_integer (gdb::array_view<gdb_byte> dst, bfd_endian byte_order, |
|||
LONGEST val) |
|||
{ |
|||
return store_integer (dst, byte_order, val); |
|||
} |
|||
|
|||
static inline void |
|||
store_signed_integer (gdb_byte *addr, int len, bfd_endian byte_order, |
|||
LONGEST val) |
|||
{ |
|||
return store_signed_integer (gdb::make_array_view (addr, len), byte_order, |
|||
val); |
|||
} |
|||
|
|||
static inline void |
|||
store_unsigned_integer (gdb::array_view<gdb_byte> dst, bfd_endian byte_order, |
|||
ULONGEST val) |
|||
{ |
|||
return store_integer (dst, byte_order, val); |
|||
} |
|||
|
|||
static inline void |
|||
store_unsigned_integer (gdb_byte *addr, int len, bfd_endian byte_order, |
|||
ULONGEST val) |
|||
{ |
|||
return store_unsigned_integer (gdb::make_array_view (addr, len), byte_order, |
|||
val); |
|||
} |
|||
|
|||
extern void store_typed_address (gdb_byte *buf, struct type *type, |
|||
CORE_ADDR addr); |
|||
|
|||
extern 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); |
|||
|
|||
#endif /* GDB_EXTRACT_STORE_INTEGER_H */ |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue