diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index ca1db03b02fa21..09f5b28449cb1a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -2448,493 +2448,500 @@ Function *DWARFASTParserClang::ParseFunctionFromDWARF(CompileUnit &comp_unit, return nullptr; } -bool DWARFASTParserClang::ParseChildMembers( - const DWARFDIE &parent_die, CompilerType &class_clang_type, - const LanguageType class_language, - std::vector> &base_classes, +void DWARFASTParserClang::ParseSingleMember( + const DWARFDIE &die, const DWARFDIE &parent_die, + lldb_private::CompilerType &class_clang_type, + const lldb::LanguageType class_language, std::vector &member_accessibilities, - std::vector &member_function_dies, - DelayedPropertyList &delayed_properties, AccessType &default_accessibility, - bool &is_a_class, ClangASTImporter::LayoutInfo &layout_info) { - if (!parent_die) - return false; - + lldb::AccessType &default_accessibility, + DelayedPropertyList &delayed_properties, + lldb_private::ClangASTImporter::LayoutInfo &layout_info, + BitfieldInfo &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 const uint64_t parent_byte_size = parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, UINT64_MAX); const uint64_t parent_bit_size = parent_byte_size == UINT64_MAX ? UINT64_MAX : parent_byte_size * 8; - BitfieldInfo last_field_info; - - ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule(); - ClangASTContext *ast = - llvm::dyn_cast_or_null(class_clang_type.GetTypeSystem()); - if (ast == nullptr) - return false; - - for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); - die = die.GetSibling()) { - dw_tag_t tag = die.Tag(); - - switch (tag) { - case DW_TAG_member: - case DW_TAG_APPLE_property: { - DWARFAttributes attributes; - const size_t num_attributes = die.GetAttributes(attributes); - if (num_attributes > 0) { - const char *name = nullptr; - const char *prop_name = nullptr; - const char *prop_getter_name = nullptr; - const char *prop_setter_name = nullptr; - uint32_t prop_attributes = 0; - - bool is_artificial = false; - DWARFFormValue encoding_form; - AccessType accessibility = eAccessNone; - uint32_t member_byte_offset = - (parent_die.Tag() == DW_TAG_union_type) ? 0 : UINT32_MAX; - llvm::Optional byte_size; - int64_t bit_offset = 0; - uint64_t data_bit_offset = UINT64_MAX; - size_t bit_size = 0; - bool is_external = - false; // On DW_TAG_members, this means the member is static - uint32_t i; - for (i = 0; i < num_attributes && !is_artificial; ++i) { - const dw_attr_t attr = attributes.AttributeAtIndex(i); - DWARFFormValue form_value; - if (attributes.ExtractFormValueAtIndex(i, form_value)) { - switch (attr) { - case DW_AT_name: - name = form_value.AsCString(); - break; - case DW_AT_type: - encoding_form = form_value; - break; - case DW_AT_bit_offset: - bit_offset = form_value.Signed(); - break; - case DW_AT_bit_size: - bit_size = form_value.Unsigned(); - break; - case DW_AT_byte_size: - byte_size = form_value.Unsigned(); - break; - case DW_AT_data_bit_offset: - data_bit_offset = form_value.Unsigned(); - break; - case DW_AT_data_member_location: - if (form_value.BlockData()) { - Value initialValue(0); - Value memberOffset(0); - const DWARFDataExtractor &debug_info_data = die.GetData(); - uint32_t block_length = form_value.Unsigned(); - uint32_t block_offset = - form_value.BlockData() - debug_info_data.GetDataStart(); - if (DWARFExpression::Evaluate( - nullptr, // ExecutionContext * - nullptr, // RegisterContext * - module_sp, - DataExtractor(debug_info_data, block_offset, - block_length), - die.GetCU(), eRegisterKindDWARF, &initialValue, nullptr, - memberOffset, nullptr)) { - member_byte_offset = - memberOffset.ResolveValue(nullptr).UInt(); - } - } else { - // With DWARF 3 and later, if the value is an integer constant, - // this form value is the offset in bytes from the beginning of - // the containing entity. - member_byte_offset = form_value.Unsigned(); - } - break; - - case DW_AT_accessibility: - accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); - break; - case DW_AT_artificial: - is_artificial = form_value.Boolean(); - break; - case DW_AT_APPLE_property_name: - prop_name = form_value.AsCString(); - break; - case DW_AT_APPLE_property_getter: - prop_getter_name = form_value.AsCString(); - break; - case DW_AT_APPLE_property_setter: - prop_setter_name = form_value.AsCString(); - break; - case DW_AT_APPLE_property_attribute: - prop_attributes = form_value.Unsigned(); - break; - case DW_AT_external: - is_external = form_value.Boolean(); - break; - - default: - case DW_AT_declaration: - case DW_AT_description: - case DW_AT_mutable: - case DW_AT_visibility: - case DW_AT_sibling: - break; + DWARFAttributes attributes; + const size_t num_attributes = die.GetAttributes(attributes); + if (num_attributes > 0) { + const char *name = nullptr; + const char *prop_name = nullptr; + const char *prop_getter_name = nullptr; + const char *prop_setter_name = nullptr; + uint32_t prop_attributes = 0; + + bool is_artificial = false; + DWARFFormValue encoding_form; + AccessType accessibility = eAccessNone; + uint32_t member_byte_offset = + (parent_die.Tag() == DW_TAG_union_type) ? 0 : UINT32_MAX; + llvm::Optional byte_size; + int64_t bit_offset = 0; + uint64_t data_bit_offset = UINT64_MAX; + size_t bit_size = 0; + bool is_external = + false; // On DW_TAG_members, this means the member is static + uint32_t i; + for (i = 0; i < num_attributes && !is_artificial; ++i) { + const dw_attr_t attr = attributes.AttributeAtIndex(i); + DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(i, form_value)) { + switch (attr) { + case DW_AT_name: + name = form_value.AsCString(); + break; + case DW_AT_type: + encoding_form = form_value; + break; + case DW_AT_bit_offset: + bit_offset = form_value.Signed(); + break; + case DW_AT_bit_size: + bit_size = form_value.Unsigned(); + break; + case DW_AT_byte_size: + byte_size = form_value.Unsigned(); + break; + case DW_AT_data_bit_offset: + data_bit_offset = form_value.Unsigned(); + break; + case DW_AT_data_member_location: + if (form_value.BlockData()) { + Value initialValue(0); + Value memberOffset(0); + const DWARFDataExtractor &debug_info_data = die.GetData(); + uint32_t block_length = form_value.Unsigned(); + uint32_t block_offset = + form_value.BlockData() - debug_info_data.GetDataStart(); + if (DWARFExpression::Evaluate( + nullptr, // ExecutionContext * + nullptr, // RegisterContext * + module_sp, + DataExtractor(debug_info_data, block_offset, block_length), + die.GetCU(), eRegisterKindDWARF, &initialValue, nullptr, + memberOffset, nullptr)) { + member_byte_offset = memberOffset.ResolveValue(nullptr).UInt(); } + } else { + // With DWARF 3 and later, if the value is an integer constant, + // this form value is the offset in bytes from the beginning of + // the containing entity. + member_byte_offset = form_value.Unsigned(); } - } - - if (prop_name) { - ConstString fixed_getter; - ConstString fixed_setter; - - // Check if the property getter/setter were provided as full names. - // We want basenames, so we extract them. - - if (prop_getter_name && prop_getter_name[0] == '-') { - ObjCLanguage::MethodName prop_getter_method(prop_getter_name, true); - prop_getter_name = prop_getter_method.GetSelector().GetCString(); - } + break; - if (prop_setter_name && prop_setter_name[0] == '-') { - ObjCLanguage::MethodName prop_setter_method(prop_setter_name, true); - prop_setter_name = prop_setter_method.GetSelector().GetCString(); - } + case DW_AT_accessibility: + accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); + break; + case DW_AT_artificial: + is_artificial = form_value.Boolean(); + break; + case DW_AT_APPLE_property_name: + prop_name = form_value.AsCString(); + break; + case DW_AT_APPLE_property_getter: + prop_getter_name = form_value.AsCString(); + break; + case DW_AT_APPLE_property_setter: + prop_setter_name = form_value.AsCString(); + break; + case DW_AT_APPLE_property_attribute: + prop_attributes = form_value.Unsigned(); + break; + case DW_AT_external: + is_external = form_value.Boolean(); + break; - // If the names haven't been provided, they need to be filled in. + default: + case DW_AT_declaration: + case DW_AT_description: + case DW_AT_mutable: + case DW_AT_visibility: + case DW_AT_sibling: + break; + } + } + } - if (!prop_getter_name) { - prop_getter_name = prop_name; - } - if (!prop_setter_name && prop_name[0] && - !(prop_attributes & DW_APPLE_PROPERTY_readonly)) { - StreamString ss; + if (prop_name) { + ConstString fixed_getter; + ConstString fixed_setter; - ss.Printf("set%c%s:", toupper(prop_name[0]), &prop_name[1]); + // Check if the property getter/setter were provided as full names. + // We want basenames, so we extract them. - fixed_setter.SetString(ss.GetString()); - prop_setter_name = fixed_setter.GetCString(); - } - } + if (prop_getter_name && prop_getter_name[0] == '-') { + ObjCLanguage::MethodName prop_getter_method(prop_getter_name, true); + prop_getter_name = prop_getter_method.GetSelector().GetCString(); + } - // Clang has a DWARF generation bug where sometimes it represents - // fields that are references with bad byte size and bit size/offset - // information such as: - // - // DW_AT_byte_size( 0x00 ) - // DW_AT_bit_size( 0x40 ) - // DW_AT_bit_offset( 0xffffffffffffffc0 ) - // - // So check the bit offset to make sure it is sane, and if the values - // are not sane, remove them. If we don't do this then we will end up - // with a crash if we try to use this type in an expression when clang - // becomes unhappy with its recycled debug info. - - if (byte_size.getValueOr(0) == 0 && bit_offset < 0) { - bit_size = 0; - bit_offset = 0; - } + if (prop_setter_name && prop_setter_name[0] == '-') { + ObjCLanguage::MethodName prop_setter_method(prop_setter_name, true); + prop_setter_name = prop_setter_method.GetSelector().GetCString(); + } - // FIXME: Make Clang ignore Objective-C accessibility for expressions - if (class_language == eLanguageTypeObjC || - class_language == eLanguageTypeObjC_plus_plus) - accessibility = eAccessNone; - - // Handle static members - if (is_external && member_byte_offset == UINT32_MAX) { - Type *var_type = die.ResolveTypeUID(encoding_form.Reference()); - - if (var_type) { - if (accessibility == eAccessNone) - accessibility = eAccessPublic; - ClangASTContext::AddVariableToRecordType( - class_clang_type, name, var_type->GetLayoutCompilerType(), - accessibility); - } - break; - } + // If the names haven't been provided, they need to be filled in. - if (!is_artificial) { - Type *member_type = die.ResolveTypeUID(encoding_form.Reference()); - - clang::FieldDecl *field_decl = nullptr; - if (tag == DW_TAG_member) { - if (member_type) { - 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; - 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; - } else { - if (!byte_size) - byte_size = member_type->GetByteSize(); - - ObjectFile *objfile = die.GetDWARF()->GetObjectFile(); - if (objfile->GetByteOrder() == eByteOrderLittle) { - this_field_info.bit_offset += byte_size.getValueOr(0) * 8; - this_field_info.bit_offset -= (bit_offset + bit_size); - } else { - this_field_info.bit_offset += bit_offset; - } - } + if (!prop_getter_name) { + prop_getter_name = prop_name; + } + if (!prop_setter_name && prop_name[0] && + !(prop_attributes & DW_APPLE_PROPERTY_readonly)) { + StreamString ss; - if ((this_field_info.bit_offset >= parent_bit_size) || - !last_field_info.NextBitfieldOffsetIsValid( - this_field_info.bit_offset)) { - ObjectFile *objfile = die.GetDWARF()->GetObjectFile(); - objfile->GetModule()->ReportWarning( - "0x%8.8" PRIx64 ": %s bitfield named \"%s\" has invalid " - "bit offset (0x%8.8" PRIx64 - ") member will be ignored. Please file a bug against the " - "compiler and include the preprocessed output for %s\n", - die.GetID(), DW_TAG_value_to_name(tag), name, - this_field_info.bit_offset, - GetUnitName(parent_die).c_str()); - this_field_info.Clear(); - continue; - } + ss.Printf("set%c%s:", toupper(prop_name[0]), &prop_name[1]); - // 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. - bool detect_unnamed_bitfields = true; - - if (class_language == eLanguageTypeObjC || - class_language == eLanguageTypeObjC_plus_plus) - detect_unnamed_bitfields = - 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; - } - } - } + fixed_setter.SetString(ss.GetString()); + prop_setter_name = fixed_setter.GetCString(); + } + } - if (anon_field_info.IsValid()) { - clang::FieldDecl *unnamed_bitfield_decl = - ClangASTContext::AddFieldToRecordType( - class_clang_type, llvm::StringRef(), - m_ast.GetBuiltinTypeForEncodingAndBitSize( - eEncodingSint, word_width), - accessibility, anon_field_info.bit_size); + // Clang has a DWARF generation bug where sometimes it represents + // fields that are references with bad byte size and bit size/offset + // information such as: + // + // DW_AT_byte_size( 0x00 ) + // DW_AT_bit_size( 0x40 ) + // DW_AT_bit_offset( 0xffffffffffffffc0 ) + // + // So check the bit offset to make sure it is sane, and if the values + // are not sane, remove them. If we don't do this then we will end up + // with a crash if we try to use this type in an expression when clang + // becomes unhappy with its recycled debug info. + + if (byte_size.getValueOr(0) == 0 && bit_offset < 0) { + bit_size = 0; + bit_offset = 0; + } + + // FIXME: Make Clang ignore Objective-C accessibility for expressions + if (class_language == eLanguageTypeObjC || + class_language == eLanguageTypeObjC_plus_plus) + accessibility = eAccessNone; + + // Handle static members + if (is_external && member_byte_offset == UINT32_MAX) { + Type *var_type = die.ResolveTypeUID(encoding_form.Reference()); + + if (var_type) { + if (accessibility == eAccessNone) + accessibility = eAccessPublic; + ClangASTContext::AddVariableToRecordType( + class_clang_type, name, var_type->GetLayoutCompilerType(), + accessibility); + } + return; + } + + if (!is_artificial) { + Type *member_type = die.ResolveTypeUID(encoding_form.Reference()); + + clang::FieldDecl *field_decl = nullptr; + if (tag == DW_TAG_member) { + if (member_type) { + 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; + 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; + } else { + if (!byte_size) + byte_size = member_type->GetByteSize(); - layout_info.field_offsets.insert(std::make_pair( - unnamed_bitfield_decl, anon_field_info.bit_offset)); - } - } - last_field_info = this_field_info; + ObjectFile *objfile = die.GetDWARF()->GetObjectFile(); + if (objfile->GetByteOrder() == eByteOrderLittle) { + this_field_info.bit_offset += byte_size.getValueOr(0) * 8; + this_field_info.bit_offset -= (bit_offset + bit_size); } else { - last_field_info.Clear(); + this_field_info.bit_offset += bit_offset; } + } - CompilerType member_clang_type = - member_type->GetLayoutCompilerType(); - if (!member_clang_type.IsCompleteType()) - member_clang_type.GetCompleteType(); + if ((this_field_info.bit_offset >= parent_bit_size) || + !last_field_info.NextBitfieldOffsetIsValid( + this_field_info.bit_offset)) { + ObjectFile *objfile = die.GetDWARF()->GetObjectFile(); + objfile->GetModule()->ReportWarning( + "0x%8.8" PRIx64 ": %s bitfield named \"%s\" has invalid " + "bit offset (0x%8.8" PRIx64 + ") member will be ignored. Please file a bug against the " + "compiler and include the preprocessed output for %s\n", + die.GetID(), DW_TAG_value_to_name(tag), name, + this_field_info.bit_offset, GetUnitName(parent_die).c_str()); + this_field_info.Clear(); + return; + } + // 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. + bool detect_unnamed_bitfields = true; + + if (class_language == eLanguageTypeObjC || + class_language == eLanguageTypeObjC_plus_plus) + detect_unnamed_bitfields = + 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 { - // Older versions of clang emit array[0] and array[1] in the - // same way (). If the current field - // is at the end of the structure, then there is definitely no - // room for extra elements and we override the type to - // array[0]. - - CompilerType member_array_element_type; - uint64_t member_array_size; - bool member_array_is_incomplete; - - if (member_clang_type.IsArrayType( - &member_array_element_type, &member_array_size, - &member_array_is_incomplete) && - !member_array_is_incomplete) { - uint64_t parent_byte_size = - parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, - UINT64_MAX); - - if (member_byte_offset >= parent_byte_size) { - if (member_array_size != 1 && - (member_array_size != 0 || - member_byte_offset > parent_byte_size)) { - module_sp->ReportError( - "0x%8.8" PRIx64 - ": DW_TAG_member '%s' refers to type 0x%8.8x" - " which extends beyond the bounds of 0x%8.8" PRIx64, - die.GetID(), name, - encoding_form.Reference().GetOffset(), - parent_die.GetID()); - } + uint64_t last_field_end = 0; - member_clang_type = m_ast.CreateArrayType( - member_array_element_type, 0, false); + 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; } } } - if (ClangASTContext::IsCXXClassType(member_clang_type) && - !member_clang_type.GetCompleteType()) { - if (die.GetCU()->GetProducer() == eProducerClang) - module_sp->ReportError( - "DWARF DIE at 0x%8.8x (class %s) has a member variable " - "0x%8.8x (%s) whose type is a forward declaration, not a " - "complete definition.\nTry compiling the source file " - "with -fstandalone-debug", - parent_die.GetOffset(), parent_die.GetName(), - die.GetOffset(), name); - else - module_sp->ReportError( - "DWARF DIE at 0x%8.8x (class %s) has a member variable " - "0x%8.8x (%s) whose type is a forward declaration, not a " - "complete definition.\nPlease file a bug against the " - "compiler and include the preprocessed output for %s", - parent_die.GetOffset(), parent_die.GetName(), - die.GetOffset(), name, GetUnitName(parent_die).c_str()); - // We have no choice other than to pretend that the member - // class is complete. If we don't do this, clang will crash - // when trying to layout the class. Since we provide layout - // assistance, all ivars in this class and other classes will - // be fine, this is the best we can do short of crashing. - if (ClangASTContext::StartTagDeclarationDefinition( - member_clang_type)) { - ClangASTContext::CompleteTagDeclarationDefinition( - member_clang_type); - } else { - module_sp->ReportError( - "DWARF DIE at 0x%8.8x (class %s) has a member variable " - "0x%8.8x (%s) whose type claims to be a C++ class but we " - "were not able to start its definition.\nPlease file a " - "bug and attach the file at the start of this error " - "message", - parent_die.GetOffset(), parent_die.GetName(), - die.GetOffset(), name); - } + if (anon_field_info.IsValid()) { + clang::FieldDecl *unnamed_bitfield_decl = + ClangASTContext::AddFieldToRecordType( + class_clang_type, llvm::StringRef(), + m_ast.GetBuiltinTypeForEncodingAndBitSize(eEncodingSint, + word_width), + accessibility, anon_field_info.bit_size); + + layout_info.field_offsets.insert(std::make_pair( + unnamed_bitfield_decl, anon_field_info.bit_offset)); } + } + last_field_info = this_field_info; + } else { + last_field_info.Clear(); + } - field_decl = ClangASTContext::AddFieldToRecordType( - class_clang_type, name, member_clang_type, accessibility, - bit_size); + CompilerType member_clang_type = member_type->GetLayoutCompilerType(); + if (!member_clang_type.IsCompleteType()) + member_clang_type.GetCompleteType(); + + { + // Older versions of clang emit array[0] and array[1] in the + // same way (). If the current field + // is at the end of the structure, then there is definitely no + // room for extra elements and we override the type to + // array[0]. + + CompilerType member_array_element_type; + uint64_t member_array_size; + bool member_array_is_incomplete; + + if (member_clang_type.IsArrayType(&member_array_element_type, + &member_array_size, + &member_array_is_incomplete) && + !member_array_is_incomplete) { + uint64_t parent_byte_size = + parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, + UINT64_MAX); + + if (member_byte_offset >= parent_byte_size) { + if (member_array_size != 1 && + (member_array_size != 0 || + member_byte_offset > parent_byte_size)) { + module_sp->ReportError( + "0x%8.8" PRIx64 + ": DW_TAG_member '%s' refers to type 0x%8.8x" + " which extends beyond the bounds of 0x%8.8" PRIx64, + die.GetID(), name, encoding_form.Reference().GetOffset(), + parent_die.GetID()); + } - m_ast.SetMetadataAsUserID(field_decl, die.GetID()); + member_clang_type = + m_ast.CreateArrayType(member_array_element_type, 0, false); + } + } + } - layout_info.field_offsets.insert( - std::make_pair(field_decl, field_bit_offset)); + if (ClangASTContext::IsCXXClassType(member_clang_type) && + !member_clang_type.GetCompleteType()) { + if (die.GetCU()->GetProducer() == eProducerClang) + module_sp->ReportError( + "DWARF DIE at 0x%8.8x (class %s) has a member variable " + "0x%8.8x (%s) whose type is a forward declaration, not a " + "complete definition.\nTry compiling the source file " + "with -fstandalone-debug", + parent_die.GetOffset(), parent_die.GetName(), die.GetOffset(), + name); + else + module_sp->ReportError( + "DWARF DIE at 0x%8.8x (class %s) has a member variable " + "0x%8.8x (%s) whose type is a forward declaration, not a " + "complete definition.\nPlease file a bug against the " + "compiler and include the preprocessed output for %s", + parent_die.GetOffset(), parent_die.GetName(), die.GetOffset(), + name, GetUnitName(parent_die).c_str()); + // We have no choice other than to pretend that the member + // class is complete. If we don't do this, clang will crash + // when trying to layout the class. Since we provide layout + // assistance, all ivars in this class and other classes will + // be fine, this is the best we can do short of crashing. + if (ClangASTContext::StartTagDeclarationDefinition( + member_clang_type)) { + ClangASTContext::CompleteTagDeclarationDefinition( + member_clang_type); } else { - if (name) - module_sp->ReportError( - "0x%8.8" PRIx64 - ": DW_TAG_member '%s' refers to type 0x%8.8x" - " which was unable to be parsed", - die.GetID(), name, encoding_form.Reference().GetOffset()); - else - module_sp->ReportError( - "0x%8.8" PRIx64 ": DW_TAG_member refers to type 0x%8.8x" - " which was unable to be parsed", - die.GetID(), encoding_form.Reference().GetOffset()); + module_sp->ReportError( + "DWARF DIE at 0x%8.8x (class %s) has a member variable " + "0x%8.8x (%s) whose type claims to be a C++ class but we " + "were not able to start its definition.\nPlease file a " + "bug and attach the file at the start of this error " + "message", + parent_die.GetOffset(), parent_die.GetName(), die.GetOffset(), + name); } } - if (prop_name != nullptr && member_type) { - clang::ObjCIvarDecl *ivar_decl = nullptr; + field_decl = ClangASTContext::AddFieldToRecordType( + class_clang_type, name, member_clang_type, accessibility, + bit_size); - if (field_decl) { - ivar_decl = clang::dyn_cast(field_decl); - assert(ivar_decl != nullptr); - } + m_ast.SetMetadataAsUserID(field_decl, die.GetID()); - ClangASTMetadata metadata; - metadata.SetUserID(die.GetID()); - delayed_properties.push_back(DelayedAddObjCClassProperty( - class_clang_type, prop_name, - member_type->GetLayoutCompilerType(), ivar_decl, - prop_setter_name, prop_getter_name, prop_attributes, - &metadata)); + layout_info.field_offsets.insert( + std::make_pair(field_decl, field_bit_offset)); + } else { + if (name) + module_sp->ReportError( + "0x%8.8" PRIx64 ": DW_TAG_member '%s' refers to type 0x%8.8x" + " which was unable to be parsed", + die.GetID(), name, encoding_form.Reference().GetOffset()); + else + module_sp->ReportError( + "0x%8.8" PRIx64 ": DW_TAG_member refers to type 0x%8.8x" + " which was unable to be parsed", + die.GetID(), encoding_form.Reference().GetOffset()); + } + } - if (ivar_decl) - m_ast.SetMetadataAsUserID(ivar_decl, die.GetID()); - } + if (prop_name != nullptr && member_type) { + clang::ObjCIvarDecl *ivar_decl = nullptr; + + if (field_decl) { + ivar_decl = clang::dyn_cast(field_decl); + assert(ivar_decl != nullptr); } + + ClangASTMetadata metadata; + metadata.SetUserID(die.GetID()); + delayed_properties.push_back(DelayedAddObjCClassProperty( + class_clang_type, prop_name, member_type->GetLayoutCompilerType(), + ivar_decl, prop_setter_name, prop_getter_name, prop_attributes, + &metadata)); + + if (ivar_decl) + m_ast.SetMetadataAsUserID(ivar_decl, die.GetID()); } - } break; + } + } +} + +bool DWARFASTParserClang::ParseChildMembers( + const DWARFDIE &parent_die, CompilerType &class_clang_type, + const LanguageType class_language, + std::vector> &base_classes, + std::vector &member_accessibilities, + std::vector &member_function_dies, + DelayedPropertyList &delayed_properties, AccessType &default_accessibility, + bool &is_a_class, ClangASTImporter::LayoutInfo &layout_info) { + if (!parent_die) + return false; + + BitfieldInfo last_field_info; + + ModuleSP module_sp = parent_die.GetDWARF()->GetObjectFile()->GetModule(); + ClangASTContext *ast = + llvm::dyn_cast_or_null(class_clang_type.GetTypeSystem()); + if (ast == nullptr) + return false; + + for (DWARFDIE die = parent_die.GetFirstChild(); die.IsValid(); + die = die.GetSibling()) { + dw_tag_t tag = die.Tag(); + + switch (tag) { + case DW_TAG_member: + 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); + break; case DW_TAG_subprogram: // Let the type parsing code handle this one for us. diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h index 31d76c1852123a..c5b630e435e94a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -170,6 +170,46 @@ class DWARFASTParserClang : public DWARFASTParser { lldb::ModuleSP GetModuleForType(const DWARFDIE &die); private: + 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; + } + } + }; + + void + ParseSingleMember(const DWARFDIE &die, const DWARFDIE &parent_die, + lldb_private::CompilerType &class_clang_type, + const lldb::LanguageType class_language, + std::vector &member_accessibilities, + lldb::AccessType &default_accessibility, + DelayedPropertyList &delayed_properties, + lldb_private::ClangASTImporter::LayoutInfo &layout_info, + BitfieldInfo &last_field_info); + bool CompleteRecordType(const DWARFDIE &die, lldb_private::Type *type, lldb_private::CompilerType &clang_type); bool CompleteEnumType(const DWARFDIE &die, lldb_private::Type *type,