shafik created this revision.
shafik added reviewers: aprantl, teemperor, jingham.
We ran into an assert when debugging clang and performing an expression on a
class derived from `DeclContext`. The assert was indicating we were getting the
offsets wrong for `RecordDeclBitfields`. We were getting both the size and
offset of unnamed bit-field members wrong. We could fix this case with a quick
change but as I extended the test suite to include more combinations we kept
finding more cases that were being handled incorrectly. A fix that handled all
the new cases as well as the cases already covered required a refactor of the
existing technique.
I removed a duplicate of `BitfieldInfo` and renamed `BitfieldInfo` ->
`FieldInfo` since it is being used for both bit-fields and non-bit-fields. I
extended `TestBitfields.py` to cover five more cases we uncovered while fixing
this bug.
https://reviews.llvm.org/D72953
Files:
lldb/packages/Python/lldbsuite/test/lang/c/bitfields/TestBitfields.py
lldb/packages/Python/lldbsuite/test/lang/c/bitfields/main.c
lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
@@ -170,11 +170,11 @@
lldb::ModuleSP GetModuleForType(const DWARFDIE &die);
private:
- struct BitfieldInfo {
+ struct FieldInfo {
uint64_t bit_size;
uint64_t bit_offset;
- BitfieldInfo()
+ FieldInfo()
: bit_size(LLDB_INVALID_ADDRESS), bit_offset(LLDB_INVALID_ADDRESS) {}
void Clear() {
@@ -194,7 +194,7 @@
// + size.
return (bit_size + bit_offset) <= next_bit_offset;
} else {
- // If the this BitfieldInfo is not valid, then any offset isOK
+ // If the this FieldInfo is not valid, then any offset isOK
return true;
}
}
@@ -208,7 +208,7 @@
lldb::AccessType &default_accessibility,
DelayedPropertyList &delayed_properties,
lldb_private::ClangASTImporter::LayoutInfo &layout_info,
- BitfieldInfo &last_field_info);
+ FieldInfo &last_bitfield_info, FieldInfo &last_field_info);
bool CompleteRecordType(const DWARFDIE &die, lldb_private::Type *type,
lldb_private::CompilerType &clang_type);
Index: lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
===================================================================
--- lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -85,35 +85,6 @@
return false;
}
-struct BitfieldInfo {
- uint64_t bit_size;
- uint64_t bit_offset;
-
- BitfieldInfo()
- : bit_size(LLDB_INVALID_ADDRESS), bit_offset(LLDB_INVALID_ADDRESS) {}
-
- void Clear() {
- bit_size = LLDB_INVALID_ADDRESS;
- bit_offset = LLDB_INVALID_ADDRESS;
- }
-
- bool IsValid() const {
- return (bit_size != LLDB_INVALID_ADDRESS) &&
- (bit_offset != LLDB_INVALID_ADDRESS);
- }
-
- bool NextBitfieldOffsetIsValid(const uint64_t next_bit_offset) const {
- if (IsValid()) {
- // This bitfield info is valid, so any subsequent bitfields must not
- // overlap and must be at a higher bit offset than any previous bitfield
- // + size.
- return (bit_size + bit_offset) <= next_bit_offset;
- } else {
- // If the this BitfieldInfo is not valid, then any offset isOK
- return true;
- }
- }
-};
ClangASTImporter &DWARFASTParserClang::GetClangASTImporter() {
if (!m_clang_ast_importer_up) {
@@ -2419,7 +2390,7 @@
lldb::AccessType &default_accessibility,
DelayedPropertyList &delayed_properties,
lldb_private::ClangASTImporter::LayoutInfo &layout_info,
- BitfieldInfo &last_field_info) {
+ FieldInfo &last_bitfield_info, FieldInfo &last_field_info) {
ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule();
const dw_tag_t tag = die.Tag();
// Get the parent byte size so we can verify any members will fit
@@ -2453,6 +2424,14 @@
const dw_attr_t attr = attributes.AttributeAtIndex(i);
DWARFFormValue form_value;
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+ // AT_data_member_location indicates the byte offset of the
+ // word from the base address of the structure.
+ //
+ // AT_bit_offset indicates how many bits into the word
+ // (according to the host endianness) the low-order bit of the
+ // field starts. AT_bit_offset can be negative.
+ //
+ // AT_bit_size indicates the size of the field in bits.
switch (attr) {
case DW_AT_name:
name = form_value.AsCString();
@@ -2603,35 +2582,24 @@
Type *member_type = die.ResolveTypeUID(encoding_form.Reference());
clang::FieldDecl *field_decl = nullptr;
+ const uint64_t character_width = 8;
+ const uint64_t word_width = 32;
if (tag == DW_TAG_member) {
if (member_type) {
+ CompilerType member_clang_type = member_type->GetLayoutCompilerType();
+
if (accessibility == eAccessNone)
accessibility = default_accessibility;
member_accessibilities.push_back(accessibility);
uint64_t field_bit_offset =
(member_byte_offset == UINT32_MAX ? 0 : (member_byte_offset * 8));
- if (bit_size > 0) {
- BitfieldInfo this_field_info;
+ if (bit_size > 0) {
+ FieldInfo this_field_info;
this_field_info.bit_offset = field_bit_offset;
this_field_info.bit_size = bit_size;
- /////////////////////////////////////////////////////////////
- // How to locate a field given the DWARF debug information
- //
- // AT_byte_size indicates the size of the word in which the bit
- // offset must be interpreted.
- //
- // AT_data_member_location indicates the byte offset of the
- // word from the base address of the structure.
- //
- // AT_bit_offset indicates how many bits into the word
- // (according to the host endianness) the low-order bit of the
- // field starts. AT_bit_offset can be negative.
- //
- // AT_bit_size indicates the size of the field in bits.
- /////////////////////////////////////////////////////////////
if (data_bit_offset != UINT64_MAX) {
this_field_info.bit_offset = data_bit_offset;
@@ -2649,7 +2617,7 @@
}
if ((this_field_info.bit_offset >= parent_bit_size) ||
- !last_field_info.NextBitfieldOffsetIsValid(
+ !last_bitfield_info.NextBitfieldOffsetIsValid(
this_field_info.bit_offset)) {
ObjectFile *objfile = die.GetDWARF()->GetObjectFile();
objfile->GetModule()->ReportWarning(
@@ -2666,33 +2634,6 @@
// Update the field bit offset we will report for layout
field_bit_offset = this_field_info.bit_offset;
- // If the member to be emitted did not start on a character
- // boundary and there is empty space between the last field and
- // this one, then we need to emit an anonymous member filling
- // up the space up to its start. There are three cases here:
- //
- // 1 If the previous member ended on a character boundary, then
- // we can emit an
- // anonymous member starting at the most recent character
- // boundary.
- //
- // 2 If the previous member did not end on a character boundary
- // and the distance
- // from the end of the previous member to the current member
- // is less than a
- // word width, then we can emit an anonymous member starting
- // right after the
- // previous member and right before this member.
- //
- // 3 If the previous member did not end on a character boundary
- // and the distance
- // from the end of the previous member to the current member
- // is greater than
- // or equal a word width, then we act as in Case 1.
-
- const uint64_t character_width = 8;
- const uint64_t word_width = 32;
-
// Objective-C has invalid DW_AT_bit_offset values in older
// versions of clang, so we have to be careful and only insert
// unnamed bitfields if we have a new enough clang.
@@ -2704,53 +2645,64 @@
die.GetCU()->Supports_unnamed_objc_bitfields();
if (detect_unnamed_bitfields) {
- BitfieldInfo anon_field_info;
-
- if ((this_field_info.bit_offset % character_width) !=
- 0) // not char aligned
- {
- uint64_t last_field_end = 0;
-
- if (last_field_info.IsValid())
- last_field_end =
- last_field_info.bit_offset + last_field_info.bit_size;
-
- if (this_field_info.bit_offset != last_field_end) {
- if (((last_field_end % character_width) == 0) || // case 1
- (this_field_info.bit_offset - last_field_end >=
- word_width)) // case 3
- {
- anon_field_info.bit_size =
- this_field_info.bit_offset % character_width;
- anon_field_info.bit_offset =
- this_field_info.bit_offset - anon_field_info.bit_size;
- } else // case 2
- {
- anon_field_info.bit_size =
- this_field_info.bit_offset - last_field_end;
- anon_field_info.bit_offset = last_field_end;
- }
- }
+ FieldInfo unnamed_field_info;
+ uint64_t last_field_end = 0;
+
+ // The last field was not a bit-field...
+ if (last_field_info.IsValid()) {
+ last_field_end =
+ last_field_info.bit_offset + last_field_info.bit_size;
+
+ // but if it did take up the entire word then we need to extend
+ // last_field_end so the bit-field does not step into the last
+ // fields padding.
+ if (last_field_end != 0 && ((last_field_end % word_width) != 0))
+ last_field_end += word_width - (last_field_end % word_width);
+ } else if (last_bitfield_info.IsValid())
+ last_field_end =
+ last_bitfield_info.bit_offset + last_bitfield_info.bit_size;
+
+ // If we have a gap between the last_field_end and the current
+ // field we have an unnamed bit-field
+ if (this_field_info.bit_offset != last_field_end &&
+ !(this_field_info.bit_offset < last_field_end)) {
+ unnamed_field_info.bit_size =
+ this_field_info.bit_offset - last_field_end;
+ unnamed_field_info.bit_offset = last_field_end;
}
- if (anon_field_info.IsValid()) {
+ if (unnamed_field_info.IsValid()) {
+ if (data_bit_offset != UINT64_MAX)
+ field_bit_offset = data_bit_offset;
+ else
+ field_bit_offset = unnamed_field_info.bit_offset +
+ unnamed_field_info.bit_size;
+
clang::FieldDecl *unnamed_bitfield_decl =
ClangASTContext::AddFieldToRecordType(
class_clang_type, llvm::StringRef(),
m_ast.GetBuiltinTypeForEncodingAndBitSize(eEncodingSint,
word_width),
- accessibility, anon_field_info.bit_size);
+ accessibility, unnamed_field_info.bit_size);
layout_info.field_offsets.insert(std::make_pair(
- unnamed_bitfield_decl, anon_field_info.bit_offset));
- }
+ unnamed_bitfield_decl, unnamed_field_info.bit_offset));
+ } else
+ field_bit_offset = this_field_info.bit_offset;
}
- last_field_info = this_field_info;
- } else {
+
last_field_info.Clear();
+ last_bitfield_info = this_field_info;
+ } else {
+ last_bitfield_info.Clear();
+ last_field_info.bit_offset = field_bit_offset;
+
+ if (llvm::Optional<uint64_t> clang_type_size =
+ member_clang_type.GetByteSize(nullptr)) {
+ last_field_info.bit_size = *clang_type_size * character_width;
+ }
}
- CompilerType member_clang_type = member_type->GetLayoutCompilerType();
if (!member_clang_type.IsCompleteType())
member_clang_type.GetCompleteType();
@@ -2835,7 +2787,6 @@
bit_size);
m_ast.SetMetadataAsUserID(field_decl, die.GetID());
-
layout_info.field_offsets.insert(
std::make_pair(field_decl, field_bit_offset));
} else {
@@ -2885,7 +2836,10 @@
if (!parent_die)
return false;
- BitfieldInfo last_field_info;
+ FieldInfo last_bitfield_info;
+ FieldInfo last_field_info;
+ last_field_info.bit_offset = 0;
+ last_field_info.bit_size = 0;
ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule();
ClangASTContext *ast =
@@ -2902,7 +2856,8 @@
case DW_TAG_APPLE_property:
ParseSingleMember(die, parent_die, class_clang_type, class_language,
member_accessibilities, default_accessibility,
- delayed_properties, layout_info, last_field_info);
+ delayed_properties, layout_info, last_bitfield_info,
+ last_field_info);
break;
case DW_TAG_subprogram:
Index: lldb/packages/Python/lldbsuite/test/lang/c/bitfields/main.c
===================================================================
--- lldb/packages/Python/lldbsuite/test/lang/c/bitfields/main.c
+++ lldb/packages/Python/lldbsuite/test/lang/c/bitfields/main.c
@@ -9,11 +9,9 @@
#include <stdio.h>
#include <string.h>
-int main (int argc, char const *argv[])
-{
- struct Bits
- {
- uint32_t : 1, // Unnamed bitfield
+int main(int argc, char const *argv[]) {
+ struct Bits {
+ uint32_t : 1, // Unnamed bitfield
b1 : 1,
b2 : 2,
: 2, // Unnamed bitfield
@@ -24,80 +22,122 @@
b6 : 6,
b7 : 7,
four : 4;
- };
+ };
- printf("%lu", sizeof(struct Bits));
-
- struct Bits bits;
- int i;
- for (i=0; i<(1<<1); i++)
- bits.b1 = i; //// break $source:$line
- for (i=0; i<(1<<2); i++)
- bits.b2 = i; //// break $source:$line
- for (i=0; i<(1<<3); i++)
- bits.b3 = i; //// break $source:$line
- for (i=0; i<(1<<4); i++)
- bits.b4 = i; //// break $source:$line
- for (i=0; i<(1<<5); i++)
- bits.b5 = i; //// break $source:$line
- for (i=0; i<(1<<6); i++)
- bits.b6 = i; //// break $source:$line
- for (i=0; i<(1<<7); i++)
- bits.b7 = i; //// break $source:$line
- for (i=0; i<(1<<4); i++)
- bits.four = i; //// break $source:$line
-
- struct MoreBits
- {
- uint32_t a : 3;
- uint8_t : 1;
- uint8_t b : 1;
- uint8_t c : 1;
- uint8_t d : 1;
- };
+ printf("%lu", sizeof(struct Bits));
- struct MoreBits more_bits;
+ struct Bits bits;
+ int i;
+ for (i = 0; i < (1 << 1); i++)
+ bits.b1 = i; //// break $source:$line
+ for (i = 0; i < (1 << 2); i++)
+ bits.b2 = i; //// break $source:$line
+ for (i = 0; i < (1 << 3); i++)
+ bits.b3 = i; //// break $source:$line
+ for (i = 0; i < (1 << 4); i++)
+ bits.b4 = i; //// break $source:$line
+ for (i = 0; i < (1 << 5); i++)
+ bits.b5 = i; //// break $source:$line
+ for (i = 0; i < (1 << 6); i++)
+ bits.b6 = i; //// break $source:$line
+ for (i = 0; i < (1 << 7); i++)
+ bits.b7 = i; //// break $source:$line
+ for (i = 0; i < (1 << 4); i++)
+ bits.four = i; //// break $source:$line
- more_bits.a = 3;
- more_bits.b = 0;
- more_bits.c = 1;
- more_bits.d = 0;
+ struct MoreBits {
+ uint32_t a : 3;
+ uint8_t : 1;
+ uint8_t b : 1;
+ uint8_t c : 1;
+ uint8_t d : 1;
+ };
- struct EvenMoreBits
- {
- uint8_t b1 : 1, b2 : 1, b3 : 1, b4 : 1, b5 : 1, b6 : 1,
- b7 : 1, b8 : 1, b9 : 1, b10 : 1, b11 : 1, b12 : 1,
- b13 : 1, b14 : 1, b15 : 1, b16 : 1, b17 : 1;
- };
+ struct MoreBits more_bits;
+
+ more_bits.a = 3;
+ more_bits.b = 0;
+ more_bits.c = 1;
+ more_bits.d = 0;
- struct EvenMoreBits even_more_bits;
- memset(&even_more_bits, 0, sizeof(even_more_bits));
- even_more_bits.b1 = 1;
- even_more_bits.b5 = 1;
- even_more_bits.b7 = 1;
- even_more_bits.b13 = 1;
+ struct EvenMoreBits {
+ uint8_t b1 : 1, b2 : 1, b3 : 1, b4 : 1, b5 : 1, b6 : 1, b7 : 1, b8 : 1,
+ b9 : 1, b10 : 1, b11 : 1, b12 : 1, b13 : 1, b14 : 1, b15 : 1, b16 : 1,
+ b17 : 1;
+ };
+
+ struct EvenMoreBits even_more_bits;
+ memset(&even_more_bits, 0, sizeof(even_more_bits));
+ even_more_bits.b1 = 1;
+ even_more_bits.b5 = 1;
+ even_more_bits.b7 = 1;
+ even_more_bits.b13 = 1;
#pragma pack(1)
- struct PackedBits
- {
- char a;
- uint32_t b : 5,
- c : 27;
+ struct PackedBits {
+ char a;
+ uint32_t b : 5, c : 27;
+ };
+#pragma pack()
+ struct PackedBits packed;
+ packed.a = 'a';
+ packed.b = 10;
+ packed.c = 0x7112233;
+
+ struct LargePackedBits {
+ uint64_t a : 36;
+ uint64_t b : 36;
+ } __attribute__((packed));
+
+ struct LargePackedBits large_packed =
+ (struct LargePackedBits){0xcbbbbaaaa, 0xdffffeeee};
+
+ struct LargerBitsA {
+ unsigned int : 30, a : 20;
+ } lba;
+
+ struct LargerBitsB {
+ unsigned int a : 1, : 11, : 12, b : 20;
+ } lbb;
+
+ struct LargerBitsC {
+ unsigned int : 13, : 9, a : 1, b : 1, c : 5, d : 1, e : 20;
+ } lbc;
+
+ struct LargerBitsD {
+ char arr[3];
+ unsigned int : 30, a : 20;
+ } lbd;
+
+ // This case came up when debugging clang
+ struct BitExampleFromClangDeclContext {
+ struct fields {
+ uint64_t : 13;
+ uint64_t : 9;
+
+ uint64_t HasFlexibleArrayMember : 1;
+ uint64_t AnonymousStructOrUnion : 1;
+ uint64_t HasObjectMember : 1;
+ uint64_t HasVolatileMember : 1;
+ uint64_t LoadedFieldsFromExternalStorage : 1;
+ uint64_t NonTrivialToPrimitiveDefaultInitialize : 1;
+ uint64_t NonTrivialToPrimitiveCopy : 1;
+ uint64_t NonTrivialToPrimitiveDestroy : 1;
+ uint64_t HasNonTrivialToPrimitiveDefaultInitializeCUnion : 1;
+ uint64_t HasNonTrivialToPrimitiveDestructCUnion : 1;
+ uint64_t HasNonTrivialToPrimitiveCopyCUnion : 1;
+ };
+
+ union {
+ struct fields f;
};
-#pragma pack()
- struct PackedBits packed;
- packed.a = 'a';
- packed.b = 10;
- packed.c = 0x7112233;
-
- struct LargePackedBits {
- uint64_t a: 36;
- uint64_t b: 36;
- } __attribute__((packed));
-
- struct LargePackedBits large_packed =
- (struct LargePackedBits){ 0xcbbbbaaaa, 0xdffffeeee };
-
- return 0; //// Set break point at this line.
+ } clang_example;
+
+ lba.a = 2;
+ lbb.b = 3;
+ lbc.c = 4;
+ lbd.a = 5;
+ clang_example.f.HasFlexibleArrayMember = 1;
+ return 0; //// Set break point at this line.
}
Index: lldb/packages/Python/lldbsuite/test/lang/c/bitfields/TestBitfields.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/lang/c/bitfields/TestBitfields.py
+++ lldb/packages/Python/lldbsuite/test/lang/c/bitfields/TestBitfields.py
@@ -147,6 +147,74 @@
self.expect("v/x large_packed", VARIABLES_DISPLAYED_CORRECTLY,
substrs=["a = 0x0000000cbbbbaaaa", "b = 0x0000000dffffeee"])
+ self.expect(
+ "frame variable --show-types lba",
+ VARIABLES_DISPLAYED_CORRECTLY,
+ substrs=[
+ '(int:32) = ',
+ '(unsigned int:20) a =',
+ ])
+
+ self.expect(
+ "frame variable --show-types lbb",
+ VARIABLES_DISPLAYED_CORRECTLY,
+ substrs=[
+ '(unsigned int:1) a =',
+ '(int:31) =',
+ '(unsigned int:20) b =',
+ ])
+
+ self.expect(
+ "frame variable --show-types lbc",
+ VARIABLES_DISPLAYED_CORRECTLY,
+ substrs=[
+ '(int:22) =',
+ '(unsigned int:1) a =',
+ '(unsigned int:1) b =',
+ '(unsigned int:5) c =',
+ '(unsigned int:1) d =',
+ '(int:2) =',
+ '(unsigned int:20) e =',
+ ])
+
+ self.expect(
+ "frame variable --show-types lbd",
+ VARIABLES_DISPLAYED_CORRECTLY,
+ substrs=[
+ '(char [3]) arr =',
+ '(int:32) =',
+ '(unsigned int:20) a =',
+ ])
+
+ self.expect(
+ "frame variable --show-types clang_example",
+ VARIABLES_DISPLAYED_CORRECTLY,
+ substrs=[
+ '(int:22) =',
+ '(uint64_t:1) HasFlexibleArrayMember =',
+ '(uint64_t:1) AnonymousStructOrUnion =',
+ '(uint64_t:1) HasObjectMember =',
+ '(uint64_t:1) HasVolatileMember =',
+ '(uint64_t:1) LoadedFieldsFromExternalStorage =',
+ '(uint64_t:1) NonTrivialToPrimitiveDefaultInitialize =',
+ '(uint64_t:1) NonTrivialToPrimitiveCopy =',
+ '(uint64_t:1) NonTrivialToPrimitiveDestroy =',
+ '(uint64_t:1) HasNonTrivialToPrimitiveDefaultInitializeCUnion =',
+ '(uint64_t:1) HasNonTrivialToPrimitiveDestructCUnion =',
+ '(uint64_t:1) HasNonTrivialToPrimitiveCopyCUnion =',
+ ])
+
+ self.expect("expr (lba.a)", VARIABLES_DISPLAYED_CORRECTLY,
+ substrs=['unsigned int', '2'])
+ self.expect("expr (lbb.b)", VARIABLES_DISPLAYED_CORRECTLY,
+ substrs=['unsigned int', '3'])
+ self.expect("expr (lbc.c)", VARIABLES_DISPLAYED_CORRECTLY,
+ substrs=['unsigned int', '4'])
+ self.expect("expr (lbd.a)", VARIABLES_DISPLAYED_CORRECTLY,
+ substrs=['unsigned int', '5'])
+ self.expect("expr (clang_example.f.HasFlexibleArrayMember)", VARIABLES_DISPLAYED_CORRECTLY,
+ substrs=['uint64_t', '1'])
+
@add_test_categories(['pyapi'])
# BitFields exhibit crashes in record layout on Windows
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits