diff options
-rw-r--r-- | gdb/ChangeLog | 17 | ||||
-rw-r--r-- | gdb/defs.h | 5 | ||||
-rw-r--r-- | gdb/findvar.c | 128 | ||||
-rw-r--r-- | gdb/mips-fbsd-tdep.c | 45 | ||||
-rw-r--r-- | gdb/mips-linux-tdep.c | 33 | ||||
-rw-r--r-- | gdb/regcache.c | 48 | ||||
-rw-r--r-- | gdb/regcache.h | 6 |
7 files changed, 213 insertions, 69 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 8454c09..fd52229 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,20 @@ +2017-05-26 Alan Hayward <alan.hayward@arm.com> + + * defs.h (copy_integer_to_size): New declaration. + * findvar.c (copy_integer_to_size): New function. + (do_cint_test): New selftest function. + (copy_integer_to_size_test): Likewise. + (_initialize_findvar): Likewise. + * mips-fbsd-tdep.c (mips_fbsd_supply_reg): Use raw_supply_integer. + (mips_fbsd_collect_reg): Use raw_collect_integer. + * mips-linux-tdep.c (supply_32bit_reg): Use raw_supply_integer. + (mips64_fill_gregset): Use raw_collect_integer + (mips64_fill_fpregset): Use raw_supply_integer. + * regcache.c (regcache::raw_supply_integer): New function. + (regcache::raw_collect_integer): Likewise. + * regcache.h: (regcache::raw_supply_integer): New declaration. + (regcache::raw_collect_integer): Likewise. + 2017-05-24 Yao Qi <yao.qi@linaro.org> * Makefile.in (SFILES): Add gdbarch-selftests.c. @@ -658,7 +658,10 @@ extern void store_unsigned_integer (gdb_byte *, int, 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); + /* From valops.c */ extern int watchdog; diff --git a/gdb/findvar.c b/gdb/findvar.c index ed4d5c1..6c18e25 100644 --- a/gdb/findvar.c +++ b/gdb/findvar.c @@ -33,6 +33,7 @@ #include "objfiles.h" #include "language.h" #include "dwarf2loc.h" +#include "selftest.h" /* Basic byte-swapping routines. All 'extract' functions return a host-format integer from a target-format integer at ADDR which is @@ -249,7 +250,46 @@ store_typed_address (gdb_byte *buf, struct type *type, CORE_ADDR addr) gdbarch_address_to_pointer (get_type_arch (type), 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 + endianess 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); + } +} /* Return a `value' with the contents of (virtual or cooked) register REGNUM as found in the specified FRAME. The register's type is @@ -1005,3 +1045,91 @@ address_from_register (int regnum, struct frame_info *frame) return result; } +#if GDB_SELF_TEST +namespace selftests { +namespace findvar_tests { + +/* 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 findvar_test +} // namespace selftests + +#endif + +void +_initialize_findvar (void) +{ +#if GDB_SELF_TEST + register_self_test (selftests::findvar_tests::copy_integer_to_size_test); +#endif +} diff --git a/gdb/mips-fbsd-tdep.c b/gdb/mips-fbsd-tdep.c index d5bec3c..44b960d 100644 --- a/gdb/mips-fbsd-tdep.c +++ b/gdb/mips-fbsd-tdep.c @@ -42,57 +42,24 @@ 34th is a dummy for padding. */ #define MIPS_FBSD_NUM_FPREGS 34 -/* Supply a single register. If the source register size matches the - size the regcache expects, this can use regcache_raw_supply(). If - they are different, this copies the source register into a buffer - that can be passed to regcache_raw_supply(). */ +/* Supply a single register. The register size might not match, so use + regcache->raw_supply_integer (). */ static void mips_fbsd_supply_reg (struct regcache *regcache, int regnum, const void *addr, size_t len) { - struct gdbarch *gdbarch = get_regcache_arch (regcache); - - if (register_size (gdbarch, regnum) == len) - regcache_raw_supply (regcache, regnum, addr); - else - { - enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); - gdb_byte buf[MAX_REGISTER_SIZE]; - LONGEST val; - - val = extract_signed_integer ((const gdb_byte *) addr, len, byte_order); - store_signed_integer (buf, register_size (gdbarch, regnum), byte_order, - val); - regcache_raw_supply (regcache, regnum, buf); - } + regcache->raw_supply_integer (regnum, (const gdb_byte *) addr, len, true); } -/* Collect a single register. If the destination register size - matches the size the regcache expects, this can use - regcache_raw_supply(). If they are different, this fetches the - register via regcache_raw_supply() into a buffer and then copies it - into the final destination. */ +/* Collect a single register. The register size might not match, so use + regcache->raw_collect_integer (). */ static void mips_fbsd_collect_reg (const struct regcache *regcache, int regnum, void *addr, size_t len) { - struct gdbarch *gdbarch = get_regcache_arch (regcache); - - if (register_size (gdbarch, regnum) == len) - regcache_raw_collect (regcache, regnum, addr); - else - { - enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); - gdb_byte buf[MAX_REGISTER_SIZE]; - LONGEST val; - - regcache_raw_collect (regcache, regnum, buf); - val = extract_signed_integer (buf, register_size (gdbarch, regnum), - byte_order); - store_signed_integer ((gdb_byte *) addr, len, byte_order, val); - } + regcache->raw_collect_integer (regnum, (gdb_byte *) addr, len, true); } /* Supply the floating-point registers stored in FPREGS to REGCACHE. diff --git a/gdb/mips-linux-tdep.c b/gdb/mips-linux-tdep.c index 48a582a..ccfdcdf 100644 --- a/gdb/mips-linux-tdep.c +++ b/gdb/mips-linux-tdep.c @@ -116,13 +116,7 @@ mips_linux_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc) static void supply_32bit_reg (struct regcache *regcache, int regnum, const void *addr) { - struct gdbarch *gdbarch = get_regcache_arch (regcache); - enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); - gdb_byte buf[MAX_REGISTER_SIZE]; - store_signed_integer (buf, register_size (gdbarch, regnum), byte_order, - extract_signed_integer ((const gdb_byte *) addr, 4, - byte_order)); - regcache_raw_supply (regcache, regnum, buf); + regcache->raw_supply_integer (regnum, (const gdb_byte *) addr, 4, true); } /* Unpack an elf_gregset_t into GDB's register cache. */ @@ -417,7 +411,6 @@ mips64_fill_gregset (const struct regcache *regcache, mips64_elf_gregset_t *gregsetp, int regno) { struct gdbarch *gdbarch = get_regcache_arch (regcache); - enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); int regaddr, regi; mips64_elf_greg_t *regp = *gregsetp; void *dst; @@ -460,14 +453,8 @@ mips64_fill_gregset (const struct regcache *regcache, if (regaddr != -1) { - gdb_byte buf[MAX_REGISTER_SIZE]; - LONGEST val; - - regcache_raw_collect (regcache, regno, buf); - val = extract_signed_integer (buf, register_size (gdbarch, regno), - byte_order); dst = regp + regaddr; - store_signed_integer ((gdb_byte *) dst, 8, byte_order, val); + regcache->raw_collect_integer (regno, (gdb_byte *) dst, 8, true); } } @@ -564,25 +551,13 @@ mips64_fill_fpregset (const struct regcache *regcache, } else if (regno == mips_regnum (gdbarch)->fp_control_status) { - gdb_byte buf[MAX_REGISTER_SIZE]; - LONGEST val; - - regcache_raw_collect (regcache, regno, buf); - val = extract_signed_integer (buf, register_size (gdbarch, regno), - byte_order); to = (gdb_byte *) (*fpregsetp + 32); - store_signed_integer (to, 4, byte_order, val); + regcache->raw_collect_integer (regno, to, 4, true); } else if (regno == mips_regnum (gdbarch)->fp_implementation_revision) { - gdb_byte buf[MAX_REGISTER_SIZE]; - LONGEST val; - - regcache_raw_collect (regcache, regno, buf); - val = extract_signed_integer (buf, register_size (gdbarch, regno), - byte_order); to = (gdb_byte *) (*fpregsetp + 32) + 4; - store_signed_integer (to, 4, byte_order, val); + regcache->raw_collect_integer (regno, to, 4, true); } else if (regno == -1) { diff --git a/gdb/regcache.c b/gdb/regcache.c index 7638eea..3798f0a 100644 --- a/gdb/regcache.c +++ b/gdb/regcache.c @@ -1179,6 +1179,31 @@ regcache::raw_supply (int regnum, const void *buf) } } +/* Supply register REGNUM to REGCACHE. Value to supply is an integer stored at + address ADDR, in target endian, with length ADDR_LEN and sign IS_SIGNED. If + the register size is greater than ADDR_LEN, then the integer will be sign or + zero extended. If the register size is smaller than the integer, then the + most significant bytes of the integer will be truncated. */ + +void +regcache::raw_supply_integer (int regnum, const gdb_byte *addr, int addr_len, + bool is_signed) +{ + enum bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch); + gdb_byte *regbuf; + size_t regsize; + + gdb_assert (regnum >= 0 && regnum < m_descr->nr_raw_registers); + gdb_assert (!m_readonly_p); + + regbuf = register_buffer (regnum); + regsize = m_descr->sizeof_register[regnum]; + + copy_integer_to_size (regbuf, regsize, addr, addr_len, is_signed, + byte_order); + m_register_status[regnum] = REG_VALID; +} + /* Supply register REGNUM with zeroed value to REGCACHE. This is not the same as calling raw_supply with NULL (which will set the state to unavailable). */ @@ -1226,6 +1251,29 @@ regcache::raw_collect (int regnum, void *buf) const set to or from a buffer. This is the main worker function for regcache_supply_regset and regcache_collect_regset. */ +/* Collect register REGNUM from REGCACHE. Store collected value as an integer + at address ADDR, in target endian, with length ADDR_LEN and sign IS_SIGNED. + If ADDR_LEN is greater than the register size, then the integer will be sign + or zero extended. If ADDR_LEN is smaller than the register size, then the + most significant bytes of the integer will be truncated. */ + +void +regcache::raw_collect_integer (int regnum, gdb_byte *addr, int addr_len, + bool is_signed) const +{ + enum bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch); + const gdb_byte *regbuf; + size_t regsize; + + gdb_assert (regnum >= 0 && regnum < m_descr->nr_raw_registers); + + regbuf = register_buffer (regnum); + regsize = m_descr->sizeof_register[regnum]; + + copy_integer_to_size (addr, addr_len, regbuf, regsize, is_signed, + byte_order); +} + void regcache::transfer_regset (const struct regset *regset, struct regcache *out_regcache, diff --git a/gdb/regcache.h b/gdb/regcache.h index 11126e6..4cf27a0 100644 --- a/gdb/regcache.h +++ b/gdb/regcache.h @@ -304,8 +304,14 @@ public: void raw_collect (int regnum, void *buf) const; + void raw_collect_integer (int regnum, gdb_byte *addr, int addr_len, + bool is_signed) const; + void raw_supply (int regnum, const void *buf); + void raw_supply_integer (int regnum, const gdb_byte *addr, int addr_len, + bool is_signed); + void raw_supply_zeroed (int regnum); enum register_status get_register_status (int regnum) const; |