Skip to content

Commit ebe1ad8

Browse files
Merge pull request #10591 from swiftlang/lldb/unnamed-bitfields-to-6.2
[lldb][DWARFASTParserClang] Prevent unnamed bitfield creation in the presence of overlapping fields
2 parents aa2d35d + 374b6f2 commit ebe1ad8

File tree

4 files changed

+222
-42
lines changed

4 files changed

+222
-42
lines changed

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3024,7 +3024,6 @@ void DWARFASTParserClang::ParseSingleMember(
30243024
}
30253025

30263026
const uint64_t character_width = 8;
3027-
const uint64_t word_width = 32;
30283027
CompilerType member_clang_type = member_type->GetLayoutCompilerType();
30293028

30303029
const auto accessibility = attrs.accessibility == eAccessNone
@@ -3092,52 +3091,29 @@ void DWARFASTParserClang::ParseSingleMember(
30923091
detect_unnamed_bitfields =
30933092
die.GetCU()->Supports_unnamed_objc_bitfields();
30943093

3095-
if (detect_unnamed_bitfields) {
3096-
std::optional<FieldInfo> unnamed_field_info;
3097-
uint64_t last_field_end =
3098-
last_field_info.bit_offset + last_field_info.bit_size;
3099-
3100-
if (!last_field_info.IsBitfield()) {
3101-
// The last field was not a bit-field...
3102-
// but if it did take up the entire word then we need to extend
3103-
// last_field_end so the bit-field does not step into the last
3104-
// fields padding.
3105-
if (last_field_end != 0 && ((last_field_end % word_width) != 0))
3106-
last_field_end += word_width - (last_field_end % word_width);
3107-
}
3108-
3109-
if (ShouldCreateUnnamedBitfield(last_field_info, last_field_end,
3110-
this_field_info, layout_info)) {
3111-
unnamed_field_info = FieldInfo{};
3112-
unnamed_field_info->bit_size =
3113-
this_field_info.bit_offset - last_field_end;
3114-
unnamed_field_info->bit_offset = last_field_end;
3115-
}
3116-
3117-
if (unnamed_field_info) {
3118-
clang::FieldDecl *unnamed_bitfield_decl =
3119-
TypeSystemClang::AddFieldToRecordType(
3120-
class_clang_type, llvm::StringRef(),
3121-
m_ast.GetBuiltinTypeForEncodingAndBitSize(eEncodingSint,
3122-
word_width),
3123-
accessibility, unnamed_field_info->bit_size);
3124-
3125-
layout_info.field_offsets.insert(std::make_pair(
3126-
unnamed_bitfield_decl, unnamed_field_info->bit_offset));
3127-
}
3128-
}
3094+
if (detect_unnamed_bitfields)
3095+
AddUnnamedBitfieldToRecordTypeIfNeeded(layout_info, class_clang_type,
3096+
last_field_info, this_field_info);
31293097

31303098
last_field_info = this_field_info;
31313099
last_field_info.SetIsBitfield(true);
31323100
} else {
3133-
last_field_info.bit_offset = field_bit_offset;
3101+
FieldInfo this_field_info;
3102+
this_field_info.is_bitfield = false;
3103+
this_field_info.bit_offset = field_bit_offset;
31343104

3105+
// TODO: we shouldn't silently ignore the bit_size if we fail
3106+
// to GetByteSize.
31353107
if (std::optional<uint64_t> clang_type_size =
31363108
llvm::expectedToOptional(member_type->GetByteSize(nullptr))) {
3137-
last_field_info.bit_size = *clang_type_size * character_width;
3109+
this_field_info.bit_size = *clang_type_size * character_width;
31383110
}
31393111

3140-
last_field_info.SetIsBitfield(false);
3112+
if (this_field_info.GetFieldEnd() <= last_field_info.GetEffectiveFieldEnd())
3113+
this_field_info.SetEffectiveFieldEnd(
3114+
last_field_info.GetEffectiveFieldEnd());
3115+
3116+
last_field_info = this_field_info;
31413117
}
31423118

