bool DWARFUnit::parseDWO() { if (isDWO) return false; if (DWO.get()) return false; DWARFDie UnitDie = getUnitDIE(); if (!UnitDie) return false; auto DWOFileName = dwarf::toString(UnitDie.find(DW_AT_GNU_dwo_name)); if (!DWOFileName) return false; auto CompilationDir = dwarf::toString(UnitDie.find(DW_AT_comp_dir)); SmallString<16> AbsolutePath; if (sys::path::is_relative(*DWOFileName) && CompilationDir && *CompilationDir) { sys::path::append(AbsolutePath, *CompilationDir); } sys::path::append(AbsolutePath, *DWOFileName); auto DWOId = getDWOId(); if (!DWOId) return false; auto DWOContext = Context.getDWOContext(AbsolutePath); if (!DWOContext) return false; DWARFCompileUnit *DWOCU = DWOContext->getDWOCompileUnitForHash(*DWOId); if (!DWOCU) return false; DWO = std::shared_ptr<DWARFCompileUnit>(std::move(DWOContext), DWOCU); // Share .debug_addr and .debug_ranges section with compile unit in .dwo DWO->setAddrOffsetSection(AddrOffsetSection, AddrOffsetSectionBase); auto DWORangesBase = UnitDie.getRangesBaseAttribute(); DWO->setRangesSection(RangeSection, DWORangesBase ? *DWORangesBase : 0); return true; }
bool DWARFUnit::parseDWO() { if (isDWO) return false; if (DWO.get()) return false; DWARFDie UnitDie = getUnitDIE(); if (!UnitDie) return false; auto DWOFileName = dwarf::toString(UnitDie.find(DW_AT_GNU_dwo_name)); if (!DWOFileName) return false; auto CompilationDir = dwarf::toString(UnitDie.find(DW_AT_comp_dir)); SmallString<16> AbsolutePath; if (sys::path::is_relative(*DWOFileName) && CompilationDir && *CompilationDir) { sys::path::append(AbsolutePath, *CompilationDir); } sys::path::append(AbsolutePath, *DWOFileName); auto DWOId = getDWOId(); if (!DWOId) return false; auto DWOContext = Context.getDWOContext(AbsolutePath); if (!DWOContext) return false; DWARFCompileUnit *DWOCU = DWOContext->getDWOCompileUnitForHash(*DWOId); if (!DWOCU) return false; DWO = std::shared_ptr<DWARFCompileUnit>(std::move(DWOContext), DWOCU); // Share .debug_addr and .debug_ranges section with compile unit in .dwo DWO->setAddrOffsetSection(AddrOffsetSection, AddrOffsetSectionBase); if (getVersion() >= 5) { DWO->setRangesSection(&Context.getDWARFObj().getRnglistsDWOSection(), 0); DWARFDataExtractor RangesDA(Context.getDWARFObj(), *RangeSection, isLittleEndian, 0); if (auto TableOrError = parseRngListTableHeader(RangesDA, RangeSectionBase)) DWO->RngListTable = TableOrError.get(); else WithColor::error() << "parsing a range list table: " << toString(TableOrError.takeError()) << '\n'; if (DWO->RngListTable) DWO->RangeSectionBase = DWO->RngListTable->getHeaderSize(); } else { auto DWORangesBase = UnitDie.getRangesBaseAttribute(); DWO->setRangesSection(RangeSection, DWORangesBase ? *DWORangesBase : 0); } return true; }
Optional<DWARFFormValue> DWARFDie::findRecursively(ArrayRef<dwarf::Attribute> Attrs) const { std::vector<DWARFDie> Worklist; Worklist.push_back(*this); // Keep track if DIEs already seen to prevent infinite recursion. // Empirically we rarely see a depth of more than 3 when dealing with valid // DWARF. This corresponds to following the DW_AT_abstract_origin and // DW_AT_specification just once. SmallSet<DWARFDie, 3> Seen; Seen.insert(*this); while (!Worklist.empty()) { DWARFDie Die = Worklist.back(); Worklist.pop_back(); if (!Die.isValid()) continue; if (auto Value = Die.find(Attrs)) return Value; if (auto D = Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) if (Seen.insert(D).second) Worklist.push_back(D); if (auto D = Die.getAttributeValueAsReferencedDie(DW_AT_specification)) if (Seen.insert(D).second) Worklist.push_back(D); } return None; }
uint16_t CompileUnit::getLanguage() { if (!Language) { DWARFDie CU = getOrigUnit().getUnitDIE(); Language = dwarf::toUnsigned(CU.find(dwarf::DW_AT_language), 0); } return Language; }
void DwarfVariableFinder::getInfo(const DWARFDie &die) { auto tagString = TagString(die.getTag()); if (tagString.empty()) { outs() << format("DW_TAG_Unknown_%x", die.getTag()); } auto formVal = die.find(dwarf::DW_AT_name); formVal->dump(outs()); }
llvm::Optional<SectionedAddress> DWARFUnit::getBaseAddress() { if (BaseAddr) return BaseAddr; DWARFDie UnitDie = getUnitDIE(); Optional<DWARFFormValue> PC = UnitDie.find({DW_AT_low_pc, DW_AT_entry_pc}); BaseAddr = toSectionedAddress(PC); return BaseAddr; }
size_t DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) { if ((CUDieOnly && !DieArray.empty()) || DieArray.size() > 1) return 0; // Already parsed. bool HasCUDie = !DieArray.empty(); extractDIEsToVector(!HasCUDie, !CUDieOnly, DieArray); if (DieArray.empty()) return 0; // If CU DIE was just parsed, copy several attribute values from it. if (!HasCUDie) { DWARFDie UnitDie = getUnitDIE(); Optional<DWARFFormValue> PC = UnitDie.find({DW_AT_low_pc, DW_AT_entry_pc}); if (Optional<uint64_t> Addr = toAddress(PC)) setBaseAddress({*Addr, PC->getSectionIndex()}); if (!isDWO) { assert(AddrOffsetSectionBase == 0); assert(RangeSectionBase == 0); AddrOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_GNU_addr_base), 0); RangeSectionBase = toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0); } // In general, we derive the offset of the unit's contibution to the // debug_str_offsets{.dwo} section from the unit DIE's // DW_AT_str_offsets_base attribute. In dwp files we add to it the offset // we get from the index table. StringOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_str_offsets_base), 0); if (IndexEntry) if (const auto *C = IndexEntry->getOffset(DW_SECT_STR_OFFSETS)) StringOffsetSectionBase += C->Offset; // Don't fall back to DW_AT_GNU_ranges_base: it should be ignored for // skeleton CU DIE, so that DWARF users not aware of it are not broken. } return DieArray.size(); }
llvm::Optional<BaseAddress> DWARFUnit::getBaseAddress() { if (BaseAddr) return BaseAddr; DWARFDie UnitDie = getUnitDIE(); Optional<DWARFFormValue> PC = UnitDie.find({DW_AT_low_pc, DW_AT_entry_pc}); if (Optional<uint64_t> Addr = toAddress(PC)) BaseAddr = {*Addr, PC->getSectionIndex()}; return BaseAddr; }
std::shared_ptr<TypeInfo> DwarfVariableFinder::makeType(const DWARFDie &die) { if (!die.isValid()) { return std::make_shared<TypeInfo>("", ~0u); } auto opSize = die.find(dwarf::DW_AT_byte_size); unsigned size = 1; if(opSize.hasValue()) { size = opSize.getValue().getAsUnsignedConstant().getValue(); } std::string type_encoding = ""; raw_string_ostream SS(type_encoding); switch (die.getTag()) { case dwarf::DW_TAG_base_type: { auto opForm = die.find(dwarf::DW_AT_encoding); auto opEnc = opForm->getAsUnsignedConstant(); assert(opEnc < HANDLE_DW_ATE_SIZE); SS << HANDLE_DW_ATE[*opEnc]; opForm = die.find(dwarf::DW_AT_name); opForm->dump(SS); return std::make_shared<TypeInfo>(SS.str(), size); } case dwarf::DW_TAG_reference_type: case dwarf::DW_TAG_rvalue_reference_type: case dwarf::DW_TAG_pointer_type: { auto baseType = getType(die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type)); SS << "*" << baseType->getName(); return std::make_shared<TypeInfo>(SS.str(), size); } case dwarf::DW_TAG_array_type: { auto baseType = getType(die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type)); SS << baseType->getName(); size *= baseType->getSize(); for (auto childDie = die.getFirstChild(); childDie && childDie.getTag(); childDie = childDie.getSibling()) { std::shared_ptr<TypeInfo> rangeInfo = makeType(childDie); SS << "["; SS << rangeInfo->getName(); SS << "]"; size *= rangeInfo->getSize(); } return std::make_shared<TypeInfo>(SS.str(), size); } case dwarf::DW_TAG_subrange_type: { uint64_t count = 0; auto opCount = die.find(dwarf::DW_AT_count); if(opCount.hasValue()) { count = opCount.getValue().getAsUnsignedConstant().getValue(); } else { opCount = die.find(dwarf::DW_AT_upper_bound); assert(opCount.hasValue()); count = opCount.getValue().getAsUnsignedConstant().getValue() +1; } return std::make_shared<TypeInfo>(std::to_string(count), count); } case dwarf::DW_TAG_typedef: { return getType(die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type)); } case dwarf::DW_TAG_structure_type: case dwarf::DW_TAG_class_type: case dwarf::DW_TAG_union_type: { SS << "struct" << dwarf::toString(die.find(dwarf::DW_AT_name), "None"); auto structType = std::make_shared<TypeInfo>(SS.str(), size); // Add subentries for various pieces of the struct. for (auto childDie = die.getFirstChild(); childDie && childDie.getTag(); childDie = childDie.getSibling()) { if (childDie.getTag() != dwarf::DW_TAG_inheritance && childDie.getTag() != dwarf::DW_TAG_member) { continue; } uint64_t dataMemOffset = dwarf::toUnsigned(childDie.find(dwarf::DW_AT_data_member_location), ~0U); structType->getFields().emplace_back(makeType(childDie), dataMemOffset); } return structType; } case dwarf::DW_TAG_inheritance: case dwarf::DW_TAG_member: { return getType(die.getAttributeValueAsReferencedDie(dwarf::DW_AT_type)); } default: { auto tagString = TagString(die.getTag()); if (tagString.empty()) { llvm::errs() << format("DW_TAG_Unknown_%x", die.getTag()); } die.dump(llvm::errs(), 10); return std::make_shared<TypeInfo>("", ~0u); } } }
size_t DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) { if ((CUDieOnly && !DieArray.empty()) || DieArray.size() > 1) return 0; // Already parsed. bool HasCUDie = !DieArray.empty(); extractDIEsToVector(!HasCUDie, !CUDieOnly, DieArray); if (DieArray.empty()) return 0; // If CU DIE was just parsed, copy several attribute values from it. if (!HasCUDie) { DWARFDie UnitDie = getUnitDIE(); if (Optional<uint64_t> DWOId = toUnsigned(UnitDie.find(DW_AT_GNU_dwo_id))) Header.setDWOId(*DWOId); if (!isDWO) { assert(AddrOffsetSectionBase == 0); assert(RangeSectionBase == 0); AddrOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_GNU_addr_base), 0); RangeSectionBase = toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0); } // In general, in DWARF v5 and beyond we derive the start of the unit's // contribution to the string offsets table from the unit DIE's // DW_AT_str_offsets_base attribute. Split DWARF units do not use this // attribute, so we assume that there is a contribution to the string // offsets table starting at offset 0 of the debug_str_offsets.dwo section. // In both cases we need to determine the format of the contribution, // which may differ from the unit's format. uint64_t StringOffsetsContributionBase = isDWO ? 0 : toSectionOffset(UnitDie.find(DW_AT_str_offsets_base), 0); auto IndexEntry = Header.getIndexEntry(); if (IndexEntry) if (const auto *C = IndexEntry->getOffset(DW_SECT_STR_OFFSETS)) StringOffsetsContributionBase += C->Offset; DWARFDataExtractor DA(Context.getDWARFObj(), StringOffsetSection, isLittleEndian, 0); if (isDWO) StringOffsetsTableContribution = determineStringOffsetsTableContributionDWO( DA, StringOffsetsContributionBase); else if (getVersion() >= 5) StringOffsetsTableContribution = determineStringOffsetsTableContribution( DA, StringOffsetsContributionBase); // DWARF v5 uses the .debug_rnglists and .debug_rnglists.dwo sections to // describe address ranges. if (getVersion() >= 5) { if (isDWO) setRangesSection(&Context.getDWARFObj().getRnglistsDWOSection(), 0); else setRangesSection(&Context.getDWARFObj().getRnglistsSection(), toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0)); if (RangeSection->Data.size()) { // Parse the range list table header. Individual range lists are // extracted lazily. DWARFDataExtractor RangesDA(Context.getDWARFObj(), *RangeSection, isLittleEndian, 0); if (auto TableOrError = parseRngListTableHeader(RangesDA, RangeSectionBase)) RngListTable = TableOrError.get(); else WithColor::error() << "parsing a range list table: " << toString(TableOrError.takeError()) << '\n'; // In a split dwarf unit, there is no DW_AT_rnglists_base attribute. // Adjust RangeSectionBase to point past the table header. if (isDWO && RngListTable) RangeSectionBase = RngListTable->getHeaderSize(); } } // Don't fall back to DW_AT_GNU_ranges_base: it should be ignored for // skeleton CU DIE, so that DWARF users not aware of it are not broken. } return DieArray.size(); }
static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, uint32_t *OffsetPtr, dwarf::Attribute Attr, dwarf::Form Form, unsigned Indent, DIDumpOptions DumpOpts) { if (!Die.isValid()) return; const char BaseIndent[] = " "; OS << BaseIndent; OS.indent(Indent + 2); WithColor(OS, HighlightColor::Attribute) << formatv("{0}", Attr); if (DumpOpts.Verbose || DumpOpts.ShowForm) OS << formatv(" [{0}]", Form); DWARFUnit *U = Die.getDwarfUnit(); DWARFFormValue formValue(Form); if (!formValue.extractValue(U->getDebugInfoExtractor(), OffsetPtr, U->getFormParams(), U)) return; OS << "\t("; StringRef Name; std::string File; auto Color = HighlightColor::Enumerator; if (Attr == DW_AT_decl_file || Attr == DW_AT_call_file) { Color = HighlightColor::String; if (const auto *LT = U->getContext().getLineTableForUnit(U)) if (LT->getFileNameByIndex( formValue.getAsUnsignedConstant().getValue(), U->getCompilationDir(), DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File)) { File = '"' + File + '"'; Name = File; } } else if (Optional<uint64_t> Val = formValue.getAsUnsignedConstant()) Name = AttributeValueString(Attr, *Val); if (!Name.empty()) WithColor(OS, Color) << Name; else if (Attr == DW_AT_decl_line || Attr == DW_AT_call_line) OS << *formValue.getAsUnsignedConstant(); else if (Attr == DW_AT_high_pc && !DumpOpts.ShowForm && !DumpOpts.Verbose && formValue.getAsUnsignedConstant()) { if (DumpOpts.ShowAddresses) { // Print the actual address rather than the offset. uint64_t LowPC, HighPC, Index; if (Die.getLowAndHighPC(LowPC, HighPC, Index)) OS << format("0x%016" PRIx64, HighPC); else formValue.dump(OS, DumpOpts); } } else if (Attr == DW_AT_location || Attr == DW_AT_frame_base || Attr == DW_AT_data_member_location || Attr == DW_AT_GNU_call_site_value) dumpLocation(OS, formValue, U, sizeof(BaseIndent) + Indent + 4, DumpOpts); else formValue.dump(OS, DumpOpts); // We have dumped the attribute raw value. For some attributes // having both the raw value and the pretty-printed value is // interesting. These attributes are handled below. if (Attr == DW_AT_specification || Attr == DW_AT_abstract_origin) { if (const char *Name = Die.getAttributeValueAsReferencedDie(Attr).getName( DINameKind::LinkageName)) OS << " \"" << Name << '\"'; } else if (Attr == DW_AT_type) { OS << " \""; dumpTypeName(OS, Die); OS << '"'; } else if (Attr == DW_AT_APPLE_property_attribute) { if (Optional<uint64_t> OptVal = formValue.getAsUnsignedConstant()) dumpApplePropertyAttribute(OS, *OptVal); } else if (Attr == DW_AT_ranges) { const DWARFObject &Obj = Die.getDwarfUnit()->getContext().getDWARFObj(); // For DW_FORM_rnglistx we need to dump the offset separately, since // we have only dumped the index so far. Optional<DWARFFormValue> Value = Die.find(DW_AT_ranges); if (Value && Value->getForm() == DW_FORM_rnglistx) if (auto RangeListOffset = U->getRnglistOffset(*Value->getAsSectionOffset())) { DWARFFormValue FV(dwarf::DW_FORM_sec_offset); FV.setUValue(*RangeListOffset); FV.dump(OS, DumpOpts); } dumpRanges(Obj, OS, Die.getAddressRanges(), U->getAddressByteSize(), sizeof(BaseIndent) + Indent + 4, DumpOpts); } OS << ")\n"; }
unsigned DWARFVerifier::verifyNameIndexCompleteness( const DWARFDie &Die, const DWARFDebugNames::NameIndex &NI) { // First check, if the Die should be indexed. The code follows the DWARF v5 // wording as closely as possible. // "All non-defining declarations (that is, debugging information entries // with a DW_AT_declaration attribute) are excluded." if (Die.find(DW_AT_declaration)) return 0; // "DW_TAG_namespace debugging information entries without a DW_AT_name // attribute are included with the name “(anonymous namespace)”. // All other debugging information entries without a DW_AT_name attribute // are excluded." // "If a subprogram or inlined subroutine is included, and has a // DW_AT_linkage_name attribute, there will be an additional index entry for // the linkage name." auto IncludeLinkageName = Die.getTag() == DW_TAG_subprogram || Die.getTag() == DW_TAG_inlined_subroutine; auto EntryNames = getNames(Die, IncludeLinkageName); if (EntryNames.empty()) return 0; // We deviate from the specification here, which says: // "The name index must contain an entry for each debugging information entry // that defines a named subprogram, label, variable, type, or namespace, // subject to ..." // Instead whitelisting all TAGs representing a "type" or a "subprogram", to // make sure we catch any missing items, we instead blacklist all TAGs that we // know shouldn't be indexed. switch (Die.getTag()) { // Compile units and modules have names but shouldn't be indexed. case DW_TAG_compile_unit: case DW_TAG_module: return 0; // Function and template parameters are not globally visible, so we shouldn't // index them. case DW_TAG_formal_parameter: case DW_TAG_template_value_parameter: case DW_TAG_template_type_parameter: case DW_TAG_GNU_template_parameter_pack: case DW_TAG_GNU_template_template_param: return 0; // Object members aren't globally visible. case DW_TAG_member: return 0; // According to a strict reading of the specification, enumerators should not // be indexed (and LLVM currently does not do that). However, this causes // problems for the debuggers, so we may need to reconsider this. case DW_TAG_enumerator: return 0; // Imported declarations should not be indexed according to the specification // and LLVM currently does not do that. case DW_TAG_imported_declaration: return 0; // "DW_TAG_subprogram, DW_TAG_inlined_subroutine, and DW_TAG_label debugging // information entries without an address attribute (DW_AT_low_pc, // DW_AT_high_pc, DW_AT_ranges, or DW_AT_entry_pc) are excluded." case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_label: if (Die.findRecursively( {DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges, DW_AT_entry_pc})) break; return 0; // "DW_TAG_variable debugging information entries with a DW_AT_location // attribute that includes a DW_OP_addr or DW_OP_form_tls_address operator are // included; otherwise, they are excluded." // // LLVM extension: We also add DW_OP_GNU_push_tls_address to this list. case DW_TAG_variable: if (isVariableIndexable(Die, DCtx)) break; return 0; default: break; } // Now we know that our Die should be present in the Index. Let's check if // that's the case. unsigned NumErrors = 0; uint64_t DieUnitOffset = Die.getOffset() - Die.getDwarfUnit()->getOffset(); for (StringRef Name : EntryNames) { if (none_of(NI.equal_range(Name), [&](const DWARFDebugNames::Entry &E) { return E.getDIEUnitOffset() == DieUnitOffset; })) { error() << formatv("Name Index @ {0:x}: Entry for DIE @ {1:x} ({2}) with " "name {3} missing.\n", NI.getUnitOffset(), Die.getOffset(), Die.getTag(), Name); ++NumErrors; } } return NumErrors; }