Skip to content

Commit a0d3f06

Browse files
committed
Handle dynamic DW_AT_data_bit_offset
In Ada, a field can have a dynamic bit offset in its enclosing record. In DWARF 3, this was handled using a dynamic DW_AT_data_member_location, combined with a DW_AT_bit_offset -- this combination worked out ok because in practice GNAT only needs a dynamic byte offset with a fixed offset within the byte. However, this approach was deprecated in DWARF 4 and then removed in DWARF 5. No replacement approach was given, meaning that in strict mode there is no way to express this. This is a DWARF bug, see https://dwarfstd.org/issues/250501.1.html In a discussion on the DWARF mailing list, a couple people mentioned that compilers could use the obvious extension of a dynamic DW_AT_data_bit_offset. I've implemented this for LLVM: llvm/llvm-project#141106 In preparation for that landing, this patch implements support for this construct in gdb.
1 parent d61186d commit a0d3f06

File tree

6 files changed

+149
-7
lines changed

6 files changed

+149
-7
lines changed

gdb/dwarf2/read.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10010,7 +10010,28 @@ handle_member_location (struct die_info *die, struct dwarf2_cu *cu,
1001010010
{
1001110011
attr = dwarf2_attr (die, DW_AT_data_bit_offset, cu);
1001210012
if (attr != nullptr)
10013-
field->set_loc_bitpos (attr->unsigned_constant ().value_or (0));
10013+
{
10014+
if (attr->form_is_constant ())
10015+
field->set_loc_bitpos (attr->unsigned_constant ().value_or (0));
10016+
else if (attr->form_is_block ())
10017+
{
10018+
/* This is a DWARF extension. See
10019+
https://dwarfstd.org/issues/250501.1.html. */
10020+
dwarf2_per_objfile *per_objfile = cu->per_objfile;
10021+
dwarf2_locexpr_baton *dlbaton
10022+
= OBSTACK_ZALLOC (&per_objfile->objfile->objfile_obstack,
10023+
dwarf2_locexpr_baton);
10024+
dlbaton->data = attr->as_block ()->data;
10025+
dlbaton->size = attr->as_block ()->size;
10026+
dlbaton->per_objfile = per_objfile;
10027+
dlbaton->per_cu = cu->per_cu;
10028+
10029+
field->set_loc_dwarf_bitpos (dlbaton);
10030+
}
10031+
else
10032+
complaint (_("Unsupported form %s for DW_AT_data_bit_offset"),
10033+
dwarf_form_name (attr->form));
10034+
}
1001410035
}
1001510036
}
1001610037

gdb/gdb-gdb.py.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ class StructMainTypePrettyPrinter:
166166
return "physname = %s" % loc_val["physname"]
167167
elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK":
168168
return "dwarf_block = %s" % loc_val["dwarf_block"]
169+
elif loc_kind == "FIELD_LOC_KIND_DWARF_BITPOS":
170+
return "bitpos = %s" % loc_val["dwarf_block"]
169171
else:
170172
return "m_loc = ??? (unsupported m_loc_kind value)"
171173