31433119
// Don't turn artificial members such as vtable pointers into real FieldDecls
@@ -3937,6 +3913,43 @@ bool DWARFASTParserClang::ShouldCreateUnnamedBitfield(
39373913
return true;
39383914
}
39393915

3916+
void DWARFASTParserClang::AddUnnamedBitfieldToRecordTypeIfNeeded(
3917+
ClangASTImporter::LayoutInfo &class_layout_info,
3918+
const CompilerType &class_clang_type, const FieldInfo &previous_field,
3919+
const FieldInfo &current_field) {
3920+
// TODO: get this value from target
3921+
const uint64_t word_width = 32;
3922+
uint64_t last_field_end = previous_field.GetEffectiveFieldEnd();
3923+
3924+
if (!previous_field.IsBitfield()) {
3925+
// The last field was not a bit-field...
3926+
// but if it did take up the entire word then we need to extend
3927+
// last_field_end so the bit-field does not step into the last
3928+
// fields padding.
3929+
if (last_field_end != 0 && ((last_field_end % word_width) != 0))
3930+
last_field_end += word_width - (last_field_end % word_width);
3931+
}
3932+
3933+
// Nothing to be done.
3934+
if (!ShouldCreateUnnamedBitfield(previous_field, last_field_end,
3935+
current_field, class_layout_info))
3936+
return;
3937+
3938+
// Place the unnamed bitfield into the gap between the previous field's end
3939+
// and the current field's start.
3940+
const uint64_t unnamed_bit_size = current_field.bit_offset - last_field_end;
3941+
const uint64_t unnamed_bit_offset = last_field_end;
3942+
3943+
clang::FieldDecl *unnamed_bitfield_decl =
3944+
TypeSystemClang::AddFieldToRecordType(
3945+
class_clang_type, llvm::StringRef(),
3946+
m_ast.GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, word_width),
3947+
lldb::AccessType::eAccessPublic, unnamed_bit_size);
3948+
3949+
class_layout_info.field_offsets.insert(
3950+
std::make_pair(unnamed_bitfield_decl, unnamed_bit_offset));
3951+
}
3952+
39403953
void DWARFASTParserClang::ParseRustVariantPart(
39413954
DWARFDIE &die, const DWARFDIE &parent_die, CompilerType &class_clang_type,
39423955
const lldb::AccessType default_accesibility,

lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,15 +270,33 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
270270

271271
private:
272272
struct FieldInfo {
273+
/// Size in bits that this field occupies. Can but
274+
/// need not be the DW_AT_bit_size of the field.
273275
uint64_t bit_size = 0;
276+
277+
/// Offset of this field in bits from the beginning
278+
/// of the containing struct. Can but need not
279+
/// be the DW_AT_data_bit_offset of the field.
274280
uint64_t bit_offset = 0;
281+
282+
/// In case this field is folded into the storage
283+
/// of a previous member's storage (for example
284+
/// with [[no_unique_address]]), the effective field
285+
/// end is the offset in bits from the beginning of
286+
/// the containing struct where the field we were
287+
/// folded into ended.
288+
std::optional<uint64_t> effective_field_end;
289+
290+
/// Set to 'true' if this field is a bit-field.
275291
bool is_bitfield = false;
292+
293+
/// Set to 'true' if this field is DW_AT_artificial.
276294
bool is_artificial = false;
277295

278296
FieldInfo() = default;
279297

280298
void SetIsBitfield(bool flag) { is_bitfield = flag; }
281-
bool IsBitfield() { return is_bitfield; }
299+
bool IsBitfield() const { return is_bitfield; }
282300

283301
void SetIsArtificial(bool flag) { is_artificial = flag; }
284302
bool IsArtificial() const { return is_artificial; }
@@ -288,6 +306,19 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
288306
// bit offset than any previous bitfield + size.
289307
return (bit_size + bit_offset) <= next_bit_offset;
290308
}
309+
310+
/// Returns the offset in bits of where the storage this field
311+
/// occupies ends.
312+
uint64_t GetFieldEnd() const { return bit_size + bit_offset; }
313+
314+
void SetEffectiveFieldEnd(uint64_t val) { effective_field_end = val; }
315+
316+
/// If this field was folded into storage of a previous field,
317+
/// returns the offset in bits of where that storage ends. Otherwise,
318+
/// returns the regular field end (see \ref GetFieldEnd).
319+
uint64_t GetEffectiveFieldEnd() const {
320+
return effective_field_end.value_or(GetFieldEnd());
321+
}
291322
};
292323

