[Bug gdb/34369] New: [gdb] value_as_mpz does not sign-extend integers whose DW_AT_bit_size is smaller than their storage size

firmiana402 at gmail dot com via Gdb-prs <[email protected]>
Newsgroups gmane.comp.gdb.bugs.discuss
Message-ID <[email protected]/bugzilla/>
https://sourceware.org/bugzilla/show_bug.cgi?id=34369

            Bug ID: 34369
           Summary: [gdb] value_as_mpz does not sign-extend integers whose
                    DW_AT_bit_size is smaller than their storage size
           Product: gdb
           Version: HEAD
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: gdb
          Assignee: unassigned at sourceware dot org
          Reporter: firmiana402 at gmail dot com
  Target Milestone: ---

For a signed base type whose logical bit size is smaller than
its storage size (DW_AT_bit_size differs from 8 * byte_size —
e.g. GCC's _BitInt(31), stored in 4 bytes), value_as_mpz reads
the value at the full storage width and only masks off the high
bits; it never sign-extends at the logical bit size. A negative
such value is therefore read back as a large positive number.

The relevant code is in value_as_mpz (gdb/value.c):

    result.read (val->contents (), byte_order, type->is_unsigned ());

    /* Shift off any low bits, if needed.  */
    if (bit_off != 0)
      result >>= bit_off;

    /* Mask off any high bits, if needed.  */
    if (bit_size)
      result.mask (bit_size);

gdb_mpz::read interprets the sign using the full buffer width
(its sign bit is at buf.size () * 8 - 1, i.e. bit 31 for a 4-byte
type), and mask (bit_size) only clears bits at and above
bit_size. Nothing sign-extends from the logical sign bit
(bit_size - 1). For a signed _BitInt(31) holding raw 0x7fffffff
— which is -1 in 31-bit two's complement — value_as_mpz returns
+2147483647.

GDB's other integer read path handles this correctly:
value_as_long goes through unpack_long, which routes
bit_size_differs_p types to unpack_bits_as_long (gdb/value.c).
That helper masks and then sign-extends at the logical bit size
for signed types:

    if (bitsize < 8 * (int) sizeof (val))
      {
        valmask = (((ULONGEST) 1) << bitsize) - 1;
        val &= valmask;
        if (!field_type->is_unsigned ())
          {
            if (val & (valmask ^ (valmask >> 1)))
              val |= ~valmask;
          }
      }

So value_as_mpz is inconsistent with value_as_long / unpack_long
for exactly these types: the byte-based path sign-extends, the
mpz path does not.

## Impact

value_as_mpz is how the typed DWARF integer binary operators read
their operands: scalar_binop (gdb/valarith.c) does
gdb_mpz v1 = value_as_mpz (arg1); gdb_mpz v2 = value_as_mpz
(arg2); before computing. So arithmetic and comparisons on a
negative narrow-bit signed value — for instance a _BitInt(31)
produced by DW_OP_convert — are evaluated with a wrong, large
positive operand, which changes the result.

-- 
You are receiving this mail because:
You are on the CC list for the bug.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.