gdb/gdbtypes.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,7 +2121,9 @@ is_dynamic_type_internal (struct type *type, bool top_level)
21212121
return true;
21222122
/* If the field is at a fixed offset, then it is not
21232123
dynamic. */
2124-
if (type->field (i).loc_kind () != FIELD_LOC_KIND_DWARF_BLOCK)
2124+
if (field_loc_kind kind = type->field (i).loc_kind ();
2125+
kind != FIELD_LOC_KIND_DWARF_BLOCK
2126+
&& kind != FIELD_LOC_KIND_DWARF_BITPOS)
21252127
continue;
21262128
/* Do not consider C++ virtual base types to be dynamic
21272129
due to the field's offset being dynamic; these are
@@ -2723,7 +2725,9 @@ resolve_dynamic_field (struct field &field,
27232725
{
27242726
gdb_assert (!field.is_static ());
27252727

2726-
if (field.loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK)
2728+
if (field_loc_kind loc_kind = field.loc_kind ();
2729+
loc_kind == FIELD_LOC_KIND_DWARF_BLOCK
2730+
|| loc_kind == FIELD_LOC_KIND_DWARF_BITPOS)
27272731
{
27282732
dwarf2_locexpr_baton *field_loc
27292733
= field.loc_dwarf_block ();
@@ -2739,7 +2743,10 @@ resolve_dynamic_field (struct field &field,
27392743
CORE_ADDR addr;
27402744
if (dwarf2_evaluate_property (&prop, frame, addr_stack, &addr, vals))
27412745
{
2742-
field.set_loc_bitpos (TARGET_CHAR_BIT * (addr - addr_stack->addr));
2746+
if (loc_kind == FIELD_LOC_KIND_DWARF_BLOCK)
2747+
field.set_loc_bitpos (TARGET_CHAR_BIT * (addr - addr_stack->addr));
2748+
else
2749+
field.set_loc_bitpos (addr);
27432750

27442751
if (field_loc->is_field_location)
27452752
{
@@ -4416,6 +4423,7 @@ check_types_equal (struct type *type1, struct type *type2,
44164423
return false;
44174424
break;
44184425
case FIELD_LOC_KIND_DWARF_BLOCK:
4426+
case FIELD_LOC_KIND_DWARF_BITPOS:
44194427
{
44204428
struct dwarf2_locexpr_baton *block1, *block2;
44214429

@@ -5564,6 +5572,10 @@ copy_type_recursive (struct type *type, copied_types_hash_t &copied_types)
55645572
new_type->field (i).set_loc_dwarf_block
55655573
(type->field (i).loc_dwarf_block ());
55665574
break;
5575+
case FIELD_LOC_KIND_DWARF_BITPOS:
5576+
new_type->field (i).set_loc_dwarf_bitpos
5577+
(type->field (i).loc_dwarf_block ());
5578+
break;
55675579
default:
55685580
internal_error (_("Unexpected type field location kind: %d"),
55695581
type->field (i).loc_kind ());

gdb/gdbtypes.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,10 @@ enum field_loc_kind
480480
FIELD_LOC_KIND_ENUMVAL, /**< enumval */
481481
FIELD_LOC_KIND_PHYSADDR, /**< physaddr */
482482
FIELD_LOC_KIND_PHYSNAME, /**< physname */
483-
FIELD_LOC_KIND_DWARF_BLOCK /**< dwarf_block */
483+
FIELD_LOC_KIND_DWARF_BLOCK, /**< dwarf_block */
484+
/* Like FIELD_LOC_KIND_DWARF_BLOCK, but it computes a bit offset,
485+
not a byte offset. */
486+
FIELD_LOC_KIND_DWARF_BITPOS,
484487
};
485488

486489
/* * A discriminant to determine which field in the
@@ -666,7 +669,8 @@ struct field
666669

667670
dwarf2_locexpr_baton *loc_dwarf_block () const
668671
{
669-
gdb_assert (m_loc_kind == FIELD_LOC_KIND_DWARF_BLOCK);
672+
gdb_assert (m_loc_kind == FIELD_LOC_KIND_DWARF_BLOCK
673+
|| m_loc_kind == FIELD_LOC_KIND_DWARF_BITPOS);
670674
return m_loc.dwarf_block;
671675
}
672676

@@ -676,6 +680,12 @@ struct field
676680
m_loc.dwarf_block = dwarf_block;
677681
}
678682

683+
void set_loc_dwarf_bitpos (dwarf2_locexpr_baton *dwarf_block)
684+
{
685+
m_loc_kind = FIELD_LOC_KIND_DWARF_BITPOS;
686+
m_loc.dwarf_block = dwarf_block;
687+
}
688+
679689
/* Set the field's accessibility. */
680690
void set_accessibility (accessibility acc)
681691
{ m_accessibility = acc; }