293324
/// Parsed form of all attributes that are relevant for parsing type members.
@@ -330,6 +361,35 @@ class DWARFASTParserClang : public lldb_private::plugin::dwarf::DWARFASTParser {
330361
FieldInfo const &this_field_info,
331362
lldb_private::ClangASTImporter::LayoutInfo const &layout_info) const;
332363

364+
/// Tries to detect whether \ref class_clang_type contained an unnamed
365+
/// bit-field between \ref previous_field and \ref current_field, and if
366+
/// so, adds a clang::FieldDecl representing that bit-field to
367+
/// \ref class_clang_type.
368+
///
369+
/// This is necessary because Clang (and GCC) doesn't emit a DW_TAG_member
370+
/// entry for unnamed bit-fields. So we derive it (with some exceptions),
371+
/// by checking whether there is a gap between where the storage of a
372+
/// DW_TAG_member ended and the subsequent DW_TAG_member began.
373+
///
374+
/// \param[in,out] layout_info Layout information of all decls parsed by the
375+
/// current parser. Will contain an entry for
376+
/// the unnamed bit-field if this function created
377+
/// one.
378+
///
379+
/// \param[in] class_clang_type The RecordType to which the unnamed bit-field
380+
/// will be added (if any).
381+
///
382+
/// \param[in] previous_field FieldInfo of the previous DW_TAG_member
383+
/// we parsed.
384+
///
385+
/// \param[in] current_field FieldInfo of the current DW_TAG_member
386+
/// being parsed.
387+
///
388+
void AddUnnamedBitfieldToRecordTypeIfNeeded(
389+
lldb_private::ClangASTImporter::LayoutInfo &class_layout_info,
390+
const lldb_private::CompilerType &class_clang_type,
391+
const FieldInfo &previous_field, const FieldInfo &current_field);
392+
333393
/// Parses a DW_TAG_APPLE_property DIE and appends the parsed data to the
334394
/// list of delayed Objective-C properties.
335395
///

lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/string/TestDataFormatterLibcxxStringSimulator.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@
1313
class LibcxxStringDataFormatterSimulatorTestCase(TestBase):
1414
NO_DEBUG_INFO_TESTCASE = True
1515

16-
@skipIfDarwin
17-
@skipIfWindows
18-
@skipIfLinux
1916
def _run_test(self, defines):
2017
cxxflags_extras = " ".join(["-D%s" % d for d in defines])
2118
self.build(dictionary=dict(CXXFLAGS_EXTRAS=cxxflags_extras))
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// RUN: %clang --target=x86_64-apple-macosx -c -gdwarf -o %t %s
2+
// RUN: %lldb %t \
3+
// RUN: -o "target var global" \
4+
// RUN: -o "target var global2" \
5+
// RUN: -o "target var global3" \
6+
// RUN: -o "target var global4" \
7+
// RUN: -o "target var global5" \
8+
// RUN: -o "image dump ast" \
9+
// RUN: -o exit | FileCheck %s
10+
11+
// CHECK: (lldb) image dump ast
12+
// CHECK: CXXRecordDecl {{.*}} struct Foo definition
13+
// CHECK: |-FieldDecl {{.*}} data 'char[5]'
14+
// CHECK-NEXT: |-FieldDecl {{.*}} padding 'Empty'
15+
// CHECK-NEXT: `-FieldDecl {{.*}} flag 'unsigned long'
16+
// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1
17+
18+
struct Empty {};
19+
struct Empty2 {};
20+
struct Empty3 {};
21+
22+
struct Foo {
23+
char data[5];
24+
[[no_unique_address]] Empty padding;
25+
unsigned long flag : 1;
26+
};
27+
28+
Foo global;
29+
30+
// CHECK: CXXRecordDecl {{.*}} struct ConsecutiveOverlap definition
31+
// CHECK: |-FieldDecl {{.*}} data 'char[5]'
32+
// CHECK-NEXT: |-FieldDecl {{.*}} p1 'Empty'
33+
// CHECK-NEXT: |-FieldDecl {{.*}} p2 'Empty2'
34+
// CHECK-NEXT: |-FieldDecl {{.*}} p3 'Empty3'
35+
// CHECK-NEXT: `-FieldDecl {{.*}} flag 'unsigned long'
36+
// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1
37+
38+
struct ConsecutiveOverlap {
39+
char data[5];
40+
[[no_unique_address]] Empty p1;
41+
[[no_unique_address]] Empty2 p2;
42+
[[no_unique_address]] Empty3 p3;
43+
unsigned long flag : 1;
44+
};
45+
46+
ConsecutiveOverlap global2;
47+
48+
// FIXME: we fail to deduce the unnamed bitfields here.
49+
//
50+
// CHECK: CXXRecordDecl {{.*}} struct MultipleAtOffsetZero definition
51+
// CHECK: |-FieldDecl {{.*}} data 'char[5]'
52+
// CHECK-NEXT: |-FieldDecl {{.*}} p1 'Empty'
53+
// CHECK-NEXT: |-FieldDecl {{.*}} f1 'unsigned long'
54+
// CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 1
55+
// CHECK-NEXT: |-FieldDecl {{.*}} p2 'Empty2'
56+
// CHECK-NEXT: `-FieldDecl {{.*}} f2 'unsigned long'
57+
// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1
58+
59+
struct MultipleAtOffsetZero {
60+
char data[5];
61+
[[no_unique_address]] Empty p1;
62+
int : 4;
63+
unsigned long f1 : 1;
64+
[[no_unique_address]] Empty2 p2;
65+
int : 4;
66+
unsigned long f2 : 1;
67+
};
68+
69+
MultipleAtOffsetZero global3;
70+
71+
// FIXME: we fail to deduce the unnamed bitfields here.
72+
//
73+
// CHECK: CXXRecordDecl {{.*}} struct MultipleEmpty definition
74+
// CHECK: |-FieldDecl {{.*}} data 'char[5]'
75+
// CHECK-NEXT: |-FieldDecl {{.*}} p1 'Empty'
76+
// CHECK-NEXT: |-FieldDecl {{.*}} f1 'unsigned long'
77+
// CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 1
78+
// CHECK-NEXT: |-FieldDecl {{.*}} p2 'Empty'
79+
// CHECK-NEXT: `-FieldDecl {{.*}} f2 'unsigned long'
80+
// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1
81+
82+
struct MultipleEmpty {
83+
char data[5];
84+
[[no_unique_address]] Empty p1;
85+
int : 4;
86+
unsigned long f1 : 1;
87+
[[no_unique_address]] Empty p2;
88+
int : 4;
89+
unsigned long f2 : 1;
90+
};
91+
92+
MultipleEmpty global4;
93+
94+
// CHECK: CXXRecordDecl {{.*}} struct FieldBitfieldOverlap definition
95+
// CHECK: |-FieldDecl {{.*}} a 'int'
96+
// CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 3
97+
// CHECK-NEXT: |-FieldDecl {{.*}} p1 'Empty'
98+
// CHECK-NEXT: |-FieldDecl {{.*}} b 'int'
99+
// CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 6
100+
// CHECK-NEXT: `-FieldDecl {{.*}} c 'int'
101+
// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1
102+
103+
struct FieldBitfieldOverlap {
104+
int a : 3;
105+
[[no_unique_address]] Empty p1;
106+
int b : 6;
107+
int c : 1;
108+
};
109+
110+
FieldBitfieldOverlap global5;

0 commit comments

Comments
 (0)