Skip to content

Commit 692252c

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. New in v2: renamed some constants and added a helper method, per Simon's review. New in v3: more renamings. Approved-By: Simon Marchi <[email protected]>
1 parent fcfd8a4 commit 692252c

File tree

7 files changed

+162
-18
lines changed

7 files changed

+162
-18
lines changed

gdb/dwarf2/read.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9999,7 +9999,7 @@ handle_member_location (struct die_info *die, struct dwarf2_cu *cu,
99999999
dlbaton->per_objfile = per_objfile;
1000010000
dlbaton->per_cu = cu->per_cu;
1000110001

10002-
field->set_loc_dwarf_block (dlbaton);
10002+
field->set_loc_dwarf_block_addr (dlbaton);
1000310003
}
1000410004
}
1000510005
else
@@ -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_block_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: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,10 @@ class StructMainTypePrettyPrinter:
164164
return "physaddr = 0x%x" % loc_val["physaddr"]
165165
elif loc_kind == "FIELD_LOC_KIND_PHYSNAME":
166166
return "physname = %s" % loc_val["physname"]
167-
elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK":
168-
return "dwarf_block = %s" % loc_val["dwarf_block"]
167+
elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK_ADDR":
168+
return "dwarf_block_addr = %s" % loc_val["dwarf_block"]
169+
elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK_BITPOS":
170+
return "dwarf_block_bitpos = %s" % loc_val["dwarf_block"]
169171
else:
170172
return "m_loc = ??? (unsupported m_loc_kind value)"
171173

gdb/gdbtypes.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,7 +2121,7 @@ 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 (!type->field (i).loc_is_dwarf_block ())
21252125
continue;
21262126
/* Do not consider C++ virtual base types to be dynamic
21272127
due to the field's offset being dynamic; these are
@@ -2723,7 +2723,7 @@ resolve_dynamic_field (struct field &field,
27232723
{
27242724
gdb_assert (!field.is_static ());
27252725

2726-
if (field.loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK)
2726+
if (field.loc_is_dwarf_block ())
27272727
{
27282728
dwarf2_locexpr_baton *field_loc
27292729
= field.loc_dwarf_block ();
@@ -2736,10 +2736,15 @@ resolve_dynamic_field (struct field &field,
27362736
prop.set_locexpr (&baton);
27372737

27382738
CORE_ADDR vals[1] = {addr_stack->addr};
2739-
CORE_ADDR addr;
2740-
if (dwarf2_evaluate_property (&prop, frame, addr_stack, &addr, vals))
2739+
CORE_ADDR addr_or_bitpos;
2740+
if (dwarf2_evaluate_property (&prop, frame, addr_stack,
2741+
&addr_or_bitpos, vals))
27412742
{
2742-
field.set_loc_bitpos (TARGET_CHAR_BIT * (addr - addr_stack->addr));
2743+
if (field.loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK_ADDR)
2744+
field.set_loc_bitpos (TARGET_CHAR_BIT
2745+
* (addr_or_bitpos - addr_stack->addr));
2746+
else
2747+
field.set_loc_bitpos (addr_or_bitpos);
27432748

27442749
if (field_loc->is_field_location)
27452750
{
@@ -4415,7 +4420,8 @@ check_types_equal (struct type *type1, struct type *type2,
44154420
field2->loc_physname ()))
44164421
return false;
44174422
break;
4418-
case FIELD_LOC_KIND_DWARF_BLOCK:
4423+
case FIELD_LOC_KIND_DWARF_BLOCK_ADDR:
4424+
case FIELD_LOC_KIND_DWARF_BLOCK_BITPOS:
44194425
{
44204426
struct dwarf2_locexpr_baton *block1, *block2;
44214427

@@ -5560,8 +5566,12 @@ copy_type_recursive (struct type *type, copied_types_hash_t &copied_types)
55605566
new_type->field (i).set_loc_physname
55615567
(xstrdup (type->field (i).loc_physname ()));
55625568
break;
5563-
case FIELD_LOC_KIND_DWARF_BLOCK:
5564-
new_type->field (i).set_loc_dwarf_block
5569+
case FIELD_LOC_KIND_DWARF_BLOCK_ADDR:
5570+
new_type->field (i).set_loc_dwarf_block_addr
5571+
(type->field (i).loc_dwarf_block ());
5572+
break;
5573+
case FIELD_LOC_KIND_DWARF_BLOCK_BITPOS:
5574+
new_type->field (i).set_loc_dwarf_block_bitpos
55655575
(type->field (i).loc_dwarf_block ());
55665576
break;
55675577
default:

gdb/gdbtypes.h

Lines changed: 20 additions & 4 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+
/* A DWARF block that computes the address of the field. */
484+
FIELD_LOC_KIND_DWARF_BLOCK_ADDR, /**< dwarf_block */
485+
/* A DWARF block that computes the bit offset of the field. */
486+
FIELD_LOC_KIND_DWARF_BLOCK_BITPOS,
484487
};
485488

486489
/* * A discriminant to determine which field in the
@@ -616,6 +619,13 @@ struct field
616619
return m_loc_kind;
617620
}
618621

622+
/* Return true if this location has either "DWARF block" kind. */
623+
bool loc_is_dwarf_block () const
624+
{
625+
return (m_loc_kind == FIELD_LOC_KIND_DWARF_BLOCK_ADDR
626+
|| m_loc_kind == FIELD_LOC_KIND_DWARF_BLOCK_BITPOS);
627+
}
628+
619629
LONGEST loc_bitpos () const
620630
{
621631
gdb_assert (m_loc_kind == FIELD_LOC_KIND_BITPOS);
@@ -666,13 +676,19 @@ struct field
666676

667677
dwarf2_locexpr_baton *loc_dwarf_block () const
668678
{
669-
gdb_assert (m_loc_kind == FIELD_LOC_KIND_DWARF_BLOCK);
679+
gdb_assert (loc_is_dwarf_block ());
670680
return m_loc.dwarf_block;
671681
}
672682

673-
void set_loc_dwarf_block (dwarf2_locexpr_baton *dwarf_block)
683+
void set_loc_dwarf_block_addr (dwarf2_locexpr_baton *dwarf_block)
684+
{
685+
m_loc_kind = FIELD_LOC_KIND_DWARF_BLOCK_ADDR;
686+
m_loc.dwarf_block = dwarf_block;
687+
}
688+
689+
void set_loc_dwarf_block_bitpos (dwarf2_locexpr_baton *dwarf_block)
674690
{
675-
m_loc_kind = FIELD_LOC_KIND_DWARF_BLOCK;
691+
m_loc_kind = FIELD_LOC_KIND_DWARF_BLOCK_BITPOS;
676692
m_loc.dwarf_block = dwarf_block;
677693
}
678694

gdb/gnu-v3-abi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ gnuv3_baseclass_offset (struct type *type, int index,
476476
return TYPE_BASECLASS_BITPOS (type, index) / 8;
477477

478478
/* If we have a DWARF expression for the offset, evaluate it. */
479-
if (type->field (index).loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK)
479+
if (type->field (index).loc_kind () == FIELD_LOC_KIND_DWARF_BLOCK_ADDR)
480480
{
481481
struct dwarf2_property_baton baton;
482482
baton.property_type

gdb/python/py-type.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ 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 (type->field (field).loc_is_dwarf_block ())
162162
arg = gdbpy_ref<>::new_reference (Py_None);
163163
else
164164
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)