From 6eb63917ce17236f0189e8d7ff4b60e24741770b Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Wed, 27 Mar 2024 10:43:57 -0600 Subject: Handle "info symbol" in Rust language mode When I changed the Rust parser to handle 128-bit ints, this inadvertently broke some other gdb commands. For example, "info symbol 0xffffffffffffffff" now fails, because the resulting value is 128 bits, but this is rejected by extract_integer. This patch fixes the problem by changing extract_integer to allow over-long integers as long as the high bytes are either 0, or (for signed types) 0xff. Regression tested on x86-64 Fedora 38. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31565 Approved-By: Andrew Burgess --- gdb/extract-store-integer.c | 99 ++++++++++++++++++++++++++++++++++++++- gdb/extract-store-integer.h | 4 +- gdb/testsuite/gdb.rust/simple.exp | 3 ++ 3 files changed, 102 insertions(+), 4 deletions(-) diff --git a/gdb/extract-store-integer.c b/gdb/extract-store-integer.c index b2892e4..644273c 100644 --- a/gdb/extract-store-integer.c +++ b/gdb/extract-store-integer.c @@ -26,9 +26,61 @@ extract_integer (gdb::array_view buf, enum bfd_endian byte_order { typename std::make_unsigned::type retval = 0; + /* It is ok if BUF is wider than T, but only if the value is + representable. */ + bool bad_repr = false; if (buf.size () > (int) sizeof (T)) - error (_("\ -That operation is not available on integers of more than %d bytes."), + { + const size_t end = buf.size () - sizeof (T); + if (byte_order == BFD_ENDIAN_BIG) + { + for (size_t i = 0; i < end; ++i) + { + /* High bytes == 0 are always ok, and high bytes == 0xff + are ok when the type is signed. */ + if ((buf[i] == 0 + || (std::is_signed::value && buf[i] == 0xff)) + /* All the high bytes must be the same, no + alternating 0 and 0xff. */ + && (i == 0 || buf[i - 1] == buf[i])) + { + /* Ok. */ + } + else + { + bad_repr = true; + break; + } + } + buf = buf.slice (end); + } + else + { + size_t bufsz = buf.size () - 1; + for (size_t i = bufsz; i >= end; --i) + { + /* High bytes == 0 are always ok, and high bytes == 0xff + are ok when the type is signed. */ + if ((buf[i] == 0 + || (std::is_signed::value && buf[i] == 0xff)) + /* All the high bytes must be the same, no + alternating 0 and 0xff. */ + && (i == bufsz || buf[i] == buf[i + 1])) + { + /* Ok. */ + } + else + { + bad_repr = true; + break; + } + } + buf = buf.slice (0, end); + } + } + + if (bad_repr) + error (_("Value cannot be represented as integer of %d bytes."), (int) sizeof (T)); /* Start at the most significant end of the integer, and work towards @@ -240,6 +292,47 @@ copy_integer_to_size_test () do_cint_test (0xff2112345678, 0xffffff2112345678, 8, 0xffffff2112345678, 6); } +template +void +do_extract_test (gdb_byte byte1, gdb_byte byte2, enum bfd_endian endian, + std::optional expected) +{ + std::optional result; + + try + { + const gdb_byte val[2] = { byte1, byte2 }; + result = extract_integer (gdb::make_array_view (val, 2), endian); + } + catch (const gdb_exception_error &) + { + } + + SELF_CHECK (result == expected); +} + +template +void +do_extract_tests (gdb_byte low, gdb_byte high, std::optional expected) +{ + do_extract_test (low, high, BFD_ENDIAN_LITTLE, expected); + do_extract_test (high, low, BFD_ENDIAN_BIG, expected); +} + +static void +extract_integer_test () +{ + do_extract_tests (0x00, 0xff, {}); + do_extract_tests (0x7f, 0x23, {}); + do_extract_tests (0x80, 0xff, {}); + do_extract_tests (0x00, 0x00, 0x00); + + do_extract_tests (0xff, 0x00, 0xff); + do_extract_tests (0x7f, 0x23, {}); + do_extract_tests (0x80, 0xff, 0x80); + do_extract_tests (0x00, 0x00, 0x00); +} + } // namespace selftests #endif @@ -251,5 +344,7 @@ _initialize_extract_store_integer () #if GDB_SELF_TEST selftests::register_test ("copy_integer_to_size", selftests::copy_integer_to_size_test); + selftests::register_test ("extract_integer", + selftests::extract_integer_test); #endif } diff --git a/gdb/extract-store-integer.h b/gdb/extract-store-integer.h index b2c0f35..a51ef3d 100644 --- a/gdb/extract-store-integer.h +++ b/gdb/extract-store-integer.h @@ -18,9 +18,9 @@ #ifndef GDB_EXTRACT_STORE_INTEGER_H #define GDB_EXTRACT_STORE_INTEGER_H -#include "gdbsupport/traits.h" +#include -template> +template> T extract_integer (gdb::array_view, enum bfd_endian byte_order); static inline LONGEST diff --git a/gdb/testsuite/gdb.rust/simple.exp b/gdb/testsuite/gdb.rust/simple.exp index 1e6fc94..37a2e07 100644 --- a/gdb/testsuite/gdb.rust/simple.exp +++ b/gdb/testsuite/gdb.rust/simple.exp @@ -421,3 +421,6 @@ gdb_test "print 4 - 3 - 1" " = 0" # Another operator precedence bug. gdb_test "print \$one = \$two = 75" " = \\\(\\\)" + +gdb_test "info symbol 0xffffffffffffffff" \ + "No symbol matches 0xffffffffffffffff." -- cgit v1.1