Issue 260921.1: Signedness of DW_FORM_data values

Author: Simon Marchi
Champion:
Date submitted: 2026-09-21
Date revised:
Date closed:
Type: Enhancement
Status: Open
DWARF version: 6

Background

Given an attribute of form DW_FORM_data{1,2,4,8,16}, it is up to the consumer to figure out what the bytes mean. In particular, given an attribute where an integer is expected, the consumer must infer from the context whether the bytes encode a signed or unsigned integer. This is important if the provided value is smaller than the declared type (such as for a DW_TAG_variable with a DW_AT_const_value), as the consumer must decide if it should sign-extend or zero-extend the value. It is also important for attributes that do not have a declared type, like DW_AT_decimal_scale, simply to know how to interpret the value.

The problem is that "the context" is not defined. Different producers and consumers could independently reach different conclusions about when an attribute should be interpreted as signed or unsigned.

The spec says this about the DW_FORM_data<n> forms:

The data in DW_FORM_data1, DW_FORM_data2, DW_FORM_data4, DW_FORM_data8 and DW_FORM_data16 can be anything. Depending on context, it may be a signed integer, an unsigned integer, a floating-point constant, or anything else. A consumer must use context to know how to interpret the bits, which if they are target machine data (such as an integer or floating-point constant) will be in target machine byte order.

Then there is this non-normative text:

If one of the DW_FORM_data<n> forms is used to represent a signed or unsigned integer, it can be hard for a consumer to discover the context necessary to determine which interpretation is intended. Producers are therefore strongly encouraged to use DW_FORM_sdata or DW_FORM_udata for signed and unsigned integers respectively, rather than DW_FORM_data<n>.

I don't think that saying this in non-normative text is helpful. The reality is that producers do use the DW_FORM_data<n> forms for integers, so the consumers still have to know how to deal with it.

For example, given this:

0x00000037:   DW_TAG_enumeration_type
                DW_AT_name [DW_FORM_string]     ("E")
                DW_AT_encoding [DW_FORM_data1]  (DW_ATE_signed)
                DW_AT_byte_size [DW_FORM_data1] (0x04)
                DW_AT_type [DW_FORM_ref4]       (0x00000050 "int")

0x00000047:     DW_TAG_enumerator
                  DW_AT_name [DW_FORM_string]   ("X")
                  DW_AT_const_value [DW_FORM_data1]     (0xff)

Does 0xff mean 255 or -1?

And here?

0x00000037:   DW_TAG_enumeration_type
                DW_AT_name [DW_FORM_string]     ("E")
                DW_AT_encoding [DW_FORM_data1]  (DW_ATE_unsigned)
                DW_AT_byte_size [DW_FORM_data1] (0x04)
                DW_AT_type [DW_FORM_ref4]       (0x00000050 "unsigned int")

0x00000047:     DW_TAG_enumerator
                  DW_AT_name [DW_FORM_string]   ("X")
                  DW_AT_const_value [DW_FORM_data1]     (0xff)

Some people's intuition is that because the first enumeration has a signed type, then the const value should be sign-extended, unlike the second enumeration whose value should be zero-extended. Other people think that both values should always be zero-extended.

Then, there are some attributes like DW_AT_decimal_scale that don't even have an associated type, and can accept negative values. If you see a DW_AT_decimal_scale attribute with a "DW_FORM_data1 0xff" value, is it 255 or -1? I can't see what context a consumer could use to determine that.

There are two attributes that further specify the signedness of the value. DW_AT_discr_value says:

The value that selects a given variant may be represented in one of three ways. The variant entry may have a DW_AT_discr_value attribute whose value represents the discriminant value selecting this variant. The number is signed if the tag type for the variant part containing this variant is a signed type. The number is unsigned if the tag type is an unsigned type.

While DW_AT_bias says:

If the DW_AT_bias is encoded using DW_FORM_data<n>, then the bias value is treated as an unsigned integer.

History

The first time this problem was brought up was in issue 020702.1.

The outcome of this issue was to add the paragraphs quoted above. So the issue was "it's not clear how to interpret DW_FORM_data<n> integer values", and the outcome was to write in the standard "it's indeed not clear how to interpret DW_FORM_data<n> integer values". I don't think this really helped.

Then there was this thread on dwarf-discuss. This thread did not reach any particularly useful conclusion, but showed that people had different intuitions of how DW_FORM_data<n> should work.

Finally, there was this recent gcc bug, which shows that there is still some confusion about this.

The current state of things for gcc and LLVM (the only two producers I have access to) is that they'll generally use the DW_FORM_data<n> forms to represent non-negative numbers, and resort to DW_FORM_sdata for negative numbers. So, when applied to an integer context, they assume that the consumers will treat the DW_FORM_data<n> content as an unsigned integer (except for DW_AT_discr_value, because of how it's specified) and zero-extend the value if needed.

Proposal

Here are the high level ideas of this proposal (see below for the formal proposed changes).

Impact on producers:

Impact on consumers:

While not the primary goal, the introduction of the DW_FORM_sdata<n> forms will allow expressing negative numbers in a slightly more compact way than with DW_FORM_sdata, without any ambiguity.

Technically, some attributes with integer values could use the block forms (e.g. DW_AT_const_value), which would present the same ambiguity as the constant forms. I did not touch those, as I am not aware of a producer using those forms to encode integers, but we could change them in a similar way if we wanted to be thorough.

Alternatives

An alternative I considered is to get rid of DW_FORM_data<n>, and just use DW_FORM_{s,u}data for everything. This one would make the standard simpler and more uniform, which would be nice! But at the cost of making the debug info slightly larger, which is why I don't expect this option to be very popular. I modified gcc to do this, and it made the debug info of my test program (gdb) 0.66% larger. I am open to going this direction if people like it.

Proposed changes

The changes below are relative to the current DWARF Version 6 draft (dwarf-spec git commit ab16b53c56, "Incorporate changes from DWARF September 14, 2026 meeting.").

  1. In Section 2.2 ("Attribute Types"), Table 2.3 ("Classes of attribute value"), in the row for the constant class, remove the word "uninterpreted", resulting in:

    One, two, four, eight or sixteen bytes of data, or data encoded in the SLEB or ULEB variable length format (see Section 8.6).

  2. In Section 6.1.1.1 ("Simple Encodings"), remove this text:

    If the DW_AT_bias is encoded using DW_FORM_data<n>, then the bias value is treated as an unsigned integer.

  3. In Section 6.7.11 ("Variant Entries"), in the paragraph describing DW_AT_discr_value, remove this text:

    The number is signed if the tag type for the variant part containing this variant is a signed type. The number is unsigned if the tag type is an unsigned type.

  4. In Section 6.7.11 ("Variant Entries"), in the paragraph describing DW_AT_discr_list, remove this text:

    as defined above for the DW_AT_discr_value attribute

    Remove this text:

    Both values follow the rules for signedness just described.

    and add this text in its place:

    Values are signed if the tag type for the variant part containing this variant is a signed type, and unsigned if the tag type is an unsigned type.

  5. In Section 7.2.4.2 ("Producer-defined Content Descriptions"), in the list of forms that a producer-defined content type code may be combined with, add DW_FORM_sdata1, DW_FORM_sdata2, DW_FORM_sdata4, DW_FORM_sdata8 and DW_FORM_sdata16.

  6. In Section 7.3.1 ("Macro Information Header"), in the list of forms allowed for the operands of a producer extension entry opcode, add DW_FORM_sdata1, DW_FORM_sdata2, DW_FORM_sdata4, DW_FORM_sdata8 and DW_FORM_sdata16.

  7. In Section 8.5.5 ("Classes and Forms"), in the description of the constant class, remove this text:

    There are eight forms of constants. There are fixed length constant data forms for one-, two-, four-, eight- and sixteen-byte values (respectively, DW_FORM_data1, DW_FORM_data2, DW_FORM_data4, DW_FORM_data8 and DW_FORM_data16).

    and add this text in its place:

    There are thirteen forms of constants. There are fixed length constant data forms for one-, two-, four-, eight- and sixteen-byte values (respectively, DW_FORM_data1, DW_FORM_data2, DW_FORM_data4, DW_FORM_data8 and DW_FORM_data16), and signed fixed length constant data forms for the same sizes (respectively, DW_FORM_sdata1, DW_FORM_sdata2, DW_FORM_sdata4, DW_FORM_sdata8 and DW_FORM_sdata16).

    Remove this text:

    The data in DW_FORM_data1, DW_FORM_data2, DW_FORM_data4, DW_FORM_data8 and DW_FORM_data16 can be anything. Depending on context, it may be a signed integer, an unsigned integer, a floating-point constant, or anything else. A consumer must use context to know how to interpret the bits, which if they are target machine data (such as an integer or floating-point constant) will be in target machine byte order.

    [non-normative] If one of the DW_FORM_data<n> forms is used to represent a signed or unsigned integer, it can be hard for a consumer to discover the context necessary to determine which interpretation is intended. Producers are therefore strongly encouraged to use DW_FORM_sdata or DW_FORM_udata for signed and unsigned integers respectively, rather than DW_FORM_data<n>.

    and add this text in its place:

    The data in DW_FORM_data<n> can be anything. Depending on context, it may be an unsigned integer, a floating-point constant, or anything else. A consumer must use context to know how to interpret the bits, which if they are target machine data (such as an integer or floating-point constant) will be in target machine byte order. Where the context calls for an integer, the data is interpreted as an unsigned integer; if the value is used at a width larger than the form, it is zero-extended.

    The data in DW_FORM_sdata<n> is a signed integer, in target machine byte order. If the value is used at a width larger than the form, it is sign-extended.

  8. In Section 8.5.6 ("Form Encodings"), add these rows to Table 8.6 ("Attribute form encodings"):

    Form name          Value   Classes
    -------------------------------------
    DW_FORM_sdata1     0x30    constant
    DW_FORM_sdata2     0x31    constant
    DW_FORM_sdata4     0x32    constant
    DW_FORM_sdata8     0x33    constant
    DW_FORM_sdata16    0x34    constant