diff options
Diffstat (limited to 'llvm/lib/DebugInfo')
25 files changed, 1204 insertions, 534 deletions
diff --git a/llvm/lib/DebugInfo/CodeView/SymbolRecordHelpers.cpp b/llvm/lib/DebugInfo/CodeView/SymbolRecordHelpers.cpp index 51a5a9e9243e..2562c633bb99 100644 --- a/llvm/lib/DebugInfo/CodeView/SymbolRecordHelpers.cpp +++ b/llvm/lib/DebugInfo/CodeView/SymbolRecordHelpers.cpp @@ -14,7 +14,7 @@ using namespace llvm; using namespace llvm::codeview; -template <typename RecordT> RecordT createRecord(const CVSymbol &sym) { +template <typename RecordT> static RecordT createRecord(const CVSymbol &sym) { RecordT record(static_cast<SymbolRecordKind>(sym.kind())); cantFail(SymbolDeserializer::deserializeAs<RecordT>(sym, record)); return record; diff --git a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp index aba0e96d606e..f9fca74a2199 100644 --- a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp +++ b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp @@ -15,6 +15,7 @@ #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" #include "llvm/Support/Error.h" using namespace llvm; @@ -202,21 +203,6 @@ private: const TypeIndex TypeStreamMerger::Untranslated(SimpleTypeKind::NotTranslated); -static bool isIdRecord(TypeLeafKind K) { - switch (K) { - case TypeLeafKind::LF_FUNC_ID: - case TypeLeafKind::LF_MFUNC_ID: - case TypeLeafKind::LF_STRING_ID: - case TypeLeafKind::LF_SUBSTR_LIST: - case TypeLeafKind::LF_BUILDINFO: - case TypeLeafKind::LF_UDT_SRC_LINE: - case TypeLeafKind::LF_UDT_MOD_SRC_LINE: - return true; - default: - return false; - } -} - void TypeStreamMerger::addMapping(TypeIndex Idx) { if (!IsSecondPass) { assert(IndexMap.size() == slotForIndex(CurIndex) && diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp index 875f5e9989a0..575edba51ee8 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp @@ -277,7 +277,7 @@ Optional<DWARFFormValue> AppleAcceleratorTable::Entry::lookup(HeaderData::AtomType Atom) const { assert(HdrData && "Dereferencing end iterator?"); assert(HdrData->Atoms.size() == Values.size()); - for (const auto &Tuple : zip_first(HdrData->Atoms, Values)) { + for (auto Tuple : zip_first(HdrData->Atoms, Values)) { if (std::get<0>(Tuple).first == Atom) return std::get<1>(Tuple); } @@ -531,7 +531,7 @@ DWARFDebugNames::Entry::Entry(const NameIndex &NameIdx, const Abbrev &Abbr) Optional<DWARFFormValue> DWARFDebugNames::Entry::lookup(dwarf::Index Index) const { assert(Abbr->Attributes.size() == Values.size()); - for (const auto &Tuple : zip_first(Abbr->Attributes, Values)) { + for (auto Tuple : zip_first(Abbr->Attributes, Values)) { if (std::get<0>(Tuple).Index == Index) return std::get<1>(Tuple); } @@ -565,7 +565,7 @@ void DWARFDebugNames::Entry::dump(ScopedPrinter &W) const { W.printHex("Abbrev", Abbr->Code); W.startLine() << formatv("Tag: {0}\n", Abbr->Tag); assert(Abbr->Attributes.size() == Values.size()); - for (const auto &Tuple : zip_first(Abbr->Attributes, Values)) { + for (auto Tuple : zip_first(Abbr->Attributes, Values)) { W.startLine() << formatv("{0}: ", std::get<0>(Tuple).Index); std::get<1>(Tuple).dump(W.getOStream()); W.getOStream() << '\n'; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAddressRange.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAddressRange.cpp index ef6da08d34aa..ddf307de2221 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFAddressRange.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFAddressRange.cpp @@ -7,19 +7,23 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFAddressRange.h" - +#include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; void DWARFAddressRange::dump(raw_ostream &OS, uint32_t AddressSize, - DIDumpOptions DumpOpts) const { + DIDumpOptions DumpOpts, + const DWARFObject *Obj) const { OS << (DumpOpts.DisplayRawContents ? " " : "["); OS << format("0x%*.*" PRIx64 ", ", AddressSize * 2, AddressSize * 2, LowPC) << format("0x%*.*" PRIx64, AddressSize * 2, AddressSize * 2, HighPC); OS << (DumpOpts.DisplayRawContents ? "" : ")"); + + if (Obj) + DWARFFormValue::dumpAddressSection(*Obj, OS, DumpOpts, SectionIndex); } raw_ostream &llvm::operator<<(raw_ostream &OS, const DWARFAddressRange &R) { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp index c06d85d50609..aaa6d5250f23 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -288,6 +288,7 @@ static void dumpRnglistsSection( static void dumpLoclistsSection(raw_ostream &OS, DIDumpOptions DumpOpts, DWARFDataExtractor Data, const MCRegisterInfo *MRI, + const DWARFObject &Obj, Optional<uint64_t> DumpOffset) { uint64_t Offset = 0; @@ -299,13 +300,21 @@ static void dumpLoclistsSection(raw_ostream &OS, DIDumpOptions DumpOpts, } Header.dump(OS, DumpOpts); - DataExtractor LocData(Data.getData(), - Data.isLittleEndian(), Header.getAddrSize()); - DWARFDebugLoclists Loclists; uint64_t EndOffset = Header.length() + Header.getHeaderOffset(); - Loclists.parse(LocData, Offset, EndOffset, Header.getVersion()); - Loclists.dump(OS, 0, MRI, DumpOpts, DumpOffset); + Data.setAddressSize(Header.getAddrSize()); + DWARFDebugLoclists Loc(Data, Header.getVersion()); + if (DumpOffset) { + if (DumpOffset >= Offset && DumpOffset < EndOffset) { + Offset = *DumpOffset; + Loc.dumpLocationList(&Offset, OS, /*BaseAddr=*/None, MRI, Obj, nullptr, + DumpOpts, /*Indent=*/0); + OS << "\n"; + return; + } + } else { + Loc.dumpRange(Offset, EndOffset - Offset, OS, MRI, Obj, DumpOpts); + } Offset = EndOffset; } } @@ -380,21 +389,45 @@ void DWARFContext::dump( dumpDebugType(".debug_types.dwo", dwo_types_section_units()); } + DIDumpOptions LLDumpOpts = DumpOpts; + if (LLDumpOpts.Verbose) + LLDumpOpts.DisplayRawContents = true; + if (const auto *Off = shouldDump(Explicit, ".debug_loc", DIDT_ID_DebugLoc, DObj->getLocSection().Data)) { - getDebugLoc()->dump(OS, getRegisterInfo(), DumpOpts, *Off); + getDebugLoc()->dump(OS, getRegisterInfo(), *DObj, LLDumpOpts, *Off); } if (const auto *Off = shouldDump(Explicit, ".debug_loclists", DIDT_ID_DebugLoclists, DObj->getLoclistsSection().Data)) { DWARFDataExtractor Data(*DObj, DObj->getLoclistsSection(), isLittleEndian(), 0); - dumpLoclistsSection(OS, DumpOpts, Data, getRegisterInfo(), *Off); + dumpLoclistsSection(OS, LLDumpOpts, Data, getRegisterInfo(), *DObj, *Off); + } + if (const auto *Off = + shouldDump(ExplicitDWO, ".debug_loclists.dwo", DIDT_ID_DebugLoclists, + DObj->getLoclistsDWOSection().Data)) { + DWARFDataExtractor Data(*DObj, DObj->getLoclistsDWOSection(), + isLittleEndian(), 0); + dumpLoclistsSection(OS, LLDumpOpts, Data, getRegisterInfo(), *DObj, *Off); } + if (const auto *Off = shouldDump(ExplicitDWO, ".debug_loc.dwo", DIDT_ID_DebugLoc, DObj->getLocDWOSection().Data)) { - getDebugLocDWO()->dump(OS, 0, getRegisterInfo(), DumpOpts, *Off); + DWARFDataExtractor Data(*DObj, DObj->getLocDWOSection(), isLittleEndian(), + 4); + DWARFDebugLoclists Loc(Data, /*Version=*/4); + if (*Off) { + uint64_t Offset = **Off; + Loc.dumpLocationList(&Offset, OS, + /*BaseAddr=*/None, getRegisterInfo(), *DObj, nullptr, + LLDumpOpts, /*Indent=*/0); + OS << "\n"; + } else { + Loc.dumpRange(0, Data.getData().size(), OS, getRegisterInfo(), *DObj, + LLDumpOpts); + } } if (const auto *Off = shouldDump(Explicit, ".debug_frame", DIDT_ID_DebugFrame, @@ -409,6 +442,9 @@ void DWARFContext::dump( if (Explicit || !getDebugMacro()->empty()) { OS << "\n.debug_macinfo contents:\n"; getDebugMacro()->dump(OS); + } else if (ExplicitDWO || !getDebugMacroDWO()->empty()) { + OS << "\n.debug_macinfo.dwo contents:\n"; + getDebugMacroDWO()->dump(OS); } } @@ -715,32 +751,16 @@ const DWARFDebugLoc *DWARFContext::getDebugLoc() { if (Loc) return Loc.get(); - Loc.reset(new DWARFDebugLoc); // Assume all units have the same address byte size. - if (getNumCompileUnits()) { - DWARFDataExtractor LocData(*DObj, DObj->getLocSection(), isLittleEndian(), - getUnitAtIndex(0)->getAddressByteSize()); - Loc->parse(LocData); - } + auto LocData = + getNumCompileUnits() + ? DWARFDataExtractor(*DObj, DObj->getLocSection(), isLittleEndian(), + getUnitAtIndex(0)->getAddressByteSize()) + : DWARFDataExtractor("", isLittleEndian(), 0); + Loc.reset(new DWARFDebugLoc(std::move(LocData))); return Loc.get(); } -const DWARFDebugLoclists *DWARFContext::getDebugLocDWO() { - if (LocDWO) - return LocDWO.get(); - - LocDWO.reset(new DWARFDebugLoclists()); - // Assume all compile units have the same address byte size. - // FIXME: We don't need AddressSize for split DWARF since relocatable - // addresses cannot appear there. At the moment DWARFExpression requires it. - DataExtractor LocData(DObj->getLocDWOSection().Data, isLittleEndian(), 4); - // Use version 4. DWO does not support the DWARF v5 .debug_loclists yet and - // that means we are parsing the new style .debug_loc (pre-standatized version - // of the .debug_loclists). - LocDWO->parse(LocData, 0, LocData.getData().size(), 4 /* Version */); - return LocDWO.get(); -} - const DWARFDebugAranges *DWARFContext::getDebugAranges() { if (Aranges) return Aranges.get(); @@ -781,6 +801,17 @@ const DWARFDebugFrame *DWARFContext::getEHFrame() { return DebugFrame.get(); } +const DWARFDebugMacro *DWARFContext::getDebugMacroDWO() { + if (MacroDWO) + return MacroDWO.get(); + + DataExtractor MacinfoDWOData(DObj->getMacinfoDWOSection(), isLittleEndian(), + 0); + MacroDWO.reset(new DWARFDebugMacro()); + MacroDWO->parse(MacinfoDWOData); + return MacroDWO.get(); +} + const DWARFDebugMacro *DWARFContext::getDebugMacro() { if (Macro) return Macro.get(); @@ -843,7 +874,7 @@ DWARFContext::getLineTableForUnit(DWARFUnit *U) { } Expected<const DWARFDebugLine::LineTable *> DWARFContext::getLineTableForUnit( - DWARFUnit *U, std::function<void(Error)> RecoverableErrorCallback) { + DWARFUnit *U, function_ref<void(Error)> RecoverableErrorCallback) { if (!Line) Line.reset(new DWARFDebugLine); @@ -1027,19 +1058,56 @@ static Optional<uint64_t> getTypeSize(DWARFDie Type, uint64_t PointerSize) { return Optional<uint64_t>(); } +static Optional<int64_t> +getExpressionFrameOffset(ArrayRef<uint8_t> Expr, + Optional<unsigned> FrameBaseReg) { + if (!Expr.empty() && + (Expr[0] == DW_OP_fbreg || + (FrameBaseReg && Expr[0] == DW_OP_breg0 + *FrameBaseReg))) { + unsigned Count; + int64_t Offset = decodeSLEB128(Expr.data() + 1, &Count, Expr.end()); + // A single DW_OP_fbreg or DW_OP_breg. + if (Expr.size() == Count + 1) + return Offset; + // Same + DW_OP_deref (Fortran arrays look like this). + if (Expr.size() == Count + 2 && Expr[Count + 1] == DW_OP_deref) + return Offset; + // Fallthrough. Do not accept ex. (DW_OP_breg W29, DW_OP_stack_value) + } + return None; +} + void DWARFContext::addLocalsForDie(DWARFCompileUnit *CU, DWARFDie Subprogram, DWARFDie Die, std::vector<DILocal> &Result) { if (Die.getTag() == DW_TAG_variable || Die.getTag() == DW_TAG_formal_parameter) { DILocal Local; - if (auto NameAttr = Subprogram.find(DW_AT_name)) - if (Optional<const char *> Name = NameAttr->getAsCString()) - Local.FunctionName = *Name; - if (auto LocationAttr = Die.find(DW_AT_location)) - if (Optional<ArrayRef<uint8_t>> Location = LocationAttr->getAsBlock()) - if (!Location->empty() && (*Location)[0] == DW_OP_fbreg) - Local.FrameOffset = - decodeSLEB128(Location->data() + 1, nullptr, Location->end()); + if (const char *Name = Subprogram.getSubroutineName(DINameKind::ShortName)) + Local.FunctionName = Name; + + Optional<unsigned> FrameBaseReg; + if (auto FrameBase = Subprogram.find(DW_AT_frame_base)) + if (Optional<ArrayRef<uint8_t>> Expr = FrameBase->getAsBlock()) + if (!Expr->empty() && (*Expr)[0] >= DW_OP_reg0 && + (*Expr)[0] <= DW_OP_reg31) { + FrameBaseReg = (*Expr)[0] - DW_OP_reg0; + } + + if (Expected<std::vector<DWARFLocationExpression>> Loc = + Die.getLocations(DW_AT_location)) { + for (const auto &Entry : *Loc) { + if (Optional<int64_t> FrameOffset = + getExpressionFrameOffset(Entry.Expr, FrameBaseReg)) { + Local.FrameOffset = *FrameOffset; + break; + } + } + } else { + // FIXME: missing DW_AT_location is OK here, but other errors should be + // reported to the user. + consumeError(Loc.takeError()); + } + if (auto TagOffsetAttr = Die.find(DW_AT_LLVM_tag_offset)) Local.TagOffset = TagOffsetAttr->getAsUnsignedConstant(); @@ -1377,6 +1445,7 @@ class DWARFObjInMemory final : public DWARFObject { DWARFSectionMap LocSection; DWARFSectionMap LoclistsSection; + DWARFSectionMap LoclistsDWOSection; DWARFSectionMap LineSection; DWARFSectionMap RangesSection; DWARFSectionMap RnglistsSection; @@ -1403,6 +1472,7 @@ class DWARFObjInMemory final : public DWARFObject { return StringSwitch<DWARFSectionMap *>(Name) .Case("debug_loc", &LocSection) .Case("debug_loclists", &LoclistsSection) + .Case("debug_loclists.dwo", &LoclistsDWOSection) .Case("debug_line", &LineSection) .Case("debug_frame", &FrameSection) .Case("eh_frame", &EHFrameSection) @@ -1431,6 +1501,7 @@ class DWARFObjInMemory final : public DWARFObject { StringRef ArangesSection; StringRef StrSection; StringRef MacinfoSection; + StringRef MacinfoDWOSection; StringRef AbbrevDWOSection; StringRef StrDWOSection; StringRef CUIndexSection; @@ -1450,6 +1521,7 @@ class DWARFObjInMemory final : public DWARFObject { .Case("debug_aranges", &ArangesSection) .Case("debug_str", &StrSection) .Case("debug_macinfo", &MacinfoSection) + .Case("debug_macinfo.dwo", &MacinfoDWOSection) .Case("debug_abbrev.dwo", &AbbrevDWOSection) .Case("debug_str.dwo", &StrDWOSection) .Case("debug_cu_index", &CUIndexSection) @@ -1733,6 +1805,9 @@ public: const DWARFSection &getRnglistsDWOSection() const override { return RnglistsDWOSection; } + const DWARFSection &getLoclistsDWOSection() const override { + return LoclistsDWOSection; + } const DWARFSection &getAddrSection() const override { return AddrSection; } StringRef getCUIndexSection() const override { return CUIndexSection; } StringRef getGdbIndexSection() const override { return GdbIndexSection; } @@ -1773,6 +1848,7 @@ public: return RnglistsSection; } StringRef getMacinfoSection() const override { return MacinfoSection; } + StringRef getMacinfoDWOSection() const override { return MacinfoDWOSection; } const DWARFSection &getPubnamesSection() const override { return PubnamesSection; } const DWARFSection &getPubtypesSection() const override { return PubtypesSection; } const DWARFSection &getGnuPubnamesSection() const override { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp index ca6043109cdb..fa157e868851 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp @@ -113,10 +113,10 @@ void DWARFDebugAranges::construct() { Endpoints.shrink_to_fit(); } -uint32_t DWARFDebugAranges::findAddress(uint64_t Address) const { +uint64_t DWARFDebugAranges::findAddress(uint64_t Address) const { RangeCollIterator It = partition_point(Aranges, [=](Range R) { return R.HighPC() <= Address; }); if (It != Aranges.end() && It->LowPC <= Address) return It->CUOffset; - return -1U; + return -1ULL; } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp index dbee28ff5ab1..11adb1e47640 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -39,7 +39,7 @@ struct ContentDescriptor { using ContentDescriptors = SmallVector<ContentDescriptor, 4>; -} // end anonmyous namespace +} // end anonymous namespace void DWARFDebugLine::ContentTypeTracker::trackContentType( dwarf::LineNumberEntryFormat ContentType) { @@ -190,20 +190,11 @@ parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, // the end of the prologue. static llvm::Expected<ContentDescriptors> parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, - uint64_t EndPrologueOffset, DWARFDebugLine::ContentTypeTracker *ContentTypes) { ContentDescriptors Descriptors; int FormatCount = DebugLineData.getU8(OffsetPtr); bool HasPath = false; for (int I = 0; I != FormatCount; ++I) { - if (*OffsetPtr >= EndPrologueOffset) - return createStringError( - errc::invalid_argument, - "failed to parse entry content descriptions at offset " - "0x%8.8" PRIx64 - " because offset extends beyond the prologue end at offset " - "0x%8.8" PRIx64, - *OffsetPtr, EndPrologueOffset); ContentDescriptor Descriptor; Descriptor.Type = dwarf::LineNumberEntryFormat(DebugLineData.getULEB128(OffsetPtr)); @@ -224,29 +215,20 @@ parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, static Error parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, - uint64_t *OffsetPtr, uint64_t EndPrologueOffset, - const dwarf::FormParams &FormParams, + uint64_t *OffsetPtr, const dwarf::FormParams &FormParams, const DWARFContext &Ctx, const DWARFUnit *U, DWARFDebugLine::ContentTypeTracker &ContentTypes, std::vector<DWARFFormValue> &IncludeDirectories, std::vector<DWARFDebugLine::FileNameEntry> &FileNames) { // Get the directory entry description. llvm::Expected<ContentDescriptors> DirDescriptors = - parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, nullptr); + parseV5EntryFormat(DebugLineData, OffsetPtr, nullptr); if (!DirDescriptors) return DirDescriptors.takeError(); // Get the directory entries, according to the format described above. int DirEntryCount = DebugLineData.getU8(OffsetPtr); for (int I = 0; I != DirEntryCount; ++I) { - if (*OffsetPtr >= EndPrologueOffset) - return createStringError( - errc::invalid_argument, - "failed to parse directory entry at offset " - "0x%8.8" PRIx64 - " because offset extends beyond the prologue end at offset " - "0x%8.8" PRIx64, - *OffsetPtr, EndPrologueOffset); for (auto Descriptor : *DirDescriptors) { DWARFFormValue Value(Descriptor.Form); switch (Descriptor.Type) { @@ -267,22 +249,14 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, } // Get the file entry description. - llvm::Expected<ContentDescriptors> FileDescriptors = parseV5EntryFormat( - DebugLineData, OffsetPtr, EndPrologueOffset, &ContentTypes); + llvm::Expected<ContentDescriptors> FileDescriptors = + parseV5EntryFormat(DebugLineData, OffsetPtr, &ContentTypes); if (!FileDescriptors) return FileDescriptors.takeError(); // Get the file entries, according to the format described above. int FileEntryCount = DebugLineData.getU8(OffsetPtr); for (int I = 0; I != FileEntryCount; ++I) { - if (*OffsetPtr >= EndPrologueOffset) - return createStringError( - errc::invalid_argument, - "failed to parse file entry at offset " - "0x%8.8" PRIx64 - " because offset extends beyond the prologue end at offset " - "0x%8.8" PRIx64, - *OffsetPtr, EndPrologueOffset); DWARFDebugLine::FileNameEntry FileEntry; for (auto Descriptor : *FileDescriptors) { DWARFFormValue Value(Descriptor.Form); @@ -373,9 +347,9 @@ Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, } if (getVersion() >= 5) { - if (Error e = parseV5DirFileTables( - DebugLineData, OffsetPtr, EndPrologueOffset, FormParams, Ctx, U, - ContentTypes, IncludeDirectories, FileNames)) { + if (Error E = + parseV5DirFileTables(DebugLineData, OffsetPtr, FormParams, Ctx, U, + ContentTypes, IncludeDirectories, FileNames)) { return joinErrors( createStringError( errc::invalid_argument, @@ -383,7 +357,7 @@ Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, " found an invalid directory or file table description at" " 0x%8.8" PRIx64, PrologueOffset, *OffsetPtr), - std::move(e)); + std::move(E)); } } else parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, @@ -453,14 +427,18 @@ DWARFDebugLine::LineTable::LineTable() { clear(); } void DWARFDebugLine::LineTable::dump(raw_ostream &OS, DIDumpOptions DumpOptions) const { Prologue.dump(OS, DumpOptions); - OS << '\n'; if (!Rows.empty()) { + OS << '\n'; Row::dumpTableHeader(OS); for (const Row &R : Rows) { R.dump(OS); } } + + // Terminate the table with a final blank line to clearly delineate it from + // later dumps. + OS << '\n'; } void DWARFDebugLine::LineTable::clear() { @@ -510,7 +488,7 @@ DWARFDebugLine::getLineTable(uint64_t Offset) const { Expected<const DWARFDebugLine::LineTable *> DWARFDebugLine::getOrParseLineTable( DWARFDataExtractor &DebugLineData, uint64_t Offset, const DWARFContext &Ctx, - const DWARFUnit *U, std::function<void(Error)> RecoverableErrorCallback) { + const DWARFUnit *U, function_ref<void(Error)> RecoverableErrorCallback) { if (!DebugLineData.isValidOffset(Offset)) return createStringError(errc::invalid_argument, "offset 0x%8.8" PRIx64 " is not a valid debug line section offset", @@ -531,7 +509,7 @@ Expected<const DWARFDebugLine::LineTable *> DWARFDebugLine::getOrParseLineTable( Error DWARFDebugLine::LineTable::parse( DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, const DWARFContext &Ctx, const DWARFUnit *U, - std::function<void(Error)> RecoverableErrorCallback, raw_ostream *OS) { + function_ref<void(Error)> RecoverableErrorCallback, raw_ostream *OS) { const uint64_t DebugLineOffset = *OffsetPtr; clear(); @@ -548,8 +526,23 @@ Error DWARFDebugLine::LineTable::parse( if (PrologueErr) return PrologueErr; - const uint64_t EndOffset = - DebugLineOffset + Prologue.TotalLength + Prologue.sizeofTotalLength(); + uint64_t ProgramLength = Prologue.TotalLength + Prologue.sizeofTotalLength(); + if (!DebugLineData.isValidOffsetForDataOfSize(DebugLineOffset, + ProgramLength)) { + assert(DebugLineData.size() > DebugLineOffset && + "prologue parsing should handle invalid offset"); + uint64_t BytesRemaining = DebugLineData.size() - DebugLineOffset; + RecoverableErrorCallback( + createStringError(errc::invalid_argument, + "line table program with offset 0x%8.8" PRIx64 + " has length 0x%8.8" PRIx64 " but only 0x%8.8" PRIx64 + " bytes are available", + DebugLineOffset, ProgramLength, BytesRemaining)); + // Continue by capping the length at the number of remaining bytes. + ProgramLength = BytesRemaining; + } + + const uint64_t EndOffset = DebugLineOffset + ProgramLength; // See if we should tell the data extractor the address size. if (DebugLineData.getAddressSize() == 0) @@ -595,12 +588,12 @@ Error DWARFDebugLine::LineTable::parse( // address is that of the byte after the last target machine instruction // of the sequence. State.Row.EndSequence = true; - State.appendRowToMatrix(); if (OS) { *OS << "\n"; OS->indent(12); State.Row.dump(*OS); } + State.appendRowToMatrix(); State.resetRowAndSequence(); break; @@ -614,19 +607,28 @@ Error DWARFDebugLine::LineTable::parse( // // Make sure the extractor knows the address size. If not, infer it // from the size of the operand. - if (DebugLineData.getAddressSize() == 0) + { + uint8_t ExtractorAddressSize = DebugLineData.getAddressSize(); + if (ExtractorAddressSize != Len - 1 && ExtractorAddressSize != 0) + RecoverableErrorCallback(createStringError( + errc::invalid_argument, + "mismatching address size at offset 0x%8.8" PRIx64 + " expected 0x%2.2" PRIx8 " found 0x%2.2" PRIx64, + ExtOffset, ExtractorAddressSize, Len - 1)); + + // Assume that the line table is correct and temporarily override the + // address size. DebugLineData.setAddressSize(Len - 1); - else if (DebugLineData.getAddressSize() != Len - 1) { - return createStringError(errc::invalid_argument, - "mismatching address size at offset 0x%8.8" PRIx64 - " expected 0x%2.2" PRIx8 " found 0x%2.2" PRIx64, - ExtOffset, DebugLineData.getAddressSize(), - Len - 1); + State.Row.Address.Address = DebugLineData.getRelocatedAddress( + OffsetPtr, &State.Row.Address.SectionIndex); + + // Restore the address size if the extractor already had it. + if (ExtractorAddressSize != 0) + DebugLineData.setAddressSize(ExtractorAddressSize); + + if (OS) + *OS << format(" (0x%16.16" PRIx64 ")", State.Row.Address.Address); } - State.Row.Address.Address = DebugLineData.getRelocatedAddress( - OffsetPtr, &State.Row.Address.SectionIndex); - if (OS) - *OS << format(" (0x%16.16" PRIx64 ")", State.Row.Address.Address); break; case DW_LNE_define_file: @@ -813,7 +815,7 @@ Error DWARFDebugLine::LineTable::parse( // column register of the state machine. State.Row.Isa = DebugLineData.getULEB128(OffsetPtr); if (OS) - *OS << " (" << State.Row.Isa << ")"; + *OS << " (" << (uint64_t)State.Row.Isa << ")"; break; default: @@ -888,9 +890,11 @@ Error DWARFDebugLine::LineTable::parse( } if (!State.Sequence.Empty) - RecoverableErrorCallback( - createStringError(errc::illegal_byte_sequence, - "last sequence in debug line table is not terminated!")); + RecoverableErrorCallback(createStringError( + errc::illegal_byte_sequence, + "last sequence in debug line table at offset 0x%8.8" PRIx64 + " is not terminated", + DebugLineOffset)); // Sort all sequences so that address lookup will work faster. if (!Sequences.empty()) { @@ -1046,7 +1050,10 @@ bool DWARFDebugLine::Prologue::getFileNameByIndex( if (Kind == FileLineInfoKind::None || !hasFileAtIndex(FileIndex)) return false; const FileNameEntry &Entry = getFileNameEntry(FileIndex); - StringRef FileName = Entry.Name.getAsCString().getValue(); + Optional<const char *> Name = Entry.Name.getAsCString(); + if (!Name) + return false; + StringRef FileName = *Name; if (Kind != FileLineInfoKind::AbsoluteFilePath || isPathAbsoluteOnWindowsOrPosix(FileName)) { Result = FileName; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp index 4f7b01130a47..0c5f9a9c54ec 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -22,6 +22,89 @@ #include <cstdint> using namespace llvm; +using object::SectionedAddress; + +namespace { +class DWARFLocationInterpreter { + Optional<object::SectionedAddress> Base; + std::function<Optional<object::SectionedAddress>(uint32_t)> LookupAddr; + +public: + DWARFLocationInterpreter( + Optional<object::SectionedAddress> Base, + std::function<Optional<object::SectionedAddress>(uint32_t)> LookupAddr) + : Base(Base), LookupAddr(std::move(LookupAddr)) {} + + Expected<Optional<DWARFLocationExpression>> + Interpret(const DWARFLocationEntry &E); +}; +} // namespace + +static Error createResolverError(uint32_t Index, unsigned Kind) { + return createStringError(errc::invalid_argument, + "Unable to resolve indirect address %u for: %s", + Index, dwarf::LocListEncodingString(Kind).data()); +} + +Expected<Optional<DWARFLocationExpression>> +DWARFLocationInterpreter::Interpret(const DWARFLocationEntry &E) { + switch (E.Kind) { + case dwarf::DW_LLE_end_of_list: + return None; + case dwarf::DW_LLE_base_addressx: { + Base = LookupAddr(E.Value0); + if (!Base) + return createResolverError(E.Value0, E.Kind); + return None; + } + case dwarf::DW_LLE_startx_endx: { + Optional<SectionedAddress> LowPC = LookupAddr(E.Value0); + if (!LowPC) + return createResolverError(E.Value0, E.Kind); + Optional<SectionedAddress> HighPC = LookupAddr(E.Value1); + if (!HighPC) + return createResolverError(E.Value1, E.Kind); + return DWARFLocationExpression{ + DWARFAddressRange{LowPC->Address, HighPC->Address, LowPC->SectionIndex}, + E.Loc}; + } + case dwarf::DW_LLE_startx_length: { + Optional<SectionedAddress> LowPC = LookupAddr(E.Value0); + if (!LowPC) + return createResolverError(E.Value0, E.Kind); + return DWARFLocationExpression{DWARFAddressRange{LowPC->Address, + LowPC->Address + E.Value1, + LowPC->SectionIndex}, + E.Loc}; + } + case dwarf::DW_LLE_offset_pair: { + if (!Base) { + return createStringError(inconvertibleErrorCode(), + "Unable to resolve location list offset pair: " + "Base address not defined"); + } + DWARFAddressRange Range{Base->Address + E.Value0, Base->Address + E.Value1, + Base->SectionIndex}; + if (Range.SectionIndex == SectionedAddress::UndefSection) + Range.SectionIndex = E.SectionIndex; + return DWARFLocationExpression{Range, E.Loc}; + } + case dwarf::DW_LLE_default_location: + return DWARFLocationExpression{None, E.Loc}; + case dwarf::DW_LLE_base_address: + Base = SectionedAddress{E.Value0, E.SectionIndex}; + return None; + case dwarf::DW_LLE_start_end: + return DWARFLocationExpression{ + DWARFAddressRange{E.Value0, E.Value1, E.SectionIndex}, E.Loc}; + case dwarf::DW_LLE_start_length: + return DWARFLocationExpression{ + DWARFAddressRange{E.Value0, E.Value0 + E.Value1, E.SectionIndex}, + E.Loc}; + default: + llvm_unreachable("unreachable locations list kind"); + } +} // When directly dumping the .debug_loc without a compile unit, we have to guess // at the DWARF version. This only affects DW_OP_call_ref, which is a rare @@ -35,126 +118,180 @@ static void dumpExpression(raw_ostream &OS, ArrayRef<uint8_t> Data, DWARFExpression(Extractor, dwarf::DWARF_VERSION, AddressSize).print(OS, MRI, U); } -void DWARFDebugLoc::LocationList::dump(raw_ostream &OS, uint64_t BaseAddress, - bool IsLittleEndian, - unsigned AddressSize, - const MCRegisterInfo *MRI, DWARFUnit *U, - DIDumpOptions DumpOpts, - unsigned Indent) const { - for (const Entry &E : Entries) { - OS << '\n'; +bool DWARFLocationTable::dumpLocationList(uint64_t *Offset, raw_ostream &OS, + Optional<SectionedAddress> BaseAddr, + const MCRegisterInfo *MRI, + const DWARFObject &Obj, DWARFUnit *U, + DIDumpOptions DumpOpts, + unsigned Indent) const { + DWARFLocationInterpreter Interp( + BaseAddr, [U](uint32_t Index) -> Optional<SectionedAddress> { + if (U) + return U->getAddrOffsetSectionItem(Index); + return None; + }); + OS << format("0x%8.8" PRIx64 ": ", *Offset); + Error E = visitLocationList(Offset, [&](const DWARFLocationEntry &E) { + Expected<Optional<DWARFLocationExpression>> Loc = Interp.Interpret(E); + if (!Loc || DumpOpts.DisplayRawContents) + dumpRawEntry(E, OS, Indent, DumpOpts, Obj); + if (Loc && *Loc) { + OS << "\n"; + OS.indent(Indent); + if (DumpOpts.DisplayRawContents) + OS << " => "; + + DIDumpOptions RangeDumpOpts(DumpOpts); + RangeDumpOpts.DisplayRawContents = false; + if (Loc.get()->Range) + Loc.get()->Range->dump(OS, Data.getAddressSize(), RangeDumpOpts, &Obj); + else + OS << "<default>"; + } + if (!Loc) + consumeError(Loc.takeError()); + + if (E.Kind != dwarf::DW_LLE_base_address && + E.Kind != dwarf::DW_LLE_base_addressx && + E.Kind != dwarf::DW_LLE_end_of_list) { + OS << ": "; + dumpExpression(OS, E.Loc, Data.isLittleEndian(), Data.getAddressSize(), + MRI, U); + } + return true; + }); + if (E) { + OS << "\n"; OS.indent(Indent); - OS << format("[0x%*.*" PRIx64 ", ", AddressSize * 2, AddressSize * 2, - BaseAddress + E.Begin); - OS << format(" 0x%*.*" PRIx64 ")", AddressSize * 2, AddressSize * 2, - BaseAddress + E.End); - OS << ": "; - - dumpExpression(OS, E.Loc, IsLittleEndian, AddressSize, MRI, U); + OS << "error: " << toString(std::move(E)); + return false; } + return true; } -DWARFDebugLoc::LocationList const * -DWARFDebugLoc::getLocationListAtOffset(uint64_t Offset) const { - auto It = partition_point( - Locations, [=](const LocationList &L) { return L.Offset < Offset; }); - if (It != Locations.end() && It->Offset == Offset) - return &(*It); - return nullptr; +Error DWARFLocationTable::visitAbsoluteLocationList( + uint64_t Offset, Optional<SectionedAddress> BaseAddr, + std::function<Optional<SectionedAddress>(uint32_t)> LookupAddr, + function_ref<bool(Expected<DWARFLocationExpression>)> Callback) const { + DWARFLocationInterpreter Interp(BaseAddr, std::move(LookupAddr)); + return visitLocationList(&Offset, [&](const DWARFLocationEntry &E) { + Expected<Optional<DWARFLocationExpression>> Loc = Interp.Interpret(E); + if (!Loc) + return Callback(Loc.takeError()); + if (*Loc) + return Callback(**Loc); + return true; + }); } -void DWARFDebugLoc::dump(raw_ostream &OS, const MCRegisterInfo *MRI, DIDumpOptions DumpOpts, - Optional<uint64_t> Offset) const { - auto DumpLocationList = [&](const LocationList &L) { - OS << format("0x%8.8" PRIx64 ": ", L.Offset); - L.dump(OS, 0, IsLittleEndian, AddressSize, MRI, nullptr, DumpOpts, 12); - OS << "\n"; - }; - - if (Offset) { - if (auto *L = getLocationListAtOffset(*Offset)) - DumpLocationList(*L); - return; - } - - for (const LocationList &L : Locations) { - DumpLocationList(L); - if (&L != &Locations.back()) +void DWARFDebugLoc::dump(raw_ostream &OS, const MCRegisterInfo *MRI, + const DWARFObject &Obj, DIDumpOptions DumpOpts, + Optional<uint64_t> DumpOffset) const { + auto BaseAddr = None; + unsigned Indent = 12; + if (DumpOffset) { + dumpLocationList(&*DumpOffset, OS, BaseAddr, MRI, Obj, nullptr, DumpOpts, + Indent); + } else { + uint64_t Offset = 0; + StringRef Separator; + bool CanContinue = true; + while (CanContinue && Data.isValidOffset(Offset)) { + OS << Separator; + Separator = "\n"; + + CanContinue = dumpLocationList(&Offset, OS, BaseAddr, MRI, Obj, nullptr, + DumpOpts, Indent); OS << '\n'; + } } } -Expected<DWARFDebugLoc::LocationList> -DWARFDebugLoc::parseOneLocationList(const DWARFDataExtractor &Data, - uint64_t *Offset) { - LocationList LL; - LL.Offset = *Offset; - AddressSize = Data.getAddressSize(); +Error DWARFDebugLoc::visitLocationList( + uint64_t *Offset, + function_ref<bool(const DWARFLocationEntry &)> Callback) const { DataExtractor::Cursor C(*Offset); - - // 2.6.2 Location Lists - // A location list entry consists of: while (true) { - Entry E; - - // 1. A beginning address offset. ... - E.Begin = Data.getRelocatedAddress(C); + uint64_t SectionIndex; + uint64_t Value0 = Data.getRelocatedAddress(C); + uint64_t Value1 = Data.getRelocatedAddress(C, &SectionIndex); - // 2. An ending address offset. ... - E.End = Data.getRelocatedAddress(C); - - if (Error Err = C.takeError()) - return std::move(Err); + DWARFLocationEntry E; // The end of any given location list is marked by an end of list entry, // which consists of a 0 for the beginning address offset and a 0 for the - // ending address offset. - if (E.Begin == 0 && E.End == 0) { - *Offset = C.tell(); - return LL; - } - - if (E.Begin != (AddressSize == 4 ? -1U : -1ULL)) { + // ending address offset. A beginning offset of 0xff...f marks the base + // address selection entry. + if (Value0 == 0 && Value1 == 0) { + E.Kind = dwarf::DW_LLE_end_of_list; + } else if (Value0 == (Data.getAddressSize() == 4 ? -1U : -1ULL)) { + E.Kind = dwarf::DW_LLE_base_address; + E.Value0 = Value1; + E.SectionIndex = SectionIndex; + } else { + E.Kind = dwarf::DW_LLE_offset_pair; + E.Value0 = Value0; + E.Value1 = Value1; + E.SectionIndex = SectionIndex; unsigned Bytes = Data.getU16(C); // A single location description describing the location of the object... Data.getU8(C, E.Loc, Bytes); } - LL.Entries.push_back(std::move(E)); + if (!C) + return C.takeError(); + if (!Callback(E) || E.Kind == dwarf::DW_LLE_end_of_list) + break; } + *Offset = C.tell(); + return Error::success(); } -void DWARFDebugLoc::parse(const DWARFDataExtractor &data) { - IsLittleEndian = data.isLittleEndian(); - AddressSize = data.getAddressSize(); - - uint64_t Offset = 0; - while (Offset < data.getData().size()) { - if (auto LL = parseOneLocationList(data, &Offset)) - Locations.push_back(std::move(*LL)); - else { - logAllUnhandledErrors(LL.takeError(), WithColor::error()); - break; - } +void DWARFDebugLoc::dumpRawEntry(const DWARFLocationEntry &Entry, + raw_ostream &OS, unsigned Indent, + DIDumpOptions DumpOpts, + const DWARFObject &Obj) const { + uint64_t Value0, Value1; + switch (Entry.Kind) { + case dwarf::DW_LLE_base_address: + Value0 = Data.getAddressSize() == 4 ? -1U : -1ULL; + Value1 = Entry.Value0; + break; + case dwarf::DW_LLE_offset_pair: + Value0 = Entry.Value0; + Value1 = Entry.Value1; + break; + case dwarf::DW_LLE_end_of_list: + Value0 = Value1 = 0; + return; + default: + llvm_unreachable("Not possible in DWARF4!"); } + OS << '\n'; + OS.indent(Indent); + OS << '(' << format_hex(Value0, 2 + Data.getAddressSize() * 2) << ", " + << format_hex(Value1, 2 + Data.getAddressSize() * 2) << ')'; + DWARFFormValue::dumpAddressSection(Obj, OS, DumpOpts, Entry.SectionIndex); } -Expected<DWARFDebugLoclists::LocationList> -DWARFDebugLoclists::parseOneLocationList(const DataExtractor &Data, - uint64_t *Offset, unsigned Version) { - LocationList LL; - LL.Offset = *Offset; - DataExtractor::Cursor C(*Offset); +Error DWARFDebugLoclists::visitLocationList( + uint64_t *Offset, function_ref<bool(const DWARFLocationEntry &)> F) const { - // dwarf::DW_LLE_end_of_list_entry is 0 and indicates the end of the list. - while (auto Kind = Data.getU8(C)) { - Entry E; - E.Kind = Kind; - E.Offset = C.tell() - 1; - switch (Kind) { + DataExtractor::Cursor C(*Offset); + bool Continue = true; + while (Continue) { + DWARFLocationEntry E; + E.Kind = Data.getU8(C); + switch (E.Kind) { + case dwarf::DW_LLE_end_of_list: + break; case dwarf::DW_LLE_base_addressx: E.Value0 = Data.getULEB128(C); break; + case dwarf::DW_LLE_startx_endx: + E.Value0 = Data.getULEB128(C); + E.Value1 = Data.getULEB128(C); + break; case dwarf::DW_LLE_startx_length: E.Value0 = Data.getULEB128(C); // Pre-DWARF 5 has different interpretation of the length field. We have @@ -164,176 +301,109 @@ DWARFDebugLoclists::parseOneLocationList(const DataExtractor &Data, else E.Value1 = Data.getULEB128(C); break; - case dwarf::DW_LLE_start_length: - E.Value0 = Data.getAddress(C); - E.Value1 = Data.getULEB128(C); - break; case dwarf::DW_LLE_offset_pair: E.Value0 = Data.getULEB128(C); E.Value1 = Data.getULEB128(C); + E.SectionIndex = SectionedAddress::UndefSection; + break; + case dwarf::DW_LLE_default_location: break; case dwarf::DW_LLE_base_address: - E.Value0 = Data.getAddress(C); + E.Value0 = Data.getRelocatedAddress(C, &E.SectionIndex); + break; + case dwarf::DW_LLE_start_end: + E.Value0 = Data.getRelocatedAddress(C, &E.SectionIndex); + E.Value1 = Data.getRelocatedAddress(C); + break; + case dwarf::DW_LLE_start_length: + E.Value0 = Data.getRelocatedAddress(C, &E.SectionIndex); + E.Value1 = Data.getULEB128(C); break; default: cantFail(C.takeError()); return createStringError(errc::illegal_byte_sequence, - "LLE of kind %x not supported", (int)Kind); + "LLE of kind %x not supported", (int)E.Kind); } - if (Kind != dwarf::DW_LLE_base_address && - Kind != dwarf::DW_LLE_base_addressx) { + if (E.Kind != dwarf::DW_LLE_base_address && + E.Kind != dwarf::DW_LLE_base_addressx && + E.Kind != dwarf::DW_LLE_end_of_list) { unsigned Bytes = Version >= 5 ? Data.getULEB128(C) : Data.getU16(C); // A single location description describing the location of the object... Data.getU8(C, E.Loc, Bytes); } - LL.Entries.push_back(std::move(E)); + if (!C) + return C.takeError(); + Continue = F(E) && E.Kind != dwarf::DW_LLE_end_of_list; } - if (Error Err = C.takeError()) - return std::move(Err); - Entry E; - E.Kind = dwarf::DW_LLE_end_of_list; - E.Offset = C.tell() - 1; - LL.Entries.push_back(E); *Offset = C.tell(); - return LL; -} - -void DWARFDebugLoclists::parse(DataExtractor data, uint64_t Offset, uint64_t EndOffset, uint16_t Version) { - IsLittleEndian = data.isLittleEndian(); - AddressSize = data.getAddressSize(); - - while (Offset < EndOffset) { - if (auto LL = parseOneLocationList(data, &Offset, Version)) - Locations.push_back(std::move(*LL)); - else { - logAllUnhandledErrors(LL.takeError(), WithColor::error()); - return; - } - } -} - -DWARFDebugLoclists::LocationList const * -DWARFDebugLoclists::getLocationListAtOffset(uint64_t Offset) const { - auto It = partition_point( - Locations, [=](const LocationList &L) { return L.Offset < Offset; }); - if (It != Locations.end() && It->Offset == Offset) - return &(*It); - return nullptr; + return Error::success(); } -void DWARFDebugLoclists::Entry::dump(raw_ostream &OS, uint64_t &BaseAddr, - bool IsLittleEndian, unsigned AddressSize, - const MCRegisterInfo *MRI, DWARFUnit *U, - DIDumpOptions DumpOpts, unsigned Indent, - size_t MaxEncodingStringLength) const { - if (DumpOpts.Verbose) { - OS << "\n"; - OS.indent(Indent); - auto EncodingString = dwarf::LocListEncodingString(Kind); - // Unsupported encodings should have been reported during parsing. - assert(!EncodingString.empty() && "Unknown loclist entry encoding"); - OS << format("%s%*c", EncodingString.data(), - MaxEncodingStringLength - EncodingString.size() + 1, '('); - switch (Kind) { - case dwarf::DW_LLE_startx_length: - case dwarf::DW_LLE_start_length: - case dwarf::DW_LLE_offset_pair: - OS << format("0x%*.*" PRIx64 ", 0x%*.*" PRIx64, AddressSize * 2, - AddressSize * 2, Value0, AddressSize * 2, AddressSize * 2, - Value1); - break; - case dwarf::DW_LLE_base_addressx: - case dwarf::DW_LLE_base_address: - OS << format("0x%*.*" PRIx64, AddressSize * 2, AddressSize * 2, - Value0); - break; - case dwarf::DW_LLE_end_of_list: - break; - } - OS << ')'; - } - auto PrintPrefix = [&] { - OS << "\n"; - OS.indent(Indent); - if (DumpOpts.Verbose) - OS << format("%*s", MaxEncodingStringLength, (const char *)"=> "); - }; - switch (Kind) { - case dwarf::DW_LLE_startx_length: - PrintPrefix(); - OS << "Addr idx " << Value0 << " (w/ length " << Value1 << "): "; - break; - case dwarf::DW_LLE_start_length: - PrintPrefix(); - DWARFAddressRange(Value0, Value0 + Value1) - .dump(OS, AddressSize, DumpOpts); - OS << ": "; +void DWARFDebugLoclists::dumpRawEntry(const DWARFLocationEntry &Entry, + raw_ostream &OS, unsigned Indent, + DIDumpOptions DumpOpts, + const DWARFObject &Obj) const { + size_t MaxEncodingStringLength = 0; +#define HANDLE_DW_LLE(ID, NAME) \ + MaxEncodingStringLength = std::max(MaxEncodingStringLength, \ + dwarf::LocListEncodingString(ID).size()); +#include "llvm/BinaryFormat/Dwarf.def" + + OS << "\n"; + OS.indent(Indent); + StringRef EncodingString = dwarf::LocListEncodingString(Entry.Kind); + // Unsupported encodings should have been reported during parsing. + assert(!EncodingString.empty() && "Unknown loclist entry encoding"); + OS << format("%-*s(", MaxEncodingStringLength, EncodingString.data()); + unsigned FieldSize = 2 + 2 * Data.getAddressSize(); + switch (Entry.Kind) { + case dwarf::DW_LLE_end_of_list: + case dwarf::DW_LLE_default_location: break; + case dwarf::DW_LLE_startx_endx: + case dwarf::DW_LLE_startx_length: case dwarf::DW_LLE_offset_pair: - PrintPrefix(); - DWARFAddressRange(BaseAddr + Value0, BaseAddr + Value1) - .dump(OS, AddressSize, DumpOpts); - OS << ": "; + case dwarf::DW_LLE_start_end: + case dwarf::DW_LLE_start_length: + OS << format_hex(Entry.Value0, FieldSize) << ", " + << format_hex(Entry.Value1, FieldSize); break; case dwarf::DW_LLE_base_addressx: - if (!DumpOpts.Verbose) - return; - break; - case dwarf::DW_LLE_end_of_list: - if (!DumpOpts.Verbose) - return; + case dwarf::DW_LLE_base_address: + OS << format_hex(Entry.Value0, FieldSize); break; + } + OS << ')'; + switch (Entry.Kind) { case dwarf::DW_LLE_base_address: - BaseAddr = Value0; - if (!DumpOpts.Verbose) - return; + case dwarf::DW_LLE_start_end: + case dwarf::DW_LLE_start_length: + DWARFFormValue::dumpAddressSection(Obj, OS, DumpOpts, Entry.SectionIndex); break; default: - llvm_unreachable("unreachable locations list kind"); + break; } - - dumpExpression(OS, Loc, IsLittleEndian, AddressSize, MRI, U); -} -void DWARFDebugLoclists::LocationList::dump(raw_ostream &OS, uint64_t BaseAddr, - bool IsLittleEndian, - unsigned AddressSize, - const MCRegisterInfo *MRI, - DWARFUnit *U, - DIDumpOptions DumpOpts, - unsigned Indent) const { - size_t MaxEncodingStringLength = 0; - if (DumpOpts.Verbose) - for (const auto &Entry : Entries) - MaxEncodingStringLength = - std::max(MaxEncodingStringLength, - dwarf::LocListEncodingString(Entry.Kind).size()); - - for (const Entry &E : Entries) - E.dump(OS, BaseAddr, IsLittleEndian, AddressSize, MRI, U, DumpOpts, Indent, - MaxEncodingStringLength); } -void DWARFDebugLoclists::dump(raw_ostream &OS, uint64_t BaseAddr, - const MCRegisterInfo *MRI, DIDumpOptions DumpOpts, - Optional<uint64_t> Offset) const { - auto DumpLocationList = [&](const LocationList &L) { - OS << format("0x%8.8" PRIx64 ": ", L.Offset); - L.dump(OS, BaseAddr, IsLittleEndian, AddressSize, MRI, nullptr, DumpOpts, - /*Indent=*/12); - OS << "\n"; - }; - - if (Offset) { - if (auto *L = getLocationListAtOffset(*Offset)) - DumpLocationList(*L); +void DWARFDebugLoclists::dumpRange(uint64_t StartOffset, uint64_t Size, + raw_ostream &OS, const MCRegisterInfo *MRI, + const DWARFObject &Obj, + DIDumpOptions DumpOpts) { + if (!Data.isValidOffsetForDataOfSize(StartOffset, Size)) { + OS << "Invalid dump range\n"; return; } - - for (const LocationList &L : Locations) { - DumpLocationList(L); - if (&L != &Locations.back()) - OS << '\n'; + uint64_t Offset = StartOffset; + StringRef Separator; + bool CanContinue = true; + while (CanContinue && Offset < StartOffset + Size) { + OS << Separator; + Separator = "\n"; + + CanContinue = dumpLocationList(&Offset, OS, /*BaseAddr=*/None, MRI, Obj, + nullptr, DumpOpts, /*Indent=*/12); + OS << '\n'; } } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp index 9a0e770aed3d..8cb259ebc622 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp @@ -17,36 +17,39 @@ using namespace dwarf; void DWARFDebugMacro::dump(raw_ostream &OS) const { unsigned IndLevel = 0; - for (const Entry &E : Macros) { - // There should not be DW_MACINFO_end_file when IndLevel is Zero. However, - // this check handles the case of corrupted ".debug_macinfo" section. - if (IndLevel > 0) - IndLevel -= (E.Type == DW_MACINFO_end_file); - // Print indentation. - for (unsigned I = 0; I < IndLevel; I++) - OS << " "; - IndLevel += (E.Type == DW_MACINFO_start_file); + for (const auto &Macros : MacroLists) { + for (const Entry &E : Macros) { + // There should not be DW_MACINFO_end_file when IndLevel is Zero. However, + // this check handles the case of corrupted ".debug_macinfo" section. + if (IndLevel > 0) + IndLevel -= (E.Type == DW_MACINFO_end_file); + // Print indentation. + for (unsigned I = 0; I < IndLevel; I++) + OS << " "; + IndLevel += (E.Type == DW_MACINFO_start_file); - WithColor(OS, HighlightColor::Macro).get() << MacinfoString(E.Type); - switch (E.Type) { - default: - // Got a corrupted ".debug_macinfo" section (invalid macinfo type). - break; - case DW_MACINFO_define: - case DW_MACINFO_undef: - OS << " - lineno: " << E.Line; - OS << " macro: " << E.MacroStr; - break; - case DW_MACINFO_start_file: - OS << " - lineno: " << E.Line; - OS << " filenum: " << E.File; - break; - case DW_MACINFO_end_file: - break; - case DW_MACINFO_vendor_ext: - OS << " - constant: " << E.ExtConstant; - OS << " string: " << E.ExtStr; - break; + WithColor(OS, HighlightColor::Macro).get() << MacinfoString(E.Type); + switch (E.Type) { + default: + // Got a corrupted ".debug_macinfo" section (invalid macinfo type). + break; + case DW_MACINFO_define: + case DW_MACINFO_undef: + OS << " - lineno: " << E.Line; + OS << " macro: " << E.MacroStr; + break; + case DW_MACINFO_start_file: + OS << " - lineno: " << E.Line; + OS << " filenum: " << E.File; + break; + case DW_MACINFO_end_file: + break; + case DW_MACINFO_vendor_ext: + OS << " - constant: " << E.ExtConstant; + OS << " string: " << E.ExtStr; + break; + } + OS << "\n"; } OS << "\n"; } @@ -54,15 +57,21 @@ void DWARFDebugMacro::dump(raw_ostream &OS) const { void DWARFDebugMacro::parse(DataExtractor data) { uint64_t Offset = 0; + MacroList *M = nullptr; while (data.isValidOffset(Offset)) { + if (!M) { + MacroLists.emplace_back(); + M = &MacroLists.back(); + } // A macro list entry consists of: - Entry E; + M->emplace_back(); + Entry &E = M->back(); // 1. Macinfo type E.Type = data.getULEB128(&Offset); if (E.Type == 0) { - // Reached end of ".debug_macinfo" section. - return; + // Reached end of a ".debug_macinfo" section contribution. + continue; } switch (E.Type) { @@ -70,7 +79,6 @@ void DWARFDebugMacro::parse(DataExtractor data) { // Got a corrupted ".debug_macinfo" section (invalid macinfo type). // Push the corrupted entry to the list and halt parsing. E.Type = DW_MACINFO_invalid; - Macros.push_back(E); return; case DW_MACINFO_define: case DW_MACINFO_undef: @@ -94,7 +102,5 @@ void DWARFDebugMacro::parse(DataExtractor data) { E.ExtStr = data.getCStr(&Offset); break; } - - Macros.push_back(E); } } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp index f6785b89e86d..9ae4c5b73ebe 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugRnglists.cpp @@ -114,12 +114,21 @@ Error RangeListEntry::extract(DWARFDataExtractor Data, uint64_t End, DWARFAddressRangesVector DWARFDebugRnglist::getAbsoluteRanges( llvm::Optional<object::SectionedAddress> BaseAddr, DWARFUnit &U) const { + return getAbsoluteRanges(BaseAddr, [&](uint32_t Index) { + return U.getAddrOffsetSectionItem(Index); + }); +} + +DWARFAddressRangesVector DWARFDebugRnglist::getAbsoluteRanges( + Optional<object::SectionedAddress> BaseAddr, + function_ref<Optional<object::SectionedAddress>(uint32_t)> + LookupPooledAddress) const { DWARFAddressRangesVector Res; for (const RangeListEntry &RLE : Entries) { if (RLE.EntryKind == dwarf::DW_RLE_end_of_list) break; if (RLE.EntryKind == dwarf::DW_RLE_base_addressx) { - BaseAddr = U.getAddrOffsetSectionItem(RLE.Value0); + BaseAddr = LookupPooledAddress(RLE.Value0); if (!BaseAddr) BaseAddr = {RLE.Value0, -1ULL}; continue; @@ -152,7 +161,7 @@ DWARFAddressRangesVector DWARFDebugRnglist::getAbsoluteRanges( E.HighPC = E.LowPC + RLE.Value1; break; case dwarf::DW_RLE_startx_length: { - auto Start = U.getAddrOffsetSectionItem(RLE.Value0); + auto Start = LookupPooledAddress(RLE.Value0); if (!Start) Start = {0, -1ULL}; E.SectionIndex = Start->SectionIndex; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp index cec194e8b6b3..c1dc3b68c6ab 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -62,16 +62,10 @@ static void dumpRanges(const DWARFObject &Obj, raw_ostream &OS, if (!DumpOpts.ShowAddresses) return; - ArrayRef<SectionName> SectionNames; - if (DumpOpts.Verbose) - SectionNames = Obj.getSectionNames(); - for (const DWARFAddressRange &R : Ranges) { OS << '\n'; OS.indent(Indent); - R.dump(OS, AddressSize); - - DWARFFormValue::dumpAddressSection(Obj, OS, DumpOpts, R.SectionIndex); + R.dump(OS, AddressSize, DumpOpts, &Obj); } } @@ -79,7 +73,6 @@ static void dumpLocation(raw_ostream &OS, DWARFFormValue &FormValue, DWARFUnit *U, unsigned Indent, DIDumpOptions DumpOpts) { DWARFContext &Ctx = U->getContext(); - const DWARFObject &Obj = Ctx.getDWARFObj(); const MCRegisterInfo *MRI = Ctx.getRegisterInfo(); if (FormValue.isFormClass(DWARFFormValue::FC_Block) || FormValue.isFormClass(DWARFFormValue::FC_Exprloc)) { @@ -91,49 +84,24 @@ static void dumpLocation(raw_ostream &OS, DWARFFormValue &FormValue, return; } - FormValue.dump(OS, DumpOpts); - const auto &DumpLL = [&](auto ExpectedLL) { - if (ExpectedLL) { - uint64_t BaseAddr = 0; - if (Optional<object::SectionedAddress> BA = U->getBaseAddress()) - BaseAddr = BA->Address; - auto LLDumpOpts = DumpOpts; - LLDumpOpts.Verbose = false; - ExpectedLL->dump(OS, BaseAddr, Ctx.isLittleEndian(), Obj.getAddressSize(), - MRI, U, LLDumpOpts, Indent); - } else { - OS << '\n'; - OS.indent(Indent); - OS << formatv("error extracting location list: {0}", - fmt_consume(ExpectedLL.takeError())); - } - }; if (FormValue.isFormClass(DWARFFormValue::FC_SectionOffset)) { uint64_t Offset = *FormValue.getAsSectionOffset(); - if (!U->isDWOUnit() && !U->getLocSection()->Data.empty()) { - DWARFDebugLoc DebugLoc; - DWARFDataExtractor Data(Obj, *U->getLocSection(), Ctx.isLittleEndian(), - Obj.getAddressSize()); - DumpLL(DebugLoc.parseOneLocationList(Data, &Offset)); - return; - } - bool UseLocLists = !U->isDWOUnit(); - StringRef LoclistsSectionData = - UseLocLists ? Obj.getLoclistsSection().Data : U->getLocSectionData(); + if (FormValue.getForm() == DW_FORM_loclistx) { + FormValue.dump(OS, DumpOpts); - if (!LoclistsSectionData.empty()) { - DataExtractor Data(LoclistsSectionData, Ctx.isLittleEndian(), - Obj.getAddressSize()); - - // Old-style location list were used in DWARF v4 (.debug_loc.dwo section). - // Modern locations list (.debug_loclists) are used starting from v5. - // Ideally we should take the version from the .debug_loclists section - // header, but using CU's version for simplicity. - DumpLL(DWARFDebugLoclists::parseOneLocationList( - Data, &Offset, UseLocLists ? U->getVersion() : 4)); + if (auto LoclistOffset = U->getLoclistOffset(Offset)) + Offset = *LoclistOffset; + else + return; } + U->getLocationTable().dumpLocationList(&Offset, OS, U->getBaseAddress(), + MRI, Ctx.getDWARFObj(), U, DumpOpts, + Indent); + return; } + + FormValue.dump(OS, DumpOpts); } /// Dump the name encoded in the type tag. @@ -311,7 +279,8 @@ static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, else FormValue.dump(OS, DumpOpts); } - } else if (DWARFAttribute::mayHaveLocationDescription(Attr)) + } else if (Form == dwarf::Form::DW_FORM_exprloc || + DWARFAttribute::mayHaveLocationDescription(Attr)) dumpLocation(OS, FormValue, U, sizeof(BaseIndent) + Indent + 4, DumpOpts); else FormValue.dump(OS, DumpOpts); @@ -441,6 +410,10 @@ Optional<uint64_t> DWARFDie::getRangesBaseAttribute() const { return toSectionOffset(find({DW_AT_rnglists_base, DW_AT_GNU_ranges_base})); } +Optional<uint64_t> DWARFDie::getLocBaseAttribute() const { + return toSectionOffset(find(DW_AT_loclists_base)); +} + Optional<uint64_t> DWARFDie::getHighPC(uint64_t LowPC) const { if (auto FormValue = find(DW_AT_high_pc)) { if (auto Address = FormValue->getAsAddress()) { @@ -516,6 +489,37 @@ bool DWARFDie::addressRangeContainsAddress(const uint64_t Address) const { return false; } +Expected<DWARFLocationExpressionsVector> +DWARFDie::getLocations(dwarf::Attribute Attr) const { + Optional<DWARFFormValue> Location = find(Attr); + if (!Location) + return createStringError(inconvertibleErrorCode(), "No %s", + dwarf::AttributeString(Attr).data()); + + if (Optional<uint64_t> Off = Location->getAsSectionOffset()) { + uint64_t Offset = *Off; + + if (Location->getForm() == DW_FORM_loclistx) { + if (auto LoclistOffset = U->getLoclistOffset(Offset)) + Offset = *LoclistOffset; + else + return createStringError(inconvertibleErrorCode(), + "Loclist table not found"); + } + return U->findLoclistFromOffset(Offset); + } + + if (Optional<ArrayRef<uint8_t>> Expr = Location->getAsBlock()) { + return DWARFLocationExpressionsVector{ + DWARFLocationExpression{None, to_vector<4>(*Expr)}}; + } + + return createStringError( + inconvertibleErrorCode(), "Unsupported %s encoding: %s", + dwarf::AttributeString(Attr).data(), + dwarf::FormEncodingString(Location->getForm()).data()); +} + const char *DWARFDie::getSubroutineName(DINameKind Kind) const { if (!isSubroutineDIE()) return nullptr; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp index 5009b1b7b412..7d817d8a9925 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -93,6 +93,8 @@ static DescVector getDescriptions() { Descriptions[DW_OP_implicit_value] = Desc(Op::Dwarf3, Op::SizeLEB, Op::SizeBlock); Descriptions[DW_OP_stack_value] = Desc(Op::Dwarf3); + Descriptions[DW_OP_WASM_location] = + Desc(Op::Dwarf4, Op::SizeLEB, Op::SignedSizeLEB); Descriptions[DW_OP_GNU_push_tls_address] = Desc(Op::Dwarf3); Descriptions[DW_OP_addrx] = Desc(Op::Dwarf4, Op::SizeLEB); Descriptions[DW_OP_GNU_addr_index] = Desc(Op::Dwarf4, Op::SizeLEB); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp b/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp index 26090638b34c..e97ae81345b8 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp @@ -312,6 +312,7 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, case DW_FORM_udata: case DW_FORM_ref_udata: case DW_FORM_rnglistx: + case DW_FORM_loclistx: Value.uval = Data.getULEB128(OffsetPtr); break; case DW_FORM_string: @@ -411,7 +412,7 @@ void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { if (A) dumpSectionedAddress(AddrOS, DumpOpts, *A); else - OS << "<no .debug_addr section>"; + OS << "<unresolved>"; break; } case DW_FORM_flag_present: @@ -551,6 +552,10 @@ void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { OS << format("indexed (0x%x) rangelist = ", (uint32_t)UValue); break; + case DW_FORM_loclistx: + OS << format("indexed (0x%x) loclist = ", (uint32_t)UValue); + break; + // Should be formatted to 64-bit for DWARF64. case DW_FORM_sec_offset: AddrOS << format("0x%08x", (uint32_t)UValue); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFLocationExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFLocationExpression.cpp new file mode 100644 index 000000000000..1cf73a666778 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFLocationExpression.cpp @@ -0,0 +1,19 @@ +//===- DWARFLocationExpression.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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/DWARF/DWARFLocationExpression.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; + +raw_ostream &llvm::operator<<(raw_ostream &OS, + const DWARFLocationExpression &Loc) { + return OS << Loc.Range << ": " + << formatv("{0}", make_range(Loc.Expr.begin(), Loc.Expr.end())); +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp index a56402a707ad..7bb019466161 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -175,18 +175,37 @@ DWARFUnit::DWARFUnit(DWARFContext &DC, const DWARFSection &Section, const DWARFSection *AOS, const DWARFSection &LS, bool LE, bool IsDWO, const DWARFUnitVector &UnitVector) : Context(DC), InfoSection(Section), Header(Header), Abbrev(DA), - RangeSection(RS), LocSection(LocSection), LineSection(LS), - StringSection(SS), StringOffsetSection(SOS), AddrOffsetSection(AOS), - isLittleEndian(LE), IsDWO(IsDWO), UnitVector(UnitVector) { + RangeSection(RS), LineSection(LS), StringSection(SS), + StringOffsetSection(SOS), AddrOffsetSection(AOS), isLittleEndian(LE), + IsDWO(IsDWO), UnitVector(UnitVector) { clear(); - // For split DWARF we only need to keep track of the location list section's - // data (no relocations), and if we are reading a package file, we need to - // adjust the location list data based on the index entries. if (IsDWO) { - LocSectionData = LocSection->Data; + // If we are reading a package file, we need to adjust the location list + // data based on the index entries. + StringRef Data = LocSection->Data; if (auto *IndexEntry = Header.getIndexEntry()) if (const auto *C = IndexEntry->getOffset(DW_SECT_LOC)) - LocSectionData = LocSectionData.substr(C->Offset, C->Length); + Data = Data.substr(C->Offset, C->Length); + + DWARFDataExtractor DWARFData = + Header.getVersion() >= 5 + ? DWARFDataExtractor(Context.getDWARFObj(), + Context.getDWARFObj().getLoclistsDWOSection(), + isLittleEndian, getAddressByteSize()) + : DWARFDataExtractor(Data, isLittleEndian, getAddressByteSize()); + LocTable = + std::make_unique<DWARFDebugLoclists>(DWARFData, Header.getVersion()); + + } else if (Header.getVersion() >= 5) { + LocTable = std::make_unique<DWARFDebugLoclists>( + DWARFDataExtractor(Context.getDWARFObj(), + Context.getDWARFObj().getLoclistsSection(), + isLittleEndian, getAddressByteSize()), + Header.getVersion()); + } else { + LocTable = std::make_unique<DWARFDebugLoc>( + DWARFDataExtractor(Context.getDWARFObj(), *LocSection, isLittleEndian, + getAddressByteSize())); } } @@ -209,7 +228,9 @@ DWARFUnit::getAddrOffsetSectionItem(uint32_t Index) const { if (I != R.end() && std::next(I) == R.end()) return (*I)->getAddrOffsetSectionItem(Index); } - uint64_t Offset = AddrOffsetSectionBase + Index * getAddressByteSize(); + if (!AddrOffsetSectionBase) + return None; + uint64_t Offset = *AddrOffsetSectionBase + Index * getAddressByteSize(); if (AddrOffsetSection->Data.size() < Offset + getAddressByteSize()) return None; DWARFDataExtractor DA(Context.getDWARFObj(), *AddrOffsetSection, @@ -238,23 +259,26 @@ bool DWARFUnitHeader::extract(DWARFContext &Context, const DWARFUnitIndex *Index, const DWARFUnitIndex::Entry *Entry) { Offset = *offset_ptr; + Error Err = Error::success(); IndexEntry = Entry; if (!IndexEntry && Index) IndexEntry = Index->getFromOffset(*offset_ptr); - Length = debug_info.getRelocatedValue(4, offset_ptr); + Length = debug_info.getRelocatedValue(4, offset_ptr, nullptr, &Err); FormParams.Format = DWARF32; if (Length == dwarf::DW_LENGTH_DWARF64) { - Length = debug_info.getU64(offset_ptr); + Length = debug_info.getU64(offset_ptr, &Err); FormParams.Format = DWARF64; } - FormParams.Version = debug_info.getU16(offset_ptr); + FormParams.Version = debug_info.getU16(offset_ptr, &Err); if (FormParams.Version >= 5) { - UnitType = debug_info.getU8(offset_ptr); - FormParams.AddrSize = debug_info.getU8(offset_ptr); - AbbrOffset = debug_info.getRelocatedValue(FormParams.getDwarfOffsetByteSize(), offset_ptr); + UnitType = debug_info.getU8(offset_ptr, &Err); + FormParams.AddrSize = debug_info.getU8(offset_ptr, &Err); + AbbrOffset = debug_info.getRelocatedValue( + FormParams.getDwarfOffsetByteSize(), offset_ptr, nullptr, &Err); } else { - AbbrOffset = debug_info.getRelocatedValue(FormParams.getDwarfOffsetByteSize(), offset_ptr); - FormParams.AddrSize = debug_info.getU8(offset_ptr); + AbbrOffset = debug_info.getRelocatedValue( + FormParams.getDwarfOffsetByteSize(), offset_ptr, nullptr, &Err); + FormParams.AddrSize = debug_info.getU8(offset_ptr, &Err); // Fake a unit type based on the section type. This isn't perfect, // but distinguishing compile and type units is generally enough. if (SectionKind == DW_SECT_TYPES) @@ -274,11 +298,14 @@ bool DWARFUnitHeader::extract(DWARFContext &Context, AbbrOffset = AbbrEntry->Offset; } if (isTypeUnit()) { - TypeHash = debug_info.getU64(offset_ptr); - TypeOffset = - debug_info.getUnsigned(offset_ptr, FormParams.getDwarfOffsetByteSize()); + TypeHash = debug_info.getU64(offset_ptr, &Err); + TypeOffset = debug_info.getUnsigned( + offset_ptr, FormParams.getDwarfOffsetByteSize(), &Err); } else if (UnitType == DW_UT_split_compile || UnitType == DW_UT_skeleton) - DWOId = debug_info.getU64(offset_ptr); + DWOId = debug_info.getU64(offset_ptr, &Err); + + if (errorToBool(std::move(Err))) + return false; // Header fields all parsed, capture the size of this unit header. assert(*offset_ptr - Offset <= 255 && "unexpected header size"); @@ -305,8 +332,9 @@ bool DWARFUnitHeader::extract(DWARFContext &Context, // Parse the rangelist table header, including the optional array of offsets // following it (DWARF v5 and later). -static Expected<DWARFDebugRnglistTable> -parseRngListTableHeader(DWARFDataExtractor &DA, uint64_t Offset, +template<typename ListTableType> +static Expected<ListTableType> +parseListTableHeader(DWARFDataExtractor &DA, uint64_t Offset, DwarfFormat Format) { // We are expected to be called with Offset 0 or pointing just past the table // header. Correct Offset in the latter case so that it points to the start @@ -314,12 +342,12 @@ parseRngListTableHeader(DWARFDataExtractor &DA, uint64_t Offset, if (Offset > 0) { uint64_t HeaderSize = DWARFListTableHeader::getHeaderSize(Format); if (Offset < HeaderSize) - return createStringError(errc::invalid_argument, "Did not detect a valid" - " range list table with base = 0x%" PRIx64 "\n", + return createStringError(errc::invalid_argument, "did not detect a valid" + " list table with base = 0x%" PRIx64 "\n", Offset); Offset -= HeaderSize; } - llvm::DWARFDebugRnglistTable Table; + ListTableType Table; if (Error E = Table.extractHeaderAndOffsets(DA, &Offset)) return std::move(E); return Table; @@ -339,7 +367,8 @@ void DWARFUnit::clear() { Abbrevs = nullptr; BaseAddr.reset(); RangeSectionBase = 0; - AddrOffsetSectionBase = 0; + LocSectionBase = 0; + AddrOffsetSectionBase = None; clearDIEs(false); DWO.reset(); } @@ -427,13 +456,15 @@ Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) { if (Optional<uint64_t> DWOId = toUnsigned(UnitDie.find(DW_AT_GNU_dwo_id))) Header.setDWOId(*DWOId); if (!IsDWO) { - assert(AddrOffsetSectionBase == 0); + assert(AddrOffsetSectionBase == None); assert(RangeSectionBase == 0); - AddrOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_addr_base), 0); + assert(LocSectionBase == 0); + AddrOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_addr_base)); if (!AddrOffsetSectionBase) AddrOffsetSectionBase = - toSectionOffset(UnitDie.find(DW_AT_GNU_addr_base), 0); + toSectionOffset(UnitDie.find(DW_AT_GNU_addr_base)); RangeSectionBase = toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0); + LocSectionBase = toSectionOffset(UnitDie.find(DW_AT_loclists_base), 0); } // In general, in DWARF v5 and beyond we derive the start of the unit's @@ -471,8 +502,8 @@ Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) { // extracted lazily. DWARFDataExtractor RangesDA(Context.getDWARFObj(), *RangeSection, isLittleEndian, 0); - auto TableOrError = parseRngListTableHeader(RangesDA, RangeSectionBase, - Header.getFormat()); + auto TableOrError = parseListTableHeader<DWARFDebugRnglistTable>( + RangesDA, RangeSectionBase, Header.getFormat()); if (!TableOrError) return createStringError(errc::invalid_argument, "parsing a range list table: " + @@ -485,6 +516,37 @@ Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) { if (IsDWO && RngListTable) RangeSectionBase = RngListTable->getHeaderSize(); } + + // In a split dwarf unit, there is no DW_AT_loclists_base attribute. + // Setting LocSectionBase to point past the table header. + if (IsDWO) + setLocSection(&Context.getDWARFObj().getLoclistsDWOSection(), + DWARFListTableHeader::getHeaderSize(Header.getFormat())); + else + setLocSection(&Context.getDWARFObj().getLoclistsSection(), + toSectionOffset(UnitDie.find(DW_AT_loclists_base), 0)); + + if (LocSection->Data.size()) { + if (IsDWO) + LoclistTableHeader.emplace(".debug_loclists.dwo", "locations"); + else + LoclistTableHeader.emplace(".debug_loclists", "locations"); + + uint64_t HeaderSize = DWARFListTableHeader::getHeaderSize(Header.getFormat()); + uint64_t Offset = getLocSectionBase(); + DWARFDataExtractor Data(Context.getDWARFObj(), *LocSection, + isLittleEndian, getAddressByteSize()); + if (Offset < HeaderSize) + return createStringError(errc::invalid_argument, + "did not detect a valid" + " list table with base = 0x%" PRIx64 "\n", + Offset); + Offset -= HeaderSize; + if (Error E = LoclistTableHeader->extract(Data, &Offset)) + return createStringError(errc::invalid_argument, + "parsing a loclist table: " + + toString(std::move(E))); + } } // Don't fall back to DW_AT_GNU_ranges_base: it should be ignored for @@ -500,7 +562,9 @@ bool DWARFUnit::parseDWO() { DWARFDie UnitDie = getUnitDIE(); if (!UnitDie) return false; - auto DWOFileName = dwarf::toString(UnitDie.find(DW_AT_GNU_dwo_name)); + auto DWOFileName = getVersion() >= 5 + ? dwarf::toString(UnitDie.find(DW_AT_dwo_name)) + : dwarf::toString(UnitDie.find(DW_AT_GNU_dwo_name)); if (!DWOFileName) return false; auto CompilationDir = dwarf::toString(UnitDie.find(DW_AT_comp_dir)); @@ -522,13 +586,14 @@ bool DWARFUnit::parseDWO() { 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 (AddrOffsetSectionBase) + 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, - Header.getFormat())) + if (auto TableOrError = parseListTableHeader<DWARFDebugRnglistTable>( + RangesDA, RangeSectionBase, Header.getFormat())) DWO->RngListTable = TableOrError.get(); else WithColor::error() << "parsing a range list table: " @@ -575,7 +640,7 @@ DWARFUnit::findRnglistFromOffset(uint64_t Offset) { Expected<DWARFAddressRangesVector> DWARFUnit::findRnglistFromIndex(uint32_t Index) { if (auto Offset = getRnglistOffset(Index)) - return findRnglistFromOffset(*Offset + RangeSectionBase); + return findRnglistFromOffset(*Offset); if (RngListTable) return createStringError(errc::invalid_argument, @@ -599,6 +664,30 @@ Expected<DWARFAddressRangesVector> DWARFUnit::collectAddressRanges() { return *CUDIERangesOrError; } +Expected<DWARFLocationExpressionsVector> +DWARFUnit::findLoclistFromOffset(uint64_t Offset) { + DWARFLocationExpressionsVector Result; + + Error InterpretationError = Error::success(); + + Error ParseError = getLocationTable().visitAbsoluteLocationList( + Offset, getBaseAddress(), + [this](uint32_t Index) { return getAddrOffsetSectionItem(Index); }, + [&](Expected<DWARFLocationExpression> L) { + if (L) + Result.push_back(std::move(*L)); + else + InterpretationError = + joinErrors(L.takeError(), std::move(InterpretationError)); + return !InterpretationError; + }); + + if (ParseError || InterpretationError) + return joinErrors(std::move(ParseError), std::move(InterpretationError)); + + return Result; +} + void DWARFUnit::updateAddressDieMap(DWARFDie Die) { if (Die.isSubroutineDIE()) { auto DIERangesOrError = Die.getAddressRanges(); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp index bf499b6ee092..1fd6c1d7d282 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -196,6 +196,14 @@ unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit) { NumUnitErrors++; } + // According to DWARF Debugging Information Format Version 5, + // 3.1.2 Skeleton Compilation Unit Entries: + // "A skeleton compilation unit has no children." + if (Die.getTag() == dwarf::DW_TAG_skeleton_unit && Die.hasChildren()) { + error() << "Skeleton compilation unit has children.\n"; + NumUnitErrors++; + } + DieRangeInfo RI; NumUnitErrors += verifyDieRanges(Die, RI); @@ -468,27 +476,21 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, ReportError("DIE has invalid DW_AT_stmt_list encoding:"); break; case DW_AT_location: { - auto VerifyLocationExpr = [&](ArrayRef<uint8_t> D) { + if (Expected<std::vector<DWARFLocationExpression>> Loc = + Die.getLocations(DW_AT_location)) { DWARFUnit *U = Die.getDwarfUnit(); - DataExtractor Data(toStringRef(D), DCtx.isLittleEndian(), 0); - DWARFExpression Expression(Data, U->getVersion(), - U->getAddressByteSize()); - bool Error = llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { - return Op.isError(); - }); - if (Error || !Expression.verify(U)) - ReportError("DIE contains invalid DWARF expression:"); - }; - if (Optional<ArrayRef<uint8_t>> Expr = AttrValue.Value.getAsBlock()) { - // Verify inlined location. - VerifyLocationExpr(*Expr); - } else if (auto LocOffset = AttrValue.Value.getAsSectionOffset()) { - // Verify location list. - if (auto DebugLoc = DCtx.getDebugLoc()) - if (auto LocList = DebugLoc->getLocationListAtOffset(*LocOffset)) - for (const auto &Entry : LocList->Entries) - VerifyLocationExpr(Entry.Loc); - } + for (const auto &Entry : *Loc) { + DataExtractor Data(toStringRef(Entry.Expr), DCtx.isLittleEndian(), 0); + DWARFExpression Expression(Data, U->getVersion(), + U->getAddressByteSize()); + bool Error = any_of(Expression, [](DWARFExpression::Operation &Op) { + return Op.isError(); + }); + if (Error || !Expression.verify(U)) + ReportError("DIE contains invalid DWARF expression:"); + } + } else + ReportError(toString(Loc.takeError())); break; } case DW_AT_specification: @@ -640,7 +642,7 @@ unsigned DWARFVerifier::verifyDebugInfoReferences() { // getting the DIE by offset and emitting an error OS << "Verifying .debug_info references...\n"; unsigned NumErrors = 0; - for (const std::pair<uint64_t, std::set<uint64_t>> &Pair : + for (const std::pair<const uint64_t, std::set<uint64_t>> &Pair : ReferenceToDIEOffsets) { if (DCtx.getDIEForOffset(Pair.first)) continue; @@ -968,7 +970,7 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI, constexpr BucketInfo(uint32_t Bucket, uint32_t Index) : Bucket(Bucket), Index(Index) {} - bool operator<(const BucketInfo &RHS) const { return Index < RHS.Index; }; + bool operator<(const BucketInfo &RHS) const { return Index < RHS.Index; } }; uint32_t NumErrors = 0; @@ -1278,36 +1280,24 @@ unsigned DWARFVerifier::verifyNameIndexEntries( } static bool isVariableIndexable(const DWARFDie &Die, DWARFContext &DCtx) { - Optional<DWARFFormValue> Location = Die.findRecursively(DW_AT_location); - if (!Location) + Expected<std::vector<DWARFLocationExpression>> Loc = + Die.getLocations(DW_AT_location); + if (!Loc) { + consumeError(Loc.takeError()); return false; - - auto ContainsInterestingOperators = [&](ArrayRef<uint8_t> D) { - DWARFUnit *U = Die.getDwarfUnit(); - DataExtractor Data(toStringRef(D), DCtx.isLittleEndian(), U->getAddressByteSize()); + } + DWARFUnit *U = Die.getDwarfUnit(); + for (const auto &Entry : *Loc) { + DataExtractor Data(toStringRef(Entry.Expr), DCtx.isLittleEndian(), + U->getAddressByteSize()); DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize()); - return any_of(Expression, [](DWARFExpression::Operation &Op) { + bool IsInteresting = any_of(Expression, [](DWARFExpression::Operation &Op) { return !Op.isError() && (Op.getCode() == DW_OP_addr || Op.getCode() == DW_OP_form_tls_address || Op.getCode() == DW_OP_GNU_push_tls_address); }); - }; - - if (Optional<ArrayRef<uint8_t>> Expr = Location->getAsBlock()) { - // Inlined location. - if (ContainsInterestingOperators(*Expr)) + if (IsInteresting) return true; - } else if (Optional<uint64_t> Offset = Location->getAsSectionOffset()) { - // Location list. - if (const DWARFDebugLoc *DebugLoc = DCtx.getDebugLoc()) { - if (const DWARFDebugLoc::LocationList *LocList = - DebugLoc->getLocationListAtOffset(*Offset)) { - if (any_of(LocList->Entries, [&](const DWARFDebugLoc::Entry &E) { - return ContainsInterestingOperators(E.Loc); - })) - return true; - } - } } return false; } diff --git a/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp b/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp index ad022fec9e32..6731a8b27443 100644 --- a/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp +++ b/llvm/lib/DebugInfo/GSYM/FunctionInfo.cpp @@ -8,6 +8,7 @@ #include "llvm/DebugInfo/GSYM/FunctionInfo.h" #include "llvm/DebugInfo/GSYM/FileWriter.h" +#include "llvm/DebugInfo/GSYM/GsymReader.h" #include "llvm/DebugInfo/GSYM/LineTable.h" #include "llvm/DebugInfo/GSYM/InlineInfo.h" #include "llvm/Support/DataExtractor.h" @@ -114,7 +115,7 @@ llvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &O) const { llvm::Error err = OptLineTable->encode(O, Range.Start); if (err) return std::move(err); - const off_t Length = O.tell() - StartOffset; + const auto Length = O.tell() - StartOffset; if (Length > UINT32_MAX) return createStringError(std::errc::invalid_argument, "LineTable length is greater than UINT32_MAX"); @@ -132,7 +133,7 @@ llvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &O) const { llvm::Error err = Inline->encode(O, Range.Start); if (err) return std::move(err); - const off_t Length = O.tell() - StartOffset; + const auto Length = O.tell() - StartOffset; if (Length > UINT32_MAX) return createStringError(std::errc::invalid_argument, "InlineInfo length is greater than UINT32_MAX"); @@ -145,3 +146,104 @@ llvm::Expected<uint64_t> FunctionInfo::encode(FileWriter &O) const { O.writeU32(0); return FuncInfoOffset; } + + +llvm::Expected<LookupResult> FunctionInfo::lookup(DataExtractor &Data, + const GsymReader &GR, + uint64_t FuncAddr, + uint64_t Addr) { + LookupResult LR; + LR.LookupAddr = Addr; + LR.FuncRange.Start = FuncAddr; + uint64_t Offset = 0; + LR.FuncRange.End = FuncAddr + Data.getU32(&Offset); + uint32_t NameOffset = Data.getU32(&Offset); + // The "lookup" functions doesn't report errors as accurately as the "decode" + // function as it is meant to be fast. For more accurage errors we could call + // "decode". + if (!Data.isValidOffset(Offset)) + return createStringError(std::errc::io_error, + "FunctionInfo data is truncated"); + // This function will be called with the result of a binary search of the + // address table, we must still make sure the address does not fall into a + // gap between functions are after the last function. + if (Addr >= LR.FuncRange.End) + return createStringError(std::errc::io_error, + "address 0x%" PRIx64 " is not in GSYM", Addr); + + if (NameOffset == 0) + return createStringError(std::errc::io_error, + "0x%8.8" PRIx64 ": invalid FunctionInfo Name value 0x00000000", + Offset - 4); + LR.FuncName = GR.getString(NameOffset); + bool Done = false; + Optional<LineEntry> LineEntry; + Optional<DataExtractor> InlineInfoData; + while (!Done) { + if (!Data.isValidOffsetForDataOfSize(Offset, 8)) + return createStringError(std::errc::io_error, + "FunctionInfo data is truncated"); + const uint32_t IT = Data.getU32(&Offset); + const uint32_t InfoLength = Data.getU32(&Offset); + const StringRef InfoBytes = Data.getData().substr(Offset, InfoLength); + if (InfoLength != InfoBytes.size()) + return createStringError(std::errc::io_error, + "FunctionInfo data is truncated"); + DataExtractor InfoData(InfoBytes, Data.isLittleEndian(), + Data.getAddressSize()); + switch (IT) { + case InfoType::EndOfList: + Done = true; + break; + + case InfoType::LineTableInfo: + if (auto ExpectedLE = LineTable::lookup(InfoData, FuncAddr, Addr)) + LineEntry = ExpectedLE.get(); + else + return ExpectedLE.takeError(); + break; + + case InfoType::InlineInfo: + // We will parse the inline info after our line table, but only if + // we have a line entry. + InlineInfoData = InfoData; + break; + + default: + break; + } + Offset += InfoLength; + } + + if (!LineEntry) { + // We don't have a valid line entry for our address, fill in our source + // location as best we can and return. + SourceLocation SrcLoc; + SrcLoc.Name = LR.FuncName; + LR.Locations.push_back(SrcLoc); + return LR; + } + + Optional<FileEntry> LineEntryFile = GR.getFile(LineEntry->File); + if (!LineEntryFile) + return createStringError(std::errc::invalid_argument, + "failed to extract file[%" PRIu32 "]", + LineEntry->File); + + SourceLocation SrcLoc; + SrcLoc.Name = LR.FuncName; + SrcLoc.Dir = GR.getString(LineEntryFile->Dir); + SrcLoc.Base = GR.getString(LineEntryFile->Base); + SrcLoc.Line = LineEntry->Line; + LR.Locations.push_back(SrcLoc); + // If we don't have inline information, we are done. + if (!InlineInfoData) + return LR; + // We have inline information. Try to augment the lookup result with this + // data. + llvm::Error Err = InlineInfo::lookup(GR, *InlineInfoData, FuncAddr, Addr, + LR.Locations); + if (Err) + return std::move(Err); + return LR; +} diff --git a/llvm/lib/DebugInfo/GSYM/GsymReader.cpp b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp index 1b448cf80b70..b4f3f2052ae7 100644 --- a/llvm/lib/DebugInfo/GSYM/GsymReader.cpp +++ b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp @@ -1,9 +1,8 @@ //===- GsymReader.cpp -----------------------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -263,3 +262,18 @@ llvm::Expected<FunctionInfo> GsymReader::getFunctionInfo(uint64_t Addr) const { "failed to extract address[%" PRIu64 "]", *AddressIndex); } + +llvm::Expected<LookupResult> GsymReader::lookup(uint64_t Addr) const { + Expected<uint64_t> AddressIndex = getAddressIndex(Addr); + if (!AddressIndex) + return AddressIndex.takeError(); + // Address info offsets size should have been checked in parse(). + assert(*AddressIndex < AddrInfoOffsets.size()); + auto AddrInfoOffset = AddrInfoOffsets[*AddressIndex]; + DataExtractor Data(MemBuffer->getBuffer().substr(AddrInfoOffset), Endian, 4); + if (Optional<uint64_t> OptAddr = getAddress(*AddressIndex)) + return FunctionInfo::lookup(Data, *this, *OptAddr, Addr); + return createStringError(std::errc::invalid_argument, + "failed to extract address[%" PRIu64 "]", + *AddressIndex); +} diff --git a/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp b/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp index 32ed2c709575..1b8c974fdcd2 100644 --- a/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp +++ b/llvm/lib/DebugInfo/GSYM/InlineInfo.cpp @@ -1,14 +1,14 @@ //===- InlineInfo.cpp -------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/GSYM/FileEntry.h" #include "llvm/DebugInfo/GSYM/FileWriter.h" +#include "llvm/DebugInfo/GSYM/GsymReader.h" #include "llvm/DebugInfo/GSYM/InlineInfo.h" #include "llvm/Support/DataExtractor.h" #include <algorithm> @@ -60,6 +60,108 @@ llvm::Optional<InlineInfo::InlineArray> InlineInfo::getInlineStack(uint64_t Addr return llvm::None; } +/// Skip an InlineInfo object in the specified data at the specified offset. +/// +/// Used during the InlineInfo::lookup() call to quickly skip child InlineInfo +/// objects where the addres ranges isn't contained in the InlineInfo object +/// or its children. This avoids allocations by not appending child InlineInfo +/// objects to the InlineInfo::Children array. +/// +/// \param Data The binary stream to read the data from. +/// +/// \param Offset The byte offset within \a Data. +/// +/// \param SkippedRanges If true, address ranges have already been skipped. + +static bool skip(DataExtractor &Data, uint64_t &Offset, bool SkippedRanges) { + if (!SkippedRanges) { + if (AddressRanges::skip(Data, Offset) == 0) + return false; + } + bool HasChildren = Data.getU8(&Offset) != 0; + Data.getU32(&Offset); // Skip Inline.Name. + Data.getULEB128(&Offset); // Skip Inline.CallFile. + Data.getULEB128(&Offset); // Skip Inline.CallLine. + if (HasChildren) { + while (skip(Data, Offset, false /* SkippedRanges */)) + /* Do nothing */; + } + // We skipped a valid InlineInfo. + return true; +} + +/// A Lookup helper functions. +/// +/// Used during the InlineInfo::lookup() call to quickly only parse an +/// InlineInfo object if the address falls within this object. This avoids +/// allocations by not appending child InlineInfo objects to the +/// InlineInfo::Children array and also skips any InlineInfo objects that do +/// not contain the address we are looking up. +/// +/// \param Data The binary stream to read the data from. +/// +/// \param Offset The byte offset within \a Data. +/// +/// \param BaseAddr The address that the relative address range offsets are +/// relative to. + +static bool lookup(const GsymReader &GR, DataExtractor &Data, uint64_t &Offset, + uint64_t BaseAddr, uint64_t Addr, SourceLocations &SrcLocs, + llvm::Error &Err) { + InlineInfo Inline; + Inline.Ranges.decode(Data, BaseAddr, Offset); + if (Inline.Ranges.empty()) + return true; + // Check if the address is contained within the inline information, and if + // not, quickly skip this InlineInfo object and all its children. + if (!Inline.Ranges.contains(Addr)) { + skip(Data, Offset, true /* SkippedRanges */); + return false; + } + + // The address range is contained within this InlineInfo, add the source + // location for this InlineInfo and any children that contain the address. + bool HasChildren = Data.getU8(&Offset) != 0; + Inline.Name = Data.getU32(&Offset); + Inline.CallFile = (uint32_t)Data.getULEB128(&Offset); + Inline.CallLine = (uint32_t)Data.getULEB128(&Offset); + if (HasChildren) { + // Child address ranges are encoded relative to the first address in the + // parent InlineInfo object. + const auto ChildBaseAddr = Inline.Ranges[0].Start; + bool Done = false; + while (!Done) + Done = lookup(GR, Data, Offset, ChildBaseAddr, Addr, SrcLocs, Err); + } + + Optional<FileEntry> CallFile = GR.getFile(Inline.CallFile); + if (!CallFile) { + Err = createStringError(std::errc::invalid_argument, + "failed to extract file[%" PRIu32 "]", + Inline.CallFile); + return false; + } + + SourceLocation SrcLoc; + SrcLoc.Name = SrcLocs.back().Name; + SrcLoc.Dir = GR.getString(CallFile->Dir); + SrcLoc.Base = GR.getString(CallFile->Base); + SrcLoc.Line = Inline.CallLine; + SrcLocs.back().Name = GR.getString(Inline.Name); + SrcLocs.push_back(SrcLoc); + return true; +} + +llvm::Error InlineInfo::lookup(const GsymReader &GR, DataExtractor &Data, + uint64_t BaseAddr, uint64_t Addr, + SourceLocations &SrcLocs) { + // Call our recursive helper function starting at offset zero. + uint64_t Offset = 0; + llvm::Error Err = Error::success(); + ::lookup(GR, Data, Offset, BaseAddr, Addr, SrcLocs, Err); + return Err; +} + /// Decode an InlineInfo in Data at the specified offset. /// /// A local helper function to decode InlineInfo objects. This function is diff --git a/llvm/lib/DebugInfo/GSYM/LineTable.cpp b/llvm/lib/DebugInfo/GSYM/LineTable.cpp index 824c0041be9f..a49a3ba9bf2a 100644 --- a/llvm/lib/DebugInfo/GSYM/LineTable.cpp +++ b/llvm/lib/DebugInfo/GSYM/LineTable.cpp @@ -262,8 +262,8 @@ llvm::Expected<LineTable> LineTable::decode(DataExtractor &Data, // Parse the line table on the fly and find the row we are looking for. // We will need to determine if we need to cache the line table by calling // LineTable::parseAllEntries(...) or just call this function each time. -// There is a CPU vs memory tradeoff we will need to determine. -LineEntry LineTable::lookup(DataExtractor &Data, uint64_t BaseAddr, uint64_t Addr) { +// There is a CPU vs memory tradeoff we will need to determined. +Expected<LineEntry> LineTable::lookup(DataExtractor &Data, uint64_t BaseAddr, uint64_t Addr) { LineEntry Result; llvm::Error Err = parse(Data, BaseAddr, [Addr, &Result](const LineEntry &Row) -> bool { @@ -277,7 +277,13 @@ LineEntry LineTable::lookup(DataExtractor &Data, uint64_t BaseAddr, uint64_t Add } return true; // Keep parsing till we find the right row. }); - return Result; + if (Err) + return std::move(Err); + if (Result.isValid()) + return Result; + return createStringError(std::errc::invalid_argument, + "address 0x%" PRIx64 " is not in the line table", + Addr); } raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const LineTable <) { diff --git a/llvm/lib/DebugInfo/GSYM/LookupResult.cpp b/llvm/lib/DebugInfo/GSYM/LookupResult.cpp new file mode 100644 index 000000000000..c54b166b2887 --- /dev/null +++ b/llvm/lib/DebugInfo/GSYM/LookupResult.cpp @@ -0,0 +1,69 @@ +//===- LookupResult.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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/GSYM/LookupResult.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <ciso646> + +using namespace llvm; +using namespace gsym; + +std::string LookupResult::getSourceFile(uint32_t Index) const { + std::string Fullpath; + if (Index < Locations.size()) { + if (!Locations[Index].Dir.empty()) { + if (Locations[Index].Base.empty()) { + Fullpath = Locations[Index].Dir; + } else { + llvm::SmallString<64> Storage; + llvm::sys::path::append(Storage, Locations[Index].Dir, + Locations[Index].Base); + Fullpath.assign(Storage.begin(), Storage.end()); + } + } else if (!Locations[Index].Base.empty()) + Fullpath = Locations[Index].Base; + } + return Fullpath; +} + +raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const SourceLocation &SL) { + OS << SL.Name << " @ "; + if (!SL.Dir.empty()) { + OS << SL.Dir; + if (SL.Dir.contains('\\') and not SL.Dir.contains('/')) + OS << '\\'; + else + OS << '/'; + } + if (SL.Base.empty()) + OS << "<invalid-file>"; + else + OS << SL.Base; + OS << ':' << SL.Line; + return OS; +} + +raw_ostream &llvm::gsym::operator<<(raw_ostream &OS, const LookupResult &LR) { + OS << HEX64(LR.LookupAddr) << ": "; + auto NumLocations = LR.Locations.size(); + for (size_t I = 0; I < NumLocations; ++I) { + if (I > 0) { + OS << '\n'; + OS.indent(20); + } + const bool IsInlined = I + 1 != NumLocations; + OS << LR.Locations[I]; + if (IsInlined) + OS << " [inlined]"; + } + OS << '\n'; + return OS; +} diff --git a/llvm/lib/DebugInfo/GSYM/Range.cpp b/llvm/lib/DebugInfo/GSYM/Range.cpp index 19ab700fdd57..f78101e49bf8 100644 --- a/llvm/lib/DebugInfo/GSYM/Range.cpp +++ b/llvm/lib/DebugInfo/GSYM/Range.cpp @@ -100,3 +100,15 @@ void AddressRanges::decode(DataExtractor &Data, uint64_t BaseAddr, for (auto &Range : Ranges) Range.decode(Data, BaseAddr, Offset); } + +void AddressRange::skip(DataExtractor &Data, uint64_t &Offset) { + Data.getULEB128(&Offset); + Data.getULEB128(&Offset); +} + +uint64_t AddressRanges::skip(DataExtractor &Data, uint64_t &Offset) { + uint64_t NumRanges = Data.getULEB128(&Offset); + for (uint64_t I=0; I<NumRanges; ++I) + AddressRange::skip(Data, Offset); + return NumRanges; +} diff --git a/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp b/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp index 5095efcdee3c..9755f2ca3bdc 100644 --- a/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/DbiModuleDescriptor.cpp @@ -18,13 +18,6 @@ using namespace llvm; using namespace llvm::pdb; using namespace llvm::support; -DbiModuleDescriptor::DbiModuleDescriptor() = default; - -DbiModuleDescriptor::DbiModuleDescriptor(const DbiModuleDescriptor &Info) = - default; - -DbiModuleDescriptor::~DbiModuleDescriptor() = default; - Error DbiModuleDescriptor::initialize(BinaryStreamRef Stream, DbiModuleDescriptor &Info) { BinaryStreamReader Reader(Stream); diff --git a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp index b1a80cbc4580..2f3a2500c293 100644 --- a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp +++ b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp @@ -118,21 +118,32 @@ DIPrinter &DIPrinter::operator<<(const DIGlobal &Global) { } DIPrinter &DIPrinter::operator<<(const DILocal &Local) { - OS << Local.FunctionName << '\n'; - OS << Local.Name << '\n'; + if (Local.FunctionName.empty()) + OS << "??\n"; + else + OS << Local.FunctionName << '\n'; + + if (Local.Name.empty()) + OS << "??\n"; + else + OS << Local.Name << '\n'; + if (Local.DeclFile.empty()) OS << "??"; else OS << Local.DeclFile; OS << ':' << Local.DeclLine << '\n'; + if (Local.FrameOffset) OS << *Local.FrameOffset << ' '; else OS << "?? "; + if (Local.Size) OS << *Local.Size << ' '; else OS << "?? "; + if (Local.TagOffset) OS << *Local.TagOffset << '\n'; else diff --git a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp index be79d9e637c1..35e3ead6317b 100644 --- a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp +++ b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp @@ -284,6 +284,79 @@ bool darwinDsymMatchesBinary(const MachOObjectFile *DbgObj, return !memcmp(dbg_uuid.data(), bin_uuid.data(), dbg_uuid.size()); } +template <typename ELFT> +Optional<ArrayRef<uint8_t>> getBuildID(const ELFFile<ELFT> *Obj) { + if (!Obj) + return {}; + auto PhdrsOrErr = Obj->program_headers(); + if (!PhdrsOrErr) { + consumeError(PhdrsOrErr.takeError()); + return {}; + } + for (const auto &P : *PhdrsOrErr) { + if (P.p_type != ELF::PT_NOTE) + continue; + Error Err = Error::success(); + for (auto N : Obj->notes(P, Err)) + if (N.getType() == ELF::NT_GNU_BUILD_ID && N.getName() == ELF::ELF_NOTE_GNU) + return N.getDesc(); + } + return {}; +} + +Optional<ArrayRef<uint8_t>> getBuildID(const ELFObjectFileBase *Obj) { + Optional<ArrayRef<uint8_t>> BuildID; + if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(Obj)) + BuildID = getBuildID(O->getELFFile()); + else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(Obj)) + BuildID = getBuildID(O->getELFFile()); + else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(Obj)) + BuildID = getBuildID(O->getELFFile()); + else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Obj)) + BuildID = getBuildID(O->getELFFile()); + else + llvm_unreachable("unsupported file format"); + return BuildID; +} + +bool findDebugBinary(const std::vector<std::string> &DebugFileDirectory, + const ArrayRef<uint8_t> BuildID, + std::string &Result) { + auto getDebugPath = [&](StringRef Directory) { + SmallString<128> Path{Directory}; + sys::path::append(Path, ".build-id", + llvm::toHex(BuildID[0], /*LowerCase=*/true), + llvm::toHex(BuildID.slice(1), /*LowerCase=*/true)); + Path += ".debug"; + return Path; + }; + if (DebugFileDirectory.empty()) { + SmallString<128> Path = getDebugPath( +#if defined(__NetBSD__) + // Try /usr/libdata/debug/.build-id/../... + "/usr/libdata/debug" +#else + // Try /usr/lib/debug/.build-id/../... + "/usr/lib/debug" +#endif + ); + if (llvm::sys::fs::exists(Path)) { + Result = Path.str(); + return true; + } + } else { + for (const auto &Directory : DebugFileDirectory) { + // Try <debug-file-directory>/.build-id/../... + SmallString<128> Path = getDebugPath(Directory); + if (llvm::sys::fs::exists(Path)) { + Result = Path.str(); + return true; + } + } + } + return false; +} + } // end anonymous namespace ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath, @@ -335,6 +408,25 @@ ObjectFile *LLVMSymbolizer::lookUpDebuglinkObject(const std::string &Path, return DbgObjOrErr.get(); } +ObjectFile *LLVMSymbolizer::lookUpBuildIDObject(const std::string &Path, + const ELFObjectFileBase *Obj, + const std::string &ArchName) { + auto BuildID = getBuildID(Obj); + if (!BuildID) + return nullptr; + if (BuildID->size() < 2) + return nullptr; + std::string DebugBinaryPath; + if (!findDebugBinary(Opts.DebugFileDirectory, *BuildID, DebugBinaryPath)) + return nullptr; + auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName); + if (!DbgObjOrErr) { + consumeError(DbgObjOrErr.takeError()); + return nullptr; + } + return DbgObjOrErr.get(); +} + Expected<LLVMSymbolizer::ObjectPair> LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path, const std::string &ArchName) { @@ -355,6 +447,8 @@ LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path, if (auto MachObj = dyn_cast<const MachOObjectFile>(Obj)) DbgObj = lookUpDsymFile(Path, MachObj, ArchName); + else if (auto ELFObj = dyn_cast<const ELFObjectFileBase>(Obj)) + DbgObj = lookUpBuildIDObject(Path, ELFObj, ArchName); if (!DbgObj) DbgObj = lookUpDebuglinkObject(Path, Obj, ArchName); if (!DbgObj) |