gdb/python/py-type.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,9 @@ convert_field (struct type *type, int field)
158158
}
159159
else
160160
{
161-
if (type->field (field).loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK)
161+
if (field_loc_kind kind = type->field (field).loc_kind ();
162+
kind == FIELD_LOC_KIND_DWARF_BLOCK
163+
|| kind == FIELD_LOC_KIND_DWARF_BITPOS)
162164
arg = gdbpy_ref<>::new_reference (Py_None);
163165
else
164166
arg = gdb_py_object_from_longest (type->field (field).loc_bitpos ());
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Copyright 2025 Free Software Foundation, Inc.
2+
3+
# This program is free software; you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License as published by
5+
# the Free Software Foundation; either version 3 of the License, or
6+
# (at your option) any later version.
7+
#
8+
# This program is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
# GNU General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU General Public License
14+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
16+
# Test DW_AT_data_bit_offset with an expression. This is a DWARF
17+
# extension, but expected to be in DWARF 6. See
18+
# https://dwarfstd.org/issues/250501.1.html
19+
20+
load_lib dwarf.exp
21+
22+
# This test can only be run on targets which support DWARF-2 and use gas.
23+
require dwarf2_support
24+
25+
standard_testfile ada-array-bound.c -debug.S
26+
27+
# Set up the DWARF for the test.
28+
29+
set asm_file [standard_output_file $srcfile2]
30+
Dwarf::assemble $asm_file {
31+
global srcdir subdir srcfile
32+
33+
cu {} {
34+
DW_TAG_compile_unit {
35+
{DW_AT_language @DW_LANG_Ada95}
36+
{DW_AT_name $srcfile}
37+
} {
38+
declare_labels byte array struct
39+
40+
byte: DW_TAG_base_type {
41+
{DW_AT_byte_size 1 DW_FORM_sdata}
42+
{DW_AT_encoding @DW_ATE_unsigned}
43+
{DW_AT_name byte}
44+
}
45+
46+
array: DW_TAG_array_type {
47+
{DW_AT_name array_type}
48+
{DW_AT_type :$byte}
49+
} {
50+
DW_TAG_subrange_type {
51+
{DW_AT_type :$byte}
52+
{DW_AT_upper_bound 3 DW_FORM_sdata}
53+
}
54+
}
55+
56+
struct: DW_TAG_structure_type {
57+
{DW_AT_name discriminated}
58+
{DW_AT_byte_size 4 DW_FORM_sdata}
59+
} {
60+
DW_TAG_member {
61+
{DW_AT_name disc}
62+
{DW_AT_type :$byte}
63+
{DW_AT_data_member_location 0 DW_FORM_sdata}
64+
}
65+
66+
# We know this is always at offset 1 but use an
67+
# expression just to test this code path. This is a
68+
# DWARF extension. See
69+
# https://dwarfstd.org/issues/250501.1.html.
70+
DW_TAG_member {
71+
{DW_AT_name nums}
72+
{DW_AT_type :$array}
73+
{DW_AT_data_bit_offset {DW_OP_lit8} SPECIAL_expr}
74+
}
75+
}
76+
77+
DW_TAG_variable {
78+
{DW_AT_name "value"}
79+
{DW_AT_type :$struct}
80+
{DW_AT_external 1 DW_FORM_flag}
81+
{DW_AT_location {DW_OP_addr [gdb_target_symbol "our_data"]}
82+
SPECIAL_expr}
83+
}
84+
}
85+
}
86+
}
87+
88+
if {[prepare_for_testing "failed to prepare" ${testfile} \
89+
[list $srcfile $asm_file] {nodebug}]} {
90+
return -1
91+
}
92+
93+
gdb_test_no_output "set language ada"
94+
gdb_test "print value" \
95+
[string_to_regexp " = (disc => 3, nums => (7, 11, 13))"]

0 commit comments

Comments
 (0)