| //===-- LVELFReader.cpp ---------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This implements the LVELFReader class. | |
| // It supports ELF and Mach-O formats. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/DebugInfo/LogicalView/Readers/LVELFReader.h" | |
| #include "llvm/DebugInfo/DIContext.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" | |
| #include "llvm/DebugInfo/DWARF/DWARFExpression.h" | |
| #include "llvm/DebugInfo/LogicalView/Core/LVLine.h" | |
| #include "llvm/DebugInfo/LogicalView/Core/LVScope.h" | |
| #include "llvm/DebugInfo/LogicalView/Core/LVSymbol.h" | |
| #include "llvm/DebugInfo/LogicalView/Core/LVType.h" | |
| #include "llvm/Object/Error.h" | |
| #include "llvm/Object/MachO.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| using namespace llvm; | |
| using namespace llvm::object; | |
| using namespace llvm::logicalview; | |
| #define DEBUG_TYPE "ElfReader" | |
| LVElement *LVELFReader::createElement(dwarf::Tag Tag) { | |
| CurrentScope = nullptr; | |
| CurrentSymbol = nullptr; | |
| CurrentType = nullptr; | |
| CurrentRanges.clear(); | |
| if (!options().getPrintSymbols()) { | |
| switch (Tag) { | |
| // As the command line options did not specify a request to print | |
| // logical symbols (--print=symbols or --print=all or --print=elements), | |
| // skip its creation. | |
| case dwarf::DW_TAG_formal_parameter: | |
| case dwarf::DW_TAG_unspecified_parameters: | |
| case dwarf::DW_TAG_member: | |
| case dwarf::DW_TAG_variable: | |
| case dwarf::DW_TAG_inheritance: | |
| case dwarf::DW_TAG_constant: | |
| case dwarf::DW_TAG_call_site_parameter: | |
| case dwarf::DW_TAG_GNU_call_site_parameter: | |
| return nullptr; | |
| default: | |
| break; | |
| } | |
| } | |
| switch (Tag) { | |
| // Types. | |
| case dwarf::DW_TAG_base_type: | |
| CurrentType = new LVType(); | |
| CurrentType->setIsBase(); | |
| if (options().getAttributeBase()) | |
| CurrentType->setIncludeInPrint(); | |
| return CurrentType; | |
| case dwarf::DW_TAG_const_type: | |
| CurrentType = new LVType(); | |
| CurrentType->setIsConst(); | |
| CurrentType->setName("const"); | |
| return CurrentType; | |
| case dwarf::DW_TAG_enumerator: | |
| CurrentType = new LVTypeEnumerator(); | |
| return CurrentType; | |
| case dwarf::DW_TAG_imported_declaration: | |
| CurrentType = new LVTypeImport(); | |
| CurrentType->setIsImportDeclaration(); | |
| return CurrentType; | |
| case dwarf::DW_TAG_imported_module: | |
| CurrentType = new LVTypeImport(); | |
| CurrentType->setIsImportModule(); | |
| return CurrentType; | |
| case dwarf::DW_TAG_pointer_type: | |
| CurrentType = new LVType(); | |
| CurrentType->setIsPointer(); | |
| CurrentType->setName("*"); | |
| return CurrentType; | |
| case dwarf::DW_TAG_ptr_to_member_type: | |
| CurrentType = new LVType(); | |
| CurrentType->setIsPointerMember(); | |
| CurrentType->setName("*"); | |
| return CurrentType; | |
| case dwarf::DW_TAG_reference_type: | |
| CurrentType = new LVType(); | |
| CurrentType->setIsReference(); | |
| CurrentType->setName("&"); | |
| return CurrentType; | |
| case dwarf::DW_TAG_restrict_type: | |
| CurrentType = new LVType(); | |
| CurrentType->setIsRestrict(); | |
| CurrentType->setName("restrict"); | |
| return CurrentType; | |
| case dwarf::DW_TAG_rvalue_reference_type: | |
| CurrentType = new LVType(); | |
| CurrentType->setIsRvalueReference(); | |
| CurrentType->setName("&&"); | |
| return CurrentType; | |
| case dwarf::DW_TAG_subrange_type: | |
| CurrentType = new LVTypeSubrange(); | |
| return CurrentType; | |
| case dwarf::DW_TAG_template_value_parameter: | |
| CurrentType = new LVTypeParam(); | |
| CurrentType->setIsTemplateValueParam(); | |
| return CurrentType; | |
| case dwarf::DW_TAG_template_type_parameter: | |
| CurrentType = new LVTypeParam(); | |
| CurrentType->setIsTemplateTypeParam(); | |
| return CurrentType; | |
| case dwarf::DW_TAG_GNU_template_template_param: | |
| CurrentType = new LVTypeParam(); | |
| CurrentType->setIsTemplateTemplateParam(); | |
| return CurrentType; | |
| case dwarf::DW_TAG_typedef: | |
| CurrentType = new LVTypeDefinition(); | |
| return CurrentType; | |
| case dwarf::DW_TAG_unspecified_type: | |
| CurrentType = new LVType(); | |
| CurrentType->setIsUnspecified(); | |
| return CurrentType; | |
| case dwarf::DW_TAG_volatile_type: | |
| CurrentType = new LVType(); | |
| CurrentType->setIsVolatile(); | |
| CurrentType->setName("volatile"); | |
| return CurrentType; | |
| // Symbols. | |
| case dwarf::DW_TAG_formal_parameter: | |
| CurrentSymbol = new LVSymbol(); | |
| CurrentSymbol->setIsParameter(); | |
| return CurrentSymbol; | |
| case dwarf::DW_TAG_unspecified_parameters: | |
| CurrentSymbol = new LVSymbol(); | |
| CurrentSymbol->setIsUnspecified(); | |
| CurrentSymbol->setName("..."); | |
| return CurrentSymbol; | |
| case dwarf::DW_TAG_member: | |
| CurrentSymbol = new LVSymbol(); | |
| CurrentSymbol->setIsMember(); | |
| return CurrentSymbol; | |
| case dwarf::DW_TAG_variable: | |
| CurrentSymbol = new LVSymbol(); | |
| CurrentSymbol->setIsVariable(); | |
| return CurrentSymbol; | |
| case dwarf::DW_TAG_inheritance: | |
| CurrentSymbol = new LVSymbol(); | |
| CurrentSymbol->setIsInheritance(); | |
| return CurrentSymbol; | |
| case dwarf::DW_TAG_call_site_parameter: | |
| case dwarf::DW_TAG_GNU_call_site_parameter: | |
| CurrentSymbol = new LVSymbol(); | |
| CurrentSymbol->setIsCallSiteParameter(); | |
| return CurrentSymbol; | |
| case dwarf::DW_TAG_constant: | |
| CurrentSymbol = new LVSymbol(); | |
| CurrentSymbol->setIsConstant(); | |
| return CurrentSymbol; | |
| // Scopes. | |
| case dwarf::DW_TAG_catch_block: | |
| CurrentScope = new LVScope(); | |
| CurrentScope->setIsCatchBlock(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_lexical_block: | |
| CurrentScope = new LVScope(); | |
| CurrentScope->setIsLexicalBlock(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_try_block: | |
| CurrentScope = new LVScope(); | |
| CurrentScope->setIsTryBlock(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_compile_unit: | |
| case dwarf::DW_TAG_skeleton_unit: | |
| CurrentScope = new LVScopeCompileUnit(); | |
| CompileUnit = static_cast<LVScopeCompileUnit *>(CurrentScope); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_inlined_subroutine: | |
| CurrentScope = new LVScopeFunctionInlined(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_namespace: | |
| CurrentScope = new LVScopeNamespace(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_template_alias: | |
| CurrentScope = new LVScopeAlias(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_array_type: | |
| CurrentScope = new LVScopeArray(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_call_site: | |
| case dwarf::DW_TAG_GNU_call_site: | |
| CurrentScope = new LVScopeFunction(); | |
| CurrentScope->setIsCallSite(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_entry_point: | |
| CurrentScope = new LVScopeFunction(); | |
| CurrentScope->setIsEntryPoint(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_subprogram: | |
| CurrentScope = new LVScopeFunction(); | |
| CurrentScope->setIsSubprogram(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_subroutine_type: | |
| CurrentScope = new LVScopeFunctionType(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_label: | |
| CurrentScope = new LVScopeFunction(); | |
| CurrentScope->setIsLabel(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_class_type: | |
| CurrentScope = new LVScopeAggregate(); | |
| CurrentScope->setIsClass(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_structure_type: | |
| CurrentScope = new LVScopeAggregate(); | |
| CurrentScope->setIsStructure(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_union_type: | |
| CurrentScope = new LVScopeAggregate(); | |
| CurrentScope->setIsUnion(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_enumeration_type: | |
| CurrentScope = new LVScopeEnumeration(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_GNU_formal_parameter_pack: | |
| CurrentScope = new LVScopeFormalPack(); | |
| return CurrentScope; | |
| case dwarf::DW_TAG_GNU_template_parameter_pack: | |
| CurrentScope = new LVScopeTemplatePack(); | |
| return CurrentScope; | |
| default: | |
| // Collect TAGs not implemented. | |
| if (options().getInternalTag() && Tag) | |
| CompileUnit->addDebugTag(Tag, CurrentOffset); | |
| break; | |
| } | |
| return nullptr; | |
| } | |
| void LVELFReader::processOneAttribute(const DWARFDie &Die, LVOffset *OffsetPtr, | |
| const AttributeSpec &AttrSpec) { | |
| uint64_t OffsetOnEntry = *OffsetPtr; | |
| DWARFUnit *U = Die.getDwarfUnit(); | |
| const DWARFFormValue &FormValue = | |
| DWARFFormValue::createFromUnit(AttrSpec.Form, U, OffsetPtr); | |
| // We are processing .debug_info section, implicit_const attribute | |
| // values are not really stored here, but in .debug_abbrev section. | |
| auto GetAsUnsignedConstant = [&]() -> int64_t { | |
| return AttrSpec.isImplicitConst() ? AttrSpec.getImplicitConstValue() | |
| : *FormValue.getAsUnsignedConstant(); | |
| }; | |
| auto GetFlag = [](const DWARFFormValue &FormValue) -> bool { | |
| return FormValue.isFormClass(DWARFFormValue::FC_Flag); | |
| }; | |
| auto GetBoundValue = [](const DWARFFormValue &FormValue) -> int64_t { | |
| switch (FormValue.getForm()) { | |
| case dwarf::DW_FORM_ref_addr: | |
| case dwarf::DW_FORM_ref1: | |
| case dwarf::DW_FORM_ref2: | |
| case dwarf::DW_FORM_ref4: | |
| case dwarf::DW_FORM_ref8: | |
| case dwarf::DW_FORM_ref_udata: | |
| case dwarf::DW_FORM_ref_sig8: | |
| return *FormValue.getAsReferenceUVal(); | |
| case dwarf::DW_FORM_data1: | |
| case dwarf::DW_FORM_flag: | |
| case dwarf::DW_FORM_data2: | |
| case dwarf::DW_FORM_data4: | |
| case dwarf::DW_FORM_data8: | |
| case dwarf::DW_FORM_udata: | |
| case dwarf::DW_FORM_ref_sup4: | |
| case dwarf::DW_FORM_ref_sup8: | |
| return *FormValue.getAsUnsignedConstant(); | |
| case dwarf::DW_FORM_sdata: | |
| return *FormValue.getAsSignedConstant(); | |
| default: | |
| return 0; | |
| } | |
| }; | |
| LLVM_DEBUG({ | |
| dbgs() << " " << hexValue(OffsetOnEntry) | |
| << formatv(" {0}", AttrSpec.Attr) << "\n"; | |
| }); | |
| switch (AttrSpec.Attr) { | |
| case dwarf::DW_AT_accessibility: | |
| CurrentElement->setAccessibilityCode(*FormValue.getAsUnsignedConstant()); | |
| break; | |
| case dwarf::DW_AT_artificial: | |
| CurrentElement->setIsArtificial(); | |
| break; | |
| case dwarf::DW_AT_bit_size: | |
| CurrentElement->setBitSize(*FormValue.getAsUnsignedConstant()); | |
| break; | |
| case dwarf::DW_AT_call_file: | |
| CurrentElement->setCallFilenameIndex(GetAsUnsignedConstant()); | |
| break; | |
| case dwarf::DW_AT_call_line: | |
| CurrentElement->setCallLineNumber(IncrementFileIndex | |
| ? GetAsUnsignedConstant() + 1 | |
| : GetAsUnsignedConstant()); | |
| break; | |
| case dwarf::DW_AT_comp_dir: | |
| CompileUnit->setCompilationDirectory(dwarf::toStringRef(FormValue)); | |
| break; | |
| case dwarf::DW_AT_const_value: | |
| if (FormValue.isFormClass(DWARFFormValue::FC_Block)) { | |
| ArrayRef<uint8_t> Expr = *FormValue.getAsBlock(); | |
| // Store the expression as a hexadecimal string. | |
| CurrentElement->setValue( | |
| llvm::toHex(llvm::toStringRef(Expr), /*LowerCase=*/true)); | |
| } else if (FormValue.isFormClass(DWARFFormValue::FC_Constant)) { | |
| // In the case of negative values, generate the string representation | |
| // for a positive value prefixed with the negative sign. | |
| if (FormValue.getForm() == dwarf::DW_FORM_sdata) { | |
| std::stringstream Stream; | |
| int64_t Value = *FormValue.getAsSignedConstant(); | |
| if (Value < 0) { | |
| Stream << "-"; | |
| Value = std::abs(Value); | |
| } | |
| Stream << hexString(Value, 2); | |
| CurrentElement->setValue(Stream.str()); | |
| } else | |
| CurrentElement->setValue( | |
| hexString(*FormValue.getAsUnsignedConstant(), 2)); | |
| } else | |
| CurrentElement->setValue(dwarf::toStringRef(FormValue)); | |
| break; | |
| case dwarf::DW_AT_count: | |
| CurrentElement->setCount(*FormValue.getAsUnsignedConstant()); | |
| break; | |
| case dwarf::DW_AT_decl_line: | |
| CurrentElement->setLineNumber(GetAsUnsignedConstant()); | |
| break; | |
| case dwarf::DW_AT_decl_file: | |
| CurrentElement->setFilenameIndex(IncrementFileIndex | |
| ? GetAsUnsignedConstant() + 1 | |
| : GetAsUnsignedConstant()); | |
| break; | |
| case dwarf::DW_AT_enum_class: | |
| if (GetFlag(FormValue)) | |
| CurrentElement->setIsEnumClass(); | |
| break; | |
| case dwarf::DW_AT_external: | |
| if (GetFlag(FormValue)) | |
| CurrentElement->setIsExternal(); | |
| break; | |
| case dwarf::DW_AT_GNU_discriminator: | |
| CurrentElement->setDiscriminator(*FormValue.getAsUnsignedConstant()); | |
| break; | |
| case dwarf::DW_AT_inline: | |
| CurrentElement->setInlineCode(*FormValue.getAsUnsignedConstant()); | |
| break; | |
| case dwarf::DW_AT_lower_bound: | |
| CurrentElement->setLowerBound(GetBoundValue(FormValue)); | |
| break; | |
| case dwarf::DW_AT_name: | |
| CurrentElement->setName(dwarf::toStringRef(FormValue)); | |
| break; | |
| case dwarf::DW_AT_linkage_name: | |
| case dwarf::DW_AT_MIPS_linkage_name: | |
| CurrentElement->setLinkageName(dwarf::toStringRef(FormValue)); | |
| break; | |
| case dwarf::DW_AT_producer: | |
| if (options().getAttributeProducer()) | |
| CurrentElement->setProducer(dwarf::toStringRef(FormValue)); | |
| break; | |
| case dwarf::DW_AT_upper_bound: | |
| CurrentElement->setUpperBound(GetBoundValue(FormValue)); | |
| break; | |
| case dwarf::DW_AT_virtuality: | |
| CurrentElement->setVirtualityCode(*FormValue.getAsUnsignedConstant()); | |
| break; | |
| case dwarf::DW_AT_abstract_origin: | |
| case dwarf::DW_AT_call_origin: | |
| case dwarf::DW_AT_extension: | |
| case dwarf::DW_AT_import: | |
| case dwarf::DW_AT_specification: | |
| case dwarf::DW_AT_type: | |
| updateReference(AttrSpec.Attr, FormValue); | |
| break; | |
| case dwarf::DW_AT_low_pc: | |
| if (options().getGeneralCollectRanges()) { | |
| FoundLowPC = true; | |
| // For toolchains that support the removal of unused code, the linker | |
| // marks functions that have been removed, by setting the value for the | |
| // low_pc to the max address. | |
| if (std::optional<uint64_t> Value = FormValue.getAsAddress()) { | |
| CurrentLowPC = *Value; | |
| } else { | |
| uint64_t UValue = FormValue.getRawUValue(); | |
| if (U->getAddrOffsetSectionItem(UValue)) { | |
| CurrentLowPC = *FormValue.getAsAddress(); | |
| } else { | |
| FoundLowPC = false; | |
| // We are dealing with an index into the .debug_addr section. | |
| LLVM_DEBUG({ | |
| dbgs() << format("indexed (%8.8x) address = ", (uint32_t)UValue); | |
| }); | |
| } | |
| } | |
| if (FoundLowPC) { | |
| if (CurrentLowPC == MaxAddress) | |
| CurrentElement->setIsDiscarded(); | |
| if (CurrentElement->isCompileUnit()) | |
| setCUBaseAddress(CurrentLowPC); | |
| } | |
| } | |
| break; | |
| case dwarf::DW_AT_high_pc: | |
| if (options().getGeneralCollectRanges()) { | |
| FoundHighPC = true; | |
| if (std::optional<uint64_t> Address = FormValue.getAsAddress()) | |
| // High PC is an address. | |
| CurrentHighPC = *Address; | |
| if (std::optional<uint64_t> Offset = FormValue.getAsUnsignedConstant()) | |
| // High PC is an offset from LowPC. | |
| CurrentHighPC = CurrentLowPC + *Offset; | |
| // Store the real upper limit for the address range. | |
| if (UpdateHighAddress && CurrentHighPC > 0) | |
| --CurrentHighPC; | |
| if (CurrentElement->isCompileUnit()) | |
| setCUHighAddress(CurrentHighPC); | |
| } | |
| break; | |
| case dwarf::DW_AT_ranges: | |
| if (RangesDataAvailable && options().getGeneralCollectRanges()) { | |
| auto GetRanges = [](const DWARFFormValue &FormValue, | |
| DWARFUnit *U) -> Expected<DWARFAddressRangesVector> { | |
| if (FormValue.getForm() == dwarf::DW_FORM_rnglistx) | |
| return U->findRnglistFromIndex(*FormValue.getAsSectionOffset()); | |
| return U->findRnglistFromOffset(*FormValue.getAsSectionOffset()); | |
| }; | |
| Expected<DWARFAddressRangesVector> RangesOrError = | |
| GetRanges(FormValue, U); | |
| if (!RangesOrError) { | |
| LLVM_DEBUG({ | |
| std::string TheError(toString(RangesOrError.takeError())); | |
| dbgs() << format("error decoding address ranges = ", | |
| TheError.c_str()); | |
| }); | |
| consumeError(RangesOrError.takeError()); | |
| break; | |
| } | |
| // The address ranges are absolute. There is no need to add any addend. | |
| DWARFAddressRangesVector Ranges = RangesOrError.get(); | |
| for (DWARFAddressRange &Range : Ranges) { | |
| // This seems to be a tombstone for empty ranges. | |
| if (Range.LowPC == Range.HighPC) | |
| continue; | |
| // Store the real upper limit for the address range. | |
| if (UpdateHighAddress && Range.HighPC > 0) | |
| --Range.HighPC; | |
| // Add the pair of addresses. | |
| CurrentScope->addObject(Range.LowPC, Range.HighPC); | |
| // If the scope is the CU, do not update the ranges set. | |
| if (!CurrentElement->isCompileUnit()) | |
| CurrentRanges.emplace_back(Range.LowPC, Range.HighPC); | |
| } | |
| } | |
| break; | |
| // Get the location list for the symbol. | |
| case dwarf::DW_AT_data_member_location: | |
| if (options().getAttributeAnyLocation()) | |
| processLocationMember(AttrSpec.Attr, FormValue, Die, OffsetOnEntry); | |
| break; | |
| // Get the location list for the symbol. | |
| case dwarf::DW_AT_location: | |
| case dwarf::DW_AT_string_length: | |
| case dwarf::DW_AT_use_location: | |
| if (options().getAttributeAnyLocation() && CurrentSymbol) | |
| processLocationList(AttrSpec.Attr, FormValue, Die, OffsetOnEntry); | |
| break; | |
| case dwarf::DW_AT_call_data_value: | |
| case dwarf::DW_AT_call_value: | |
| case dwarf::DW_AT_GNU_call_site_data_value: | |
| case dwarf::DW_AT_GNU_call_site_value: | |
| if (options().getAttributeAnyLocation() && CurrentSymbol) | |
| processLocationList(AttrSpec.Attr, FormValue, Die, OffsetOnEntry, | |
| /*CallSiteLocation=*/true); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| LVScope *LVELFReader::processOneDie(const DWARFDie &InputDIE, LVScope *Parent, | |
| DWARFDie &SkeletonDie) { | |
| // If the input DIE corresponds to the compile unit, it can be: | |
| // a) Simple DWARF: a standard DIE. Ignore the skeleton DIE (is empty). | |
| // b) Split DWARF: the DIE for the split DWARF. The skeleton is the DIE | |
| // for the skeleton DWARF. Process both DIEs. | |
| const DWARFDie &DIE = SkeletonDie.isValid() ? SkeletonDie : InputDIE; | |
| DWARFDataExtractor DebugInfoData = | |
| DIE.getDwarfUnit()->getDebugInfoExtractor(); | |
| LVOffset Offset = DIE.getOffset(); | |
| // Reset values for the current DIE. | |
| CurrentLowPC = 0; | |
| CurrentHighPC = 0; | |
| CurrentOffset = Offset; | |
| CurrentEndOffset = 0; | |
| FoundLowPC = false; | |
| FoundHighPC = false; | |
| // Process supported attributes. | |
| if (DebugInfoData.isValidOffset(Offset)) { | |
| LLVM_DEBUG({ | |
| dbgs() << "DIE: " << hexValue(Offset) << formatv(" {0}", DIE.getTag()) | |
| << "\n"; | |
| }); | |
| // Create the logical view element for the current DIE. | |
| dwarf::Tag Tag = DIE.getTag(); | |
| CurrentElement = createElement(Tag); | |
| if (!CurrentElement) | |
| return CurrentScope; | |
| CurrentElement->setTag(Tag); | |
| CurrentElement->setOffset(Offset); | |
| if (options().getAttributeAnySource() && CurrentElement->isCompileUnit()) | |
| addCompileUnitOffset(Offset, | |
| static_cast<LVScopeCompileUnit *>(CurrentElement)); | |
| // Insert the newly created element into the element symbol table. If the | |
| // element is in the list, it means there are previously created elements | |
| // referencing this element. | |
| if (ElementTable.find(Offset) == ElementTable.end()) { | |
| // No previous references to this offset. | |
| ElementTable.emplace( | |
| std::piecewise_construct, std::forward_as_tuple(Offset), | |
| std::forward_as_tuple(CurrentElement, LVElementSet())); | |
| } else { | |
| // There are previous references to this element. We need to update the | |
| // element and all the references pointing to this element. | |
| LVElementEntry &Reference = ElementTable[Offset]; | |
| Reference.first = CurrentElement; | |
| // Traverse the element set and update the elements (backtracking). | |
| // Using the bit associated with 'type' or 'reference' allows us to set | |
| // the correct target. | |
| for (LVElement *Target : Reference.second) | |
| Target->getHasReference() ? Target->setReference(CurrentElement) | |
| : Target->setType(CurrentElement); | |
| // Clear the pending elements. | |
| Reference.second.clear(); | |
| } | |
| // Add the current element to its parent as there are attributes | |
| // (locations) that require the scope level. | |
| if (CurrentScope) | |
| Parent->addElement(CurrentScope); | |
| else if (CurrentSymbol) | |
| Parent->addElement(CurrentSymbol); | |
| else if (CurrentType) | |
| Parent->addElement(CurrentType); | |
| // Process the attributes for the given DIE. | |
| auto ProcessAttributes = [&](const DWARFDie &TheDIE, | |
| DWARFDataExtractor &DebugData) { | |
| CurrentEndOffset = Offset; | |
| uint32_t abbrCode = DebugData.getULEB128(&CurrentEndOffset); | |
| if (abbrCode) { | |
| if (const DWARFAbbreviationDeclaration *AbbrevDecl = | |
| TheDIE.getAbbreviationDeclarationPtr()) | |
| if (AbbrevDecl) | |
| for (const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec : | |
| AbbrevDecl->attributes()) | |
| processOneAttribute(TheDIE, &CurrentEndOffset, AttrSpec); | |
| } | |
| }; | |
| ProcessAttributes(DIE, DebugInfoData); | |
| // If the input DIE is for a compile unit, process its attributes in | |
| // the case of split DWARF, to override any common attribute values. | |
| if (SkeletonDie.isValid()) { | |
| DWARFDataExtractor DebugInfoData = | |
| InputDIE.getDwarfUnit()->getDebugInfoExtractor(); | |
| LVOffset Offset = InputDIE.getOffset(); | |
| if (DebugInfoData.isValidOffset(Offset)) | |
| ProcessAttributes(InputDIE, DebugInfoData); | |
| } | |
| } | |
| if (CurrentScope) { | |
| if (CurrentScope->getCanHaveRanges()) { | |
| // If the scope has ranges, they are already added to the scope. | |
| // Add any collected LowPC/HighPC values. | |
| bool IsCompileUnit = CurrentScope->getIsCompileUnit(); | |
| if (FoundLowPC && FoundHighPC) { | |
| CurrentScope->addObject(CurrentLowPC, CurrentHighPC); | |
| if (!IsCompileUnit) { | |
| // If the scope is a function, add it to the public names. | |
| if ((options().getAttributePublics() || | |
| options().getPrintAnyLine()) && | |
| CurrentScope->getIsFunction() && | |
| !CurrentScope->getIsInlinedFunction()) | |
| CompileUnit->addPublicName(CurrentScope, CurrentLowPC, | |
| CurrentHighPC); | |
| } | |
| } | |
| // Look for scopes with ranges and no linkage name information that | |
| // are referencing another scopes via DW_AT_specification. They are | |
| // possible candidates for a comdat scope. | |
| if (CurrentScope->getHasRanges() && | |
| !CurrentScope->getLinkageNameIndex() && | |
| CurrentScope->getHasReferenceSpecification()) { | |
| // Get the linkage name in order to search for a possible comdat. | |
| std::optional<DWARFFormValue> LinkageDIE = | |
| DIE.findRecursively(dwarf::DW_AT_linkage_name); | |
| if (LinkageDIE.has_value()) { | |
| StringRef Name(dwarf::toStringRef(LinkageDIE)); | |
| if (!Name.empty()) | |
| CurrentScope->setLinkageName(Name); | |
| } | |
| } | |
| // If the current scope is in the 'LinkageNames' table, update its | |
| // logical scope. For other scopes, always we will assume the default | |
| // ".text" section index. | |
| LVSectionIndex SectionIndex = updateSymbolTable(CurrentScope); | |
| if (CurrentScope->getIsComdat()) | |
| CompileUnit->setHasComdatScopes(); | |
| // Update section index contained ranges. | |
| if (SectionIndex) { | |
| if (!CurrentRanges.empty()) { | |
| for (LVAddressRange &Range : CurrentRanges) | |
| addSectionRange(SectionIndex, CurrentScope, Range.first, | |
| Range.second); | |
| CurrentRanges.clear(); | |
| } | |
| // If the scope is the CU, do not update the ranges set. | |
| if (FoundLowPC && FoundHighPC && !IsCompileUnit) { | |
| addSectionRange(SectionIndex, CurrentScope, CurrentLowPC, | |
| CurrentHighPC); | |
| } | |
| } | |
| } | |
| // Mark member functions. | |
| if (Parent->getIsAggregate()) | |
| CurrentScope->setIsMember(); | |
| } | |
| // Keep track of symbols with locations. | |
| if (options().getAttributeAnyLocation() && CurrentSymbol && | |
| CurrentSymbol->getHasLocation()) | |
| SymbolsWithLocations.push_back(CurrentSymbol); | |
| // If we have template parameters, mark the parent as template. | |
| if (CurrentType && CurrentType->getIsTemplateParam()) | |
| Parent->setIsTemplate(); | |
| return CurrentScope; | |
| } | |
| void LVELFReader::traverseDieAndChildren(DWARFDie &DIE, LVScope *Parent, | |
| DWARFDie &SkeletonDie) { | |
| // Process the current DIE. | |
| LVScope *Scope = processOneDie(DIE, Parent, SkeletonDie); | |
| if (Scope) { | |
| LVOffset Lower = DIE.getOffset(); | |
| LVOffset Upper = CurrentEndOffset; | |
| DWARFDie DummyDie; | |
| // Traverse the children chain. | |
| DWARFDie Child = DIE.getFirstChild(); | |
| while (Child) { | |
| traverseDieAndChildren(Child, Scope, DummyDie); | |
| Upper = Child.getOffset(); | |
| Child = Child.getSibling(); | |
| } | |
| // Calculate contributions to the debug info section. | |
| if (options().getPrintSizes() && Upper) | |
| CompileUnit->addSize(Scope, Lower, Upper); | |
| } | |
| } | |
| void LVELFReader::processLocationGaps() { | |
| if (options().getAttributeAnyLocation()) | |
| for (LVSymbol *Symbol : SymbolsWithLocations) | |
| Symbol->fillLocationGaps(); | |
| } | |
| void LVELFReader::createLineAndFileRecords( | |
| const DWARFDebugLine::LineTable *Lines) { | |
| if (!Lines) | |
| return; | |
| // Get the source filenames. | |
| if (!Lines->Prologue.FileNames.empty()) | |
| for (const DWARFDebugLine::FileNameEntry &Entry : | |
| Lines->Prologue.FileNames) { | |
| std::string Directory; | |
| if (Lines->getDirectoryForEntry(Entry, Directory)) | |
| Directory = transformPath(Directory); | |
| if (Directory.empty()) | |
| Directory = std::string(CompileUnit->getCompilationDirectory()); | |
| std::string File = transformPath(dwarf::toStringRef(Entry.Name)); | |
| std::string String; | |
| raw_string_ostream(String) << Directory << "/" << File; | |
| CompileUnit->addFilename(String); | |
| } | |
| // In DWARF5 the file indexes start at 0; | |
| bool IncrementIndex = Lines->Prologue.getVersion() >= 5; | |
| // Get the source lines if requested by command line option. | |
| if (options().getPrintLines() && Lines->Rows.size()) | |
| for (const DWARFDebugLine::Row &Row : Lines->Rows) { | |
| // Here we collect logical debug lines in CULines. Later on, | |
| // the 'processLines()' function will move each created logical line | |
| // to its enclosing logical scope, using the debug ranges information | |
| // and they will be released when its scope parent is deleted. | |
| LVLineDebug *Line = new LVLineDebug(); | |
| CULines.push_back(Line); | |
| Line->setAddress(Row.Address.Address); | |
| Line->setFilename( | |
| CompileUnit->getFilename(IncrementIndex ? Row.File + 1 : Row.File)); | |
| Line->setLineNumber(Row.Line); | |
| if (Row.Discriminator) | |
| Line->setDiscriminator(Row.Discriminator); | |
| if (Row.IsStmt) | |
| Line->setIsNewStatement(); | |
| if (Row.BasicBlock) | |
| Line->setIsBasicBlock(); | |
| if (Row.EndSequence) | |
| Line->setIsEndSequence(); | |
| if (Row.EpilogueBegin) | |
| Line->setIsEpilogueBegin(); | |
| if (Row.PrologueEnd) | |
| Line->setIsPrologueEnd(); | |
| LLVM_DEBUG({ | |
| dbgs() << "Address: " << hexValue(Line->getAddress()) | |
| << " Line: " << Line->lineNumberAsString(/*ShowZero=*/true) | |
| << "\n"; | |
| }); | |
| } | |
| } | |
| std::string LVELFReader::getRegisterName(LVSmall Opcode, uint64_t Operands[2]) { | |
| // The 'prettyPrintRegisterOp' function uses the DWARFUnit to support | |
| // DW_OP_regval_type. At this point we are operating on a logical view | |
| // item, with no access to the underlying DWARF data used by LLVM. | |
| // We do not support DW_OP_regval_type here. | |
| if (Opcode == dwarf::DW_OP_regval_type) | |
| return {}; | |
| std::string string; | |
| raw_string_ostream Stream(string); | |
| DIDumpOptions DumpOpts; | |
| auto *MCRegInfo = MRI.get(); | |
| auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum, bool IsEH) -> StringRef { | |
| if (!MCRegInfo) | |
| return {}; | |
| if (std::optional<unsigned> LLVMRegNum = | |
| MCRegInfo->getLLVMRegNum(DwarfRegNum, IsEH)) | |
| if (const char *RegName = MCRegInfo->getName(*LLVMRegNum)) | |
| return StringRef(RegName); | |
| return {}; | |
| }; | |
| DumpOpts.GetNameForDWARFReg = GetRegName; | |
| DWARFExpression::prettyPrintRegisterOp(/*U=*/nullptr, Stream, DumpOpts, | |
| Opcode, Operands); | |
| return Stream.str(); | |
| } | |
| Error LVELFReader::createScopes() { | |
| LLVM_DEBUG({ | |
| W.startLine() << "\n"; | |
| W.printString("File", Obj.getFileName().str()); | |
| W.printString("Format", FileFormatName); | |
| }); | |
| if (Error Err = LVReader::createScopes()) | |
| return Err; | |
| // As the DwarfContext object is valid only during the scopes creation, | |
| // we need to create our own Target information, to be used during the | |
| // logical view printing, in the case of instructions being requested. | |
| std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(Obj); | |
| if (!DwarfContext) | |
| return createStringError(errc::invalid_argument, | |
| "Could not create DWARF information: %s", | |
| getFilename().str().c_str()); | |
| if (Error Err = loadTargetInfo(Obj)) | |
| return Err; | |
| // Create a mapping for virtual addresses. | |
| mapVirtualAddress(Obj); | |
| // Select the correct compile unit range, depending if we are dealing with | |
| // a standard or split DWARF object. | |
| DWARFContext::compile_unit_range CompileUnits = | |
| DwarfContext->getNumCompileUnits() ? DwarfContext->compile_units() | |
| : DwarfContext->dwo_compile_units(); | |
| for (const std::unique_ptr<DWARFUnit> &CU : CompileUnits) { | |
| // Deduction of index used for the line records. | |
| // | |
| // For the following test case: test.cpp | |
| // void foo(void ParamPtr) { } | |
| // Both GCC and Clang generate DWARF-5 .debug_line layout. | |
| // * GCC (GNU C++17 11.3.0) - All DW_AT_decl_file use index 1. | |
| // | |
| // .debug_info: | |
| // format = DWARF32, version = 0x0005 | |
| // DW_TAG_compile_unit | |
| // DW_AT_name ("test.cpp") | |
| // DW_TAG_subprogram ("foo") | |
| // DW_AT_decl_file (1) | |
| // DW_TAG_formal_parameter ("ParamPtr") | |
| // DW_AT_decl_file (1) | |
| // .debug_line: | |
| // Line table prologue: format (DWARF32), version (5) | |
| // include_directories[0] = "..." | |
| // file_names[0]: name ("test.cpp"), dir_index (0) | |
| // file_names[1]: name ("test.cpp"), dir_index (0) | |
| // * Clang (14.0.6) - All DW_AT_decl_file use index 0. | |
| // | |
| // .debug_info: | |
| // format = DWARF32, version = 0x0005 | |
| // DW_AT_producer ("clang version 14.0.6") | |
| // DW_AT_name ("test.cpp") | |
| // | |
| // DW_TAG_subprogram ("foo") | |
| // DW_AT_decl_file (0) | |
| // DW_TAG_formal_parameter ("ParamPtr") | |
| // DW_AT_decl_file (0) | |
| // .debug_line: | |
| // Line table prologue: format (DWARF32), version (5) | |
| // include_directories[0] = "..." | |
| // file_names[0]: name ("test.cpp"), dir_index (0) | |
| // From DWARFDebugLine::getFileNameByIndex documentation: | |
| // In Dwarf 4, the files are 1-indexed. | |
| // In Dwarf 5, the files are 0-indexed. | |
| // Additional discussions here: | |
| // https://www.mail-archive.com/dwarf-discuss@lists.dwarfstd.org/msg00883.html | |
| // The ELF Reader is expecting the files are 1-indexed, so using | |
| // the .debug_line header information decide if the indexed require | |
| // an internal adjustment. | |
| // For the case of GCC (DWARF5), if the entries[0] and [1] are the | |
| // same, do not perform any adjustment. | |
| auto DeduceIncrementFileIndex = [&]() -> bool { | |
| if (CU->getVersion() < 5) | |
| // DWARF-4 or earlier -> Don't increment index. | |
| return false; | |
| if (const DWARFDebugLine::LineTable *LT = | |
| CU->getContext().getLineTableForUnit(CU.get())) { | |
| // Check if there are at least 2 entries and if they are the same. | |
| if (LT->hasFileAtIndex(0) && LT->hasFileAtIndex(1)) { | |
| const DWARFDebugLine::FileNameEntry &EntryZero = | |
| LT->Prologue.getFileNameEntry(0); | |
| const DWARFDebugLine::FileNameEntry &EntryOne = | |
| LT->Prologue.getFileNameEntry(1); | |
| // Check directory indexes. | |
| if (EntryZero.DirIdx != EntryOne.DirIdx) | |
| // DWARF-5 -> Increment index. | |
| return true; | |
| // Check filename. | |
| std::string FileZero; | |
| std::string FileOne; | |
| StringRef None; | |
| LT->getFileNameByIndex( | |
| 0, None, DILineInfoSpecifier::FileLineInfoKind::RawValue, | |
| FileZero); | |
| LT->getFileNameByIndex( | |
| 1, None, DILineInfoSpecifier::FileLineInfoKind::RawValue, | |
| FileOne); | |
| return FileZero.compare(FileOne); | |
| } | |
| } | |
| // DWARF-5 -> Increment index. | |
| return true; | |
| }; | |
| // The ELF reader expects the indexes as 1-indexed. | |
| IncrementFileIndex = DeduceIncrementFileIndex(); | |
| DWARFDie UnitDie = CU->getUnitDIE(); | |
| SmallString<16> DWOAlternativeLocation; | |
| if (UnitDie) { | |
| std::optional<const char *> DWOFileName = | |
| CU->getVersion() >= 5 | |
| ? dwarf::toString(UnitDie.find(dwarf::DW_AT_dwo_name)) | |
| : dwarf::toString(UnitDie.find(dwarf::DW_AT_GNU_dwo_name)); | |
| StringRef From(DWOFileName.value_or("")); | |
| DWOAlternativeLocation = createAlternativePath(From); | |
| } | |
| // The current CU can be a normal compile unit (standard) or a skeleton | |
| // compile unit (split). For both cases, the returned die, will be used | |
| // to create the logical scopes. | |
| DWARFDie CUDie = CU->getNonSkeletonUnitDIE( | |
| /*ExtractUnitDIEOnly=*/false, | |
| /*DWOAlternativeLocation=*/DWOAlternativeLocation); | |
| if (!CUDie.isValid()) | |
| continue; | |
| // The current unit corresponds to the .dwo file. We need to get the | |
| // skeleton unit and query for any ranges that will enclose any ranges | |
| // in the non-skeleton unit. | |
| DWARFDie DummyDie; | |
| DWARFDie SkeletonDie = | |
| CUDie.getDwarfUnit()->isDWOUnit() ? CU->getUnitDIE(false) : DummyDie; | |
| // Disable the ranges processing if we have just a single .dwo object, | |
| // as any DW_AT_ranges will access not available range information. | |
| RangesDataAvailable = | |
| (!CUDie.getDwarfUnit()->isDWOUnit() || | |
| (SkeletonDie.isValid() ? !SkeletonDie.getDwarfUnit()->isDWOUnit() | |
| : true)); | |
| traverseDieAndChildren(CUDie, Root, SkeletonDie); | |
| createLineAndFileRecords(DwarfContext->getLineTableForUnit(CU.get())); | |
| if (Error Err = createInstructions()) | |
| return Err; | |
| // Process the compilation unit, as there are cases where enclosed | |
| // functions have the same ranges values. Insert the compilation unit | |
| // ranges at the end, to allow enclosing ranges to be first in the list. | |
| LVSectionIndex SectionIndex = getSectionIndex(CompileUnit); | |
| addSectionRange(SectionIndex, CompileUnit); | |
| LVRange *ScopesWithRanges = getSectionRanges(SectionIndex); | |
| ScopesWithRanges->sort(); | |
| processLines(&CULines, SectionIndex); | |
| processLocationGaps(); | |
| // These are per compile unit. | |
| ScopesWithRanges->clear(); | |
| SymbolsWithLocations.clear(); | |
| CULines.clear(); | |
| } | |
| return Error::success(); | |
| } | |
| // Get the location information for the associated attribute. | |
| void LVELFReader::processLocationList(dwarf::Attribute Attr, | |
| const DWARFFormValue &FormValue, | |
| const DWARFDie &Die, | |
| uint64_t OffsetOnEntry, | |
| bool CallSiteLocation) { | |
| auto ProcessLocationExpression = [&](const DWARFExpression &Expression) { | |
| // DW_OP_const_type is variable-length and has 3 | |
| // operands. DWARFExpression thus far only supports 2. | |
| uint64_t Operands[2] = {0}; | |
| for (const DWARFExpression::Operation &Op : Expression) { | |
| DWARFExpression::Operation::Description Description = Op.getDescription(); | |
| for (unsigned Operand = 0; Operand < 2; ++Operand) { | |
| if (Description.Op[Operand] == DWARFExpression::Operation::SizeNA) | |
| break; | |
| Operands[Operand] = Op.getRawOperand(Operand); | |
| } | |
| CurrentSymbol->addLocationOperands(Op.getCode(), Operands[0], | |
| Operands[1]); | |
| } | |
| }; | |
| DWARFUnit *U = Die.getDwarfUnit(); | |
| DWARFContext &DwarfContext = U->getContext(); | |
| bool IsLittleEndian = DwarfContext.isLittleEndian(); | |
| if (FormValue.isFormClass(DWARFFormValue::FC_Block) || | |
| (DWARFAttribute::mayHaveLocationExpr(Attr) && | |
| FormValue.isFormClass(DWARFFormValue::FC_Exprloc))) { | |
| ArrayRef<uint8_t> Expr = *FormValue.getAsBlock(); | |
| DataExtractor Data(StringRef((const char *)Expr.data(), Expr.size()), | |
| IsLittleEndian, 0); | |
| DWARFExpression Expression(Data, U->getAddressByteSize(), | |
| U->getFormParams().Format); | |
| // Add location and operation entries. | |
| CurrentSymbol->addLocation(Attr, /*LowPC=*/0, /*HighPC=*/-1, | |
| /*SectionOffset=*/0, OffsetOnEntry, | |
| CallSiteLocation); | |
| ProcessLocationExpression(Expression); | |
| return; | |
| } | |
| if (DWARFAttribute::mayHaveLocationList(Attr) && | |
| FormValue.isFormClass(DWARFFormValue::FC_SectionOffset)) { | |
| uint64_t Offset = *FormValue.getAsSectionOffset(); | |
| if (FormValue.getForm() == dwarf::DW_FORM_loclistx) { | |
| std::optional<uint64_t> LoclistOffset = U->getLoclistOffset(Offset); | |
| if (!LoclistOffset) | |
| return; | |
| Offset = *LoclistOffset; | |
| } | |
| uint64_t BaseAddr = 0; | |
| if (std::optional<SectionedAddress> BA = U->getBaseAddress()) | |
| BaseAddr = BA->Address; | |
| LVAddress LowPC = 0; | |
| LVAddress HighPC = 0; | |
| auto ProcessLocationEntry = [&](const DWARFLocationEntry &Entry) { | |
| if (Entry.Kind == dwarf::DW_LLE_base_address) { | |
| BaseAddr = Entry.Value0; | |
| return; | |
| } | |
| if (Entry.Kind == dwarf::DW_LLE_offset_pair) { | |
| LowPC = BaseAddr + Entry.Value0; | |
| HighPC = BaseAddr + Entry.Value1; | |
| DWARFAddressRange Range{LowPC, HighPC, Entry.SectionIndex}; | |
| if (Range.SectionIndex == SectionedAddress::UndefSection) | |
| Range.SectionIndex = Entry.SectionIndex; | |
| DWARFLocationExpression Loc{Range, Entry.Loc}; | |
| DWARFDataExtractor Data(Loc.Expr, IsLittleEndian, | |
| U->getAddressByteSize()); | |
| DWARFExpression Expression(Data, U->getAddressByteSize()); | |
| // Store the real upper limit for the address range. | |
| if (UpdateHighAddress && HighPC > 0) | |
| --HighPC; | |
| // Add location and operation entries. | |
| CurrentSymbol->addLocation(Attr, LowPC, HighPC, Offset, OffsetOnEntry, | |
| CallSiteLocation); | |
| ProcessLocationExpression(Expression); | |
| } | |
| }; | |
| Error E = U->getLocationTable().visitLocationList( | |
| &Offset, [&](const DWARFLocationEntry &E) { | |
| ProcessLocationEntry(E); | |
| return true; | |
| }); | |
| if (E) | |
| consumeError(std::move(E)); | |
| } | |
| } | |
| void LVELFReader::processLocationMember(dwarf::Attribute Attr, | |
| const DWARFFormValue &FormValue, | |
| const DWARFDie &Die, | |
| uint64_t OffsetOnEntry) { | |
| // Check if the value is an integer constant. | |
| if (FormValue.isFormClass(DWARFFormValue::FC_Constant)) | |
| // Add a record to hold a constant as location. | |
| CurrentSymbol->addLocationConstant(Attr, *FormValue.getAsUnsignedConstant(), | |
| OffsetOnEntry); | |
| else | |
| // This is a a location description, or a reference to one. | |
| processLocationList(Attr, FormValue, Die, OffsetOnEntry); | |
| } | |
| // Update the current element with the reference. | |
| void LVELFReader::updateReference(dwarf::Attribute Attr, | |
| const DWARFFormValue &FormValue) { | |
| // We are assuming that DW_AT_specification, DW_AT_abstract_origin, | |
| // DW_AT_type and DW_AT_extension do not appear at the same time | |
| // in the same DIE. | |
| uint64_t Reference = *FormValue.getAsReference(); | |
| // Get target for the given reference, if already created. | |
| LVElement *Target = getElementForOffset(Reference, CurrentElement); | |
| // Check if we are dealing with cross CU references. | |
| if (FormValue.getForm() == dwarf::DW_FORM_ref_addr) { | |
| if (Target) { | |
| // The global reference is ready. Mark it as global. | |
| Target->setIsGlobalReference(); | |
| // Remove global reference from the unseen list. | |
| removeGlobalOffset(Reference); | |
| } else | |
| // Record the unseen cross CU reference. | |
| addGlobalOffset(Reference); | |
| } | |
| // At this point, 'Target' can be null, in the case of the target element | |
| // not being seen. But the correct bit is set, to indicate that the target | |
| // is being referenced by (abstract_origin, extension, specification) or | |
| // (import, type). | |
| // We must differentiate between the kind of reference. This is needed to | |
| // complete inlined function instances with dropped abstract references, | |
| // in order to facilitate a logical comparison. | |
| switch (Attr) { | |
| case dwarf::DW_AT_abstract_origin: | |
| case dwarf::DW_AT_call_origin: | |
| CurrentElement->setReference(Target); | |
| CurrentElement->setHasReferenceAbstract(); | |
| break; | |
| case dwarf::DW_AT_extension: | |
| CurrentElement->setReference(Target); | |
| CurrentElement->setHasReferenceExtension(); | |
| break; | |
| case dwarf::DW_AT_specification: | |
| CurrentElement->setReference(Target); | |
| CurrentElement->setHasReferenceSpecification(); | |
| break; | |
| case dwarf::DW_AT_import: | |
| case dwarf::DW_AT_type: | |
| CurrentElement->setType(Target); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| // Get an element given the DIE offset. | |
| LVElement *LVELFReader::getElementForOffset(LVOffset Offset, | |
| LVElement *Element) { | |
| LVElement *Target = nullptr; | |
| // Search offset in the cross references. | |
| LVElementReference::iterator Iter = ElementTable.find(Offset); | |
| if (Iter == ElementTable.end()) | |
| // Reference to an unseen element. | |
| ElementTable.emplace(std::piecewise_construct, | |
| std::forward_as_tuple(Offset), | |
| std::forward_as_tuple(nullptr, LVElementSet{Element})); | |
| else { | |
| // There are previous references to this element. We need to update the | |
| // element and all the references pointing to this element. | |
| LVElementEntry &Reference = Iter->second; | |
| Target = Reference.first; | |
| if (!Target) | |
| // Add the element to the set. | |
| Reference.second.insert(Element); | |
| } | |
| return Target; | |
| } | |
| Error LVELFReader::loadTargetInfo(const ObjectFile &Obj) { | |
| // Detect the architecture from the object file. We usually don't need OS | |
| // info to lookup a target and create register info. | |
| Triple TT; | |
| TT.setArch(Triple::ArchType(Obj.getArch())); | |
| TT.setVendor(Triple::UnknownVendor); | |
| TT.setOS(Triple::UnknownOS); | |
| // Features to be passed to target/subtarget | |
| Expected<SubtargetFeatures> Features = Obj.getFeatures(); | |
| SubtargetFeatures FeaturesValue; | |
| if (!Features) { | |
| consumeError(Features.takeError()); | |
| FeaturesValue = SubtargetFeatures(); | |
| } | |
| FeaturesValue = *Features; | |
| return loadGenericTargetInfo(TT.str(), FeaturesValue.getString()); | |
| } | |
| void LVELFReader::mapRangeAddress(const ObjectFile &Obj) { | |
| for (auto Iter = Obj.symbol_begin(); Iter != Obj.symbol_end(); ++Iter) { | |
| const SymbolRef &Symbol = *Iter; | |
| Expected<SymbolRef::Type> TypeOrErr = Symbol.getType(); | |
| if (!TypeOrErr) { | |
| consumeError(TypeOrErr.takeError()); | |
| continue; | |
| } | |
| // Process only symbols that represent a function. | |
| SymbolRef::Type Type = *TypeOrErr; | |
| if (Type != SymbolRef::ST_Function) | |
| continue; | |
| // In the case of a Mach-O STAB symbol, get its section only if | |
| // the STAB symbol's section field refers to a valid section index. | |
| // Otherwise the symbol may error trying to load a section that | |
| // does not exist. | |
| const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(&Obj); | |
| bool IsSTAB = false; | |
| if (MachO) { | |
| DataRefImpl SymDRI = Symbol.getRawDataRefImpl(); | |
| uint8_t NType = | |
| (MachO->is64Bit() ? MachO->getSymbol64TableEntry(SymDRI).n_type | |
| : MachO->getSymbolTableEntry(SymDRI).n_type); | |
| if (NType & MachO::N_STAB) | |
| IsSTAB = true; | |
| } | |
| Expected<section_iterator> IterOrErr = Symbol.getSection(); | |
| if (!IterOrErr) { | |
| consumeError(IterOrErr.takeError()); | |
| continue; | |
| } | |
| section_iterator Section = IsSTAB ? Obj.section_end() : *IterOrErr; | |
| if (Section == Obj.section_end()) | |
| continue; | |
| // Get the symbol value. | |
| Expected<uint64_t> AddressOrErr = Symbol.getAddress(); | |
| if (!AddressOrErr) { | |
| consumeError(AddressOrErr.takeError()); | |
| continue; | |
| } | |
| uint64_t Address = *AddressOrErr; | |
| // Get symbol name. | |
| StringRef Name; | |
| Expected<StringRef> NameOrErr = Symbol.getName(); | |
| if (!NameOrErr) { | |
| consumeError(NameOrErr.takeError()); | |
| continue; | |
| } | |
| Name = *NameOrErr; | |
| // Check if the symbol is Comdat. | |
| Expected<uint32_t> FlagsOrErr = Symbol.getFlags(); | |
| if (!FlagsOrErr) { | |
| consumeError(FlagsOrErr.takeError()); | |
| continue; | |
| } | |
| uint32_t Flags = *FlagsOrErr; | |
| // Mark the symbol as 'comdat' in any of the following cases: | |
| // - Symbol has the SF_Weak flag or | |
| // - Symbol section index different from the DotTextSectionIndex. | |
| LVSectionIndex SectionIndex = Section->getIndex(); | |
| bool IsComdat = | |
| (Flags & SymbolRef::SF_Weak) || (SectionIndex != DotTextSectionIndex); | |
| // Record the symbol name (linkage) and its loading address. | |
| addToSymbolTable(Name, Address, SectionIndex, IsComdat); | |
| } | |
| } | |
| void LVELFReader::sortScopes() { Root->sort(); } | |
| void LVELFReader::print(raw_ostream &OS) const { | |
| OS << "LVType\n"; | |
| LLVM_DEBUG(dbgs() << "CreateReaders\n"); | |
| } |