# Signedness of DW_FORM_data values 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` 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` 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`. I don't think that saying this in non-normative text is helpful. The reality is that producers do use the `DW_FORM_data` 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`, then the bias value is > treated as an unsigned integer. History ------- The first time this problem was brought up was in issue [020702.1][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` integer values", and the outcome was to write in the standard "it's indeed not clear how to interpret `DW_FORM_data` integer values". I don't think this really helped. Then there was [this thread][1] on dwarf-discuss. This thread did not reach any particularly useful conclusion, but showed that people had different intuitions of how `DW_FORM_data` should work. [1]: https://lists.dwarfstd.org/pipermail/dwarf-discuss/2010-July/000862.html Finally, there was this recent [gcc bug][pr118837], which shows that there is still some confusion about this. [pr118837]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118837 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` 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` 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). - Define the `DW_FORM_data` forms such that when they are used to provide an integer value, they encode an unsigned integer. The `DW_FORM_data` can still be used to provide non-integer data. - Add new `DW_FORM_sdata{1,2,4,8,16}` forms, which are defined to encode a signed integer. - Remove the part about `DW_AT_discr_value` that specifies that the value is unsigned or signed depending on the type of the discriminant. The signedness of the LEB values in `DW_AT_discr_list` would still depend on the discriminant type's signedness. If we want to avoid this, we could introduce signed and unsigned versions of `DW_DSC_label` and `DW_DSC_range`. This would make it entirely possible to interpret the values without having to peek at another DIE. - Remove the sentence in the `DW_AT_bias` description saying that a `DW_FORM_data` bias value is treated as an unsigned integer. Impact on producers: - Producers that use `DW_FORM_data` to convey non-negative values don't need to change anything. - Producers that use `DW_FORM_data` to convey negative values (including for `DW_AT_discr_value`) would need to switch to the corresponding `DW_FORM_sdata` form. - In both cases, they don't need to change the data bytes they emit, just the form. Impact on consumers: - The `DW_AT_discr_value` change would be a breaking change, likely requiring a DWARF version check in the consumers. However, the result is a uniform behavior of `DW_FORM_data` across all attributes, so I think it's worth it. While not the primary goal, the introduction of the `DW_FORM_sdata` 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`, 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`, 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` 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`. and add this text in its place: > The data in `DW_FORM_data` 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` 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 [020702.1]: https://dwarfstd.org/issues/020702.1.html