diff options
Diffstat (limited to 'llvm/lib/DebugInfo/DWARF')
21 files changed, 2159 insertions, 1194 deletions
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp index abbea3a868c84..ee1ff5460b9ba 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFAbbreviationDeclaration.cpp @@ -150,6 +150,8 @@ DWARFAbbreviationDeclaration::findAttributeIndex(dwarf::Attribute Attr) const { Optional<DWARFFormValue> DWARFAbbreviationDeclaration::getAttributeValue( const uint64_t DIEOffset, const dwarf::Attribute Attr, const DWARFUnit &U) const { + // Check if this abbreviation has this attribute without needing to skip + // any data so we can return quickly if it doesn't. Optional<uint32_t> MatchAttrIndex = findAttributeIndex(Attr); if (!MatchAttrIndex) return None; @@ -159,26 +161,24 @@ Optional<DWARFFormValue> DWARFAbbreviationDeclaration::getAttributeValue( // Add the byte size of ULEB that for the abbrev Code so we can start // skipping the attribute data. uint64_t Offset = DIEOffset + CodeByteSize; - uint32_t AttrIndex = 0; - for (const auto &Spec : AttributeSpecs) { - if (*MatchAttrIndex == AttrIndex) { - // We have arrived at the attribute to extract, extract if from Offset. - if (Spec.isImplicitConst()) - return DWARFFormValue::createFromSValue(Spec.Form, - Spec.getImplicitConstValue()); - - DWARFFormValue FormValue(Spec.Form); - if (FormValue.extractValue(DebugInfoData, &Offset, U.getFormParams(), &U)) - return FormValue; - } - // March Offset along until we get to the attribute we want. - if (auto FixedSize = Spec.getByteSize(U)) + for (uint32_t CurAttrIdx = 0; CurAttrIdx != *MatchAttrIndex; ++CurAttrIdx) + // Match Offset along until we get to the attribute we want. + if (auto FixedSize = AttributeSpecs[CurAttrIdx].getByteSize(U)) Offset += *FixedSize; else - DWARFFormValue::skipValue(Spec.Form, DebugInfoData, &Offset, - U.getFormParams()); - ++AttrIndex; - } + DWARFFormValue::skipValue(AttributeSpecs[CurAttrIdx].Form, DebugInfoData, + &Offset, U.getFormParams()); + + // We have arrived at the attribute to extract, extract if from Offset. + const AttributeSpec &Spec = AttributeSpecs[*MatchAttrIndex]; + if (Spec.isImplicitConst()) + return DWARFFormValue::createFromSValue(Spec.Form, + Spec.getImplicitConstValue()); + + DWARFFormValue FormValue(Spec.Form); + if (FormValue.extractValue(DebugInfoData, &Offset, U.getFormParams(), &U)) + return FormValue; + return None; } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp index 575edba51ee89..28d35b609c242 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp @@ -365,8 +365,8 @@ AppleAcceleratorTable::equal_range(StringRef Key) const { void DWARFDebugNames::Header::dump(ScopedPrinter &W) const { DictScope HeaderScope(W, "Header"); W.printHex("Length", UnitLength); + W.printString("Format", dwarf::FormatString(Format)); W.printNumber("Version", Version); - W.printHex("Padding", Padding); W.printNumber("CU count", CompUnitCount); W.printNumber("Local TU count", LocalTypeUnitCount); W.printNumber("Foreign TU count", ForeignTypeUnitCount); @@ -378,30 +378,36 @@ void DWARFDebugNames::Header::dump(ScopedPrinter &W) const { Error DWARFDebugNames::Header::extract(const DWARFDataExtractor &AS, uint64_t *Offset) { - // Check that we can read the fixed-size part. - if (!AS.isValidOffset(*Offset + sizeof(HeaderPOD) - 1)) + auto HeaderError = [Offset = *Offset](Error E) { return createStringError(errc::illegal_byte_sequence, - "Section too small: cannot read header."); - - UnitLength = AS.getU32(Offset); - Version = AS.getU16(Offset); - Padding = AS.getU16(Offset); - CompUnitCount = AS.getU32(Offset); - LocalTypeUnitCount = AS.getU32(Offset); - ForeignTypeUnitCount = AS.getU32(Offset); - BucketCount = AS.getU32(Offset); - NameCount = AS.getU32(Offset); - AbbrevTableSize = AS.getU32(Offset); - AugmentationStringSize = alignTo(AS.getU32(Offset), 4); - - if (!AS.isValidOffsetForDataOfSize(*Offset, AugmentationStringSize)) - return createStringError( - errc::illegal_byte_sequence, - "Section too small: cannot read header augmentation."); + "parsing .debug_names header at 0x%" PRIx64 ": %s", + Offset, toString(std::move(E)).c_str()); + }; + + DataExtractor::Cursor C(*Offset); + std::tie(UnitLength, Format) = AS.getInitialLength(C); + + Version = AS.getU16(C); + AS.skip(C, 2); // padding + CompUnitCount = AS.getU32(C); + LocalTypeUnitCount = AS.getU32(C); + ForeignTypeUnitCount = AS.getU32(C); + BucketCount = AS.getU32(C); + NameCount = AS.getU32(C); + AbbrevTableSize = AS.getU32(C); + AugmentationStringSize = alignTo(AS.getU32(C), 4); + + if (!C) + return HeaderError(C.takeError()); + + if (!AS.isValidOffsetForDataOfSize(C.tell(), AugmentationStringSize)) + return HeaderError(createStringError(errc::illegal_byte_sequence, + "cannot read header augmentation")); AugmentationString.resize(AugmentationStringSize); - AS.getU8(Offset, reinterpret_cast<uint8_t *>(AugmentationString.data()), + AS.getU8(C, reinterpret_cast<uint8_t *>(AugmentationString.data()), AugmentationStringSize); - return Error::success(); + *Offset = C.tell(); + return C.takeError(); } void DWARFDebugNames::Abbrev::dump(ScopedPrinter &W) const { @@ -486,9 +492,10 @@ Error DWARFDebugNames::NameIndex::extract() { if (Error E = Hdr.extract(AS, &Offset)) return E; + const unsigned SectionOffsetSize = dwarf::getDwarfOffsetByteSize(Hdr.Format); CUsBase = Offset; - Offset += Hdr.CompUnitCount * 4; - Offset += Hdr.LocalTypeUnitCount * 4; + Offset += Hdr.CompUnitCount * SectionOffsetSize; + Offset += Hdr.LocalTypeUnitCount * SectionOffsetSize; Offset += Hdr.ForeignTypeUnitCount * 8; BucketsBase = Offset; Offset += Hdr.BucketCount * 4; @@ -496,9 +503,9 @@ Error DWARFDebugNames::NameIndex::extract() { if (Hdr.BucketCount > 0) Offset += Hdr.NameCount * 4; StringOffsetsBase = Offset; - Offset += Hdr.NameCount * 4; + Offset += Hdr.NameCount * SectionOffsetSize; EntryOffsetsBase = Offset; - Offset += Hdr.NameCount * 4; + Offset += Hdr.NameCount * SectionOffsetSize; if (!AS.isValidOffsetForDataOfSize(Offset, Hdr.AbbrevTableSize)) return createStringError(errc::illegal_byte_sequence, @@ -579,20 +586,24 @@ std::error_code DWARFDebugNames::SentinelError::convertToErrorCode() const { uint64_t DWARFDebugNames::NameIndex::getCUOffset(uint32_t CU) const { assert(CU < Hdr.CompUnitCount); - uint64_t Offset = CUsBase + 4 * CU; - return Section.AccelSection.getRelocatedValue(4, &Offset); + const unsigned SectionOffsetSize = dwarf::getDwarfOffsetByteSize(Hdr.Format); + uint64_t Offset = CUsBase + SectionOffsetSize * CU; + return Section.AccelSection.getRelocatedValue(SectionOffsetSize, &Offset); } uint64_t DWARFDebugNames::NameIndex::getLocalTUOffset(uint32_t TU) const { assert(TU < Hdr.LocalTypeUnitCount); - uint64_t Offset = CUsBase + 4 * (Hdr.CompUnitCount + TU); - return Section.AccelSection.getRelocatedValue(4, &Offset); + const unsigned SectionOffsetSize = dwarf::getDwarfOffsetByteSize(Hdr.Format); + uint64_t Offset = CUsBase + SectionOffsetSize * (Hdr.CompUnitCount + TU); + return Section.AccelSection.getRelocatedValue(SectionOffsetSize, &Offset); } uint64_t DWARFDebugNames::NameIndex::getForeignTUSignature(uint32_t TU) const { assert(TU < Hdr.ForeignTypeUnitCount); + const unsigned SectionOffsetSize = dwarf::getDwarfOffsetByteSize(Hdr.Format); uint64_t Offset = - CUsBase + 4 * (Hdr.CompUnitCount + Hdr.LocalTypeUnitCount) + 8 * TU; + CUsBase + + SectionOffsetSize * (Hdr.CompUnitCount + Hdr.LocalTypeUnitCount) + 8 * TU; return Section.AccelSection.getU64(&Offset); } @@ -613,7 +624,7 @@ DWARFDebugNames::NameIndex::getEntry(uint64_t *Offset) const { Entry E(*this, *AbbrevIt); - dwarf::FormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; + dwarf::FormParams FormParams = {Hdr.Version, 0, Hdr.Format}; for (auto &Value : E.Values) { if (!Value.extractValue(AS, Offset, FormParams)) return createStringError(errc::io_error, @@ -625,12 +636,16 @@ DWARFDebugNames::NameIndex::getEntry(uint64_t *Offset) const { DWARFDebugNames::NameTableEntry DWARFDebugNames::NameIndex::getNameTableEntry(uint32_t Index) const { assert(0 < Index && Index <= Hdr.NameCount); - uint64_t StringOffsetOffset = StringOffsetsBase + 4 * (Index - 1); - uint64_t EntryOffsetOffset = EntryOffsetsBase + 4 * (Index - 1); + const unsigned SectionOffsetSize = dwarf::getDwarfOffsetByteSize(Hdr.Format); + uint64_t StringOffsetOffset = + StringOffsetsBase + SectionOffsetSize * (Index - 1); + uint64_t EntryOffsetOffset = + EntryOffsetsBase + SectionOffsetSize * (Index - 1); const DWARFDataExtractor &AS = Section.AccelSection; - uint64_t StringOffset = AS.getRelocatedValue(4, &StringOffsetOffset); - uint64_t EntryOffset = AS.getU32(&EntryOffsetOffset); + uint64_t StringOffset = + AS.getRelocatedValue(SectionOffsetSize, &StringOffsetOffset); + uint64_t EntryOffset = AS.getUnsigned(&EntryOffsetOffset, SectionOffsetSize); EntryOffset += EntriesBase; return {Section.StringSection, Index, StringOffset, EntryOffset}; } @@ -859,13 +874,14 @@ void DWARFDebugNames::ValueIterator::next() { DWARFDebugNames::ValueIterator::ValueIterator(const DWARFDebugNames &AccelTable, StringRef Key) - : CurrentIndex(AccelTable.NameIndices.begin()), IsLocal(false), Key(Key) { + : CurrentIndex(AccelTable.NameIndices.begin()), IsLocal(false), + Key(std::string(Key)) { searchFromStartOfCurrentIndex(); } DWARFDebugNames::ValueIterator::ValueIterator( const DWARFDebugNames::NameIndex &NI, StringRef Key) - : CurrentIndex(&NI), IsLocal(true), Key(Key) { + : CurrentIndex(&NI), IsLocal(true), Key(std::string(Key)) { if (!findInCurrentIndex()) setEnd(); } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp index f59e49268288c..9bd134105c9b3 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFCompileUnit.cpp @@ -15,16 +15,18 @@ using namespace llvm; void DWARFCompileUnit::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { + int OffsetDumpWidth = 2 * dwarf::getDwarfOffsetByteSize(getFormat()); OS << format("0x%08" PRIx64, getOffset()) << ": Compile Unit:" - << " length = " << format("0x%08" PRIx64, getLength()) - << " version = " << format("0x%04x", getVersion()); + << " length = " << format("0x%0*" PRIx64, OffsetDumpWidth, getLength()) + << ", format = " << dwarf::FormatString(getFormat()) + << ", version = " << format("0x%04x", getVersion()); if (getVersion() >= 5) - OS << " unit_type = " << dwarf::UnitTypeString(getUnitType()); - OS << " abbr_offset = " + OS << ", unit_type = " << dwarf::UnitTypeString(getUnitType()); + OS << ", abbr_offset = " << format("0x%04" PRIx64, getAbbreviations()->getOffset()) - << " addr_size = " << format("0x%02x", getAddressByteSize()); + << ", addr_size = " << format("0x%02x", getAddressByteSize()); if (getVersion() >= 5 && getUnitType() != dwarf::DW_UT_compile) - OS << " DWO_id = " << format("0x%016" PRIx64, *getDWOId()); + OS << ", DWO_id = " << format("0x%016" PRIx64, *getDWOId()); OS << " (next unit at " << format("0x%08" PRIx64, getNextUnitOffset()) << ")\n"; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp index aaa6d5250f23d..bf62194977706 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -45,7 +45,6 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" -#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cstdint> @@ -66,8 +65,12 @@ using FileLineInfoKind = DILineInfoSpecifier::FileLineInfoKind; using FunctionNameKind = DILineInfoSpecifier::FunctionNameKind; DWARFContext::DWARFContext(std::unique_ptr<const DWARFObject> DObj, - std::string DWPName) - : DIContext(CK_DWARF), DWPName(std::move(DWPName)), DObj(std::move(DObj)) {} + std::string DWPName, + std::function<void(Error)> RecoverableErrorHandler, + std::function<void(Error)> WarningHandler) + : DIContext(CK_DWARF), DWPName(std::move(DWPName)), + RecoverableErrorHandler(RecoverableErrorHandler), + WarningHandler(WarningHandler), DObj(std::move(DObj)) {} DWARFContext::~DWARFContext() = default; @@ -130,10 +133,21 @@ collectContributionData(DWARFContext::unit_iterator_range Units) { return Contributions; } -static void dumpDWARFv5StringOffsetsSection( - raw_ostream &OS, StringRef SectionName, const DWARFObject &Obj, - const DWARFSection &StringOffsetsSection, StringRef StringSection, - DWARFContext::unit_iterator_range Units, bool LittleEndian) { +// Dump a DWARF string offsets section. This may be a DWARF v5 formatted +// string offsets section, where each compile or type unit contributes a +// number of entries (string offsets), with each contribution preceded by +// a header containing size and version number. Alternatively, it may be a +// monolithic series of string offsets, as generated by the pre-DWARF v5 +// implementation of split DWARF; however, in that case we still need to +// collect contributions of units because the size of the offsets (4 or 8 +// bytes) depends on the format of the referencing unit (DWARF32 or DWARF64). +static void dumpStringOffsetsSection(raw_ostream &OS, DIDumpOptions DumpOpts, + StringRef SectionName, + const DWARFObject &Obj, + const DWARFSection &StringOffsetsSection, + StringRef StringSection, + DWARFContext::unit_iterator_range Units, + bool LittleEndian) { auto Contributions = collectContributionData(Units); DWARFDataExtractor StrOffsetExt(Obj, StringOffsetsSection, LittleEndian, 0); DataExtractor StrData(StringSection, LittleEndian, 0); @@ -148,6 +162,7 @@ static void dumpDWARFv5StringOffsetsSection( } dwarf::DwarfFormat Format = Contribution->getFormat(); + int OffsetDumpWidth = 2 * dwarf::getDwarfOffsetByteSize(Format); uint16_t Version = Contribution->getVersion(); uint64_t ContributionHeader = Contribution->Base; // In DWARF v5 there is a contribution header that immediately precedes @@ -159,10 +174,10 @@ static void dumpDWARFv5StringOffsetsSection( // Detect overlapping contributions. if (Offset > ContributionHeader) { - WithColor::error() - << "overlapping contributions to string offsets table in section ." - << SectionName << ".\n"; - return; + DumpOpts.RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "overlapping contributions to string offsets table in section .%s.", + SectionName.data())); } // Report a gap in the table. if (Offset < ContributionHeader) { @@ -175,7 +190,7 @@ static void dumpDWARFv5StringOffsetsSection( // version field and the padding, a total of 4 bytes). Add them back in // for reporting. OS << "Contribution size = " << (Contribution->Size + (Version < 5 ? 0 : 4)) - << ", Format = " << (Format == DWARF32 ? "DWARF32" : "DWARF64") + << ", Format = " << dwarf::FormatString(Format) << ", Version = " << Version << "\n"; Offset = Contribution->Base; @@ -184,7 +199,7 @@ static void dumpDWARFv5StringOffsetsSection( OS << format("0x%8.8" PRIx64 ": ", Offset); uint64_t StringOffset = StrOffsetExt.getRelocatedValue(EntrySize, &Offset); - OS << format("%8.8" PRIx64 " ", StringOffset); + OS << format("%0*" PRIx64 " ", OffsetDumpWidth, StringOffset); const char *S = StrData.getCStr(&StringOffset); if (S) OS << format("\"%s\"", S); @@ -198,47 +213,6 @@ static void dumpDWARFv5StringOffsetsSection( } } -// Dump a DWARF string offsets section. This may be a DWARF v5 formatted -// string offsets section, where each compile or type unit contributes a -// number of entries (string offsets), with each contribution preceded by -// a header containing size and version number. Alternatively, it may be a -// monolithic series of string offsets, as generated by the pre-DWARF v5 -// implementation of split DWARF. -static void dumpStringOffsetsSection(raw_ostream &OS, StringRef SectionName, - const DWARFObject &Obj, - const DWARFSection &StringOffsetsSection, - StringRef StringSection, - DWARFContext::unit_iterator_range Units, - bool LittleEndian, unsigned MaxVersion) { - // If we have at least one (compile or type) unit with DWARF v5 or greater, - // we assume that the section is formatted like a DWARF v5 string offsets - // section. - if (MaxVersion >= 5) - dumpDWARFv5StringOffsetsSection(OS, SectionName, Obj, StringOffsetsSection, - StringSection, Units, LittleEndian); - else { - DataExtractor strOffsetExt(StringOffsetsSection.Data, LittleEndian, 0); - uint64_t offset = 0; - uint64_t size = StringOffsetsSection.Data.size(); - // Ensure that size is a multiple of the size of an entry. - if (size & ((uint64_t)(sizeof(uint32_t) - 1))) { - OS << "error: size of ." << SectionName << " is not a multiple of " - << sizeof(uint32_t) << ".\n"; - size &= -(uint64_t)sizeof(uint32_t); - } - DataExtractor StrData(StringSection, LittleEndian, 0); - while (offset < size) { - OS << format("0x%8.8" PRIx64 ": ", offset); - uint64_t StringOffset = strOffsetExt.getU32(&offset); - OS << format("%8.8" PRIx64 " ", StringOffset); - const char *S = StrData.getCStr(&StringOffset); - if (S) - OS << format("\"%s\"", S); - OS << "\n"; - } - } -} - // Dump the .debug_addr section. static void dumpAddrSection(raw_ostream &OS, DWARFDataExtractor &AddrData, DIDumpOptions DumpOpts, uint16_t Version, @@ -248,16 +222,17 @@ static void dumpAddrSection(raw_ostream &OS, DWARFDataExtractor &AddrData, DWARFDebugAddrTable AddrTable; uint64_t TableOffset = Offset; if (Error Err = AddrTable.extract(AddrData, &Offset, Version, AddrSize, - DWARFContext::dumpWarning)) { - WithColor::error() << toString(std::move(Err)) << '\n'; + DumpOpts.WarningHandler)) { + DumpOpts.RecoverableErrorHandler(std::move(Err)); // Keep going after an error, if we can, assuming that the length field // could be read. If it couldn't, stop reading the section. - if (!AddrTable.hasValidLength()) - break; - Offset = TableOffset + AddrTable.getLength(); - } else { - AddrTable.dump(OS, DumpOpts); + if (auto TableLength = AddrTable.getFullLength()) { + Offset = TableOffset + *TableLength; + continue; + } + break; } + AddrTable.dump(OS, DumpOpts); } } @@ -272,7 +247,7 @@ static void dumpRnglistsSection( llvm::DWARFDebugRnglistTable Rnglists; uint64_t TableOffset = Offset; if (Error Err = Rnglists.extract(rnglistData, &Offset)) { - WithColor::error() << toString(std::move(Err)) << '\n'; + DumpOpts.RecoverableErrorHandler(std::move(Err)); uint64_t Length = Rnglists.length(); // Keep going after an error, if we can, assuming that the length field // could be read. If it couldn't, stop reading the section. @@ -285,6 +260,48 @@ static void dumpRnglistsSection( } } +std::unique_ptr<DWARFDebugMacro> +DWARFContext::parseMacroOrMacinfo(MacroSecType SectionType) { + auto Macro = std::make_unique<DWARFDebugMacro>(); + auto ParseAndDump = [&](DWARFDataExtractor &Data, bool IsMacro) { + if (Error Err = IsMacro ? Macro->parseMacro(SectionType == MacroSection + ? compile_units() + : dwo_compile_units(), + SectionType == MacroSection + ? getStringExtractor() + : getStringDWOExtractor(), + Data) + : Macro->parseMacinfo(Data)) { + RecoverableErrorHandler(std::move(Err)); + Macro = nullptr; + } + }; + switch (SectionType) { + case MacinfoSection: { + DWARFDataExtractor Data(DObj->getMacinfoSection(), isLittleEndian(), 0); + ParseAndDump(Data, /*IsMacro=*/false); + break; + } + case MacinfoDwoSection: { + DWARFDataExtractor Data(DObj->getMacinfoDWOSection(), isLittleEndian(), 0); + ParseAndDump(Data, /*IsMacro=*/false); + break; + } + case MacroSection: { + DWARFDataExtractor Data(*DObj, DObj->getMacroSection(), isLittleEndian(), + 0); + ParseAndDump(Data, /*IsMacro=*/true); + break; + } + case MacroDwoSection: { + DWARFDataExtractor Data(DObj->getMacroDWOSection(), isLittleEndian(), 0); + ParseAndDump(Data, /*IsMacro=*/true); + break; + } + } + return Macro; +} + static void dumpLoclistsSection(raw_ostream &OS, DIDumpOptions DumpOpts, DWARFDataExtractor Data, const MCRegisterInfo *MRI, @@ -295,7 +312,7 @@ static void dumpLoclistsSection(raw_ostream &OS, DIDumpOptions DumpOpts, while (Data.isValidOffset(Offset)) { DWARFListTableHeader Header(".debug_loclists", "locations"); if (Error E = Header.extract(Data, &Offset)) { - WithColor::error() << toString(std::move(E)) << '\n'; + DumpOpts.RecoverableErrorHandler(std::move(E)); return; } @@ -319,10 +336,16 @@ static void dumpLoclistsSection(raw_ostream &OS, DIDumpOptions DumpOpts, } } +static void dumpPubTableSection(raw_ostream &OS, DIDumpOptions DumpOpts, + DWARFDataExtractor Data, bool GnuStyle) { + DWARFDebugPubTable Table; + Table.extract(Data, GnuStyle, DumpOpts.RecoverableErrorHandler); + Table.dump(OS); +} + void DWARFContext::dump( raw_ostream &OS, DIDumpOptions DumpOpts, std::array<Optional<uint64_t>, DIDT_ID_Count> DumpOffsets) { - uint64_t DumpType = DumpOpts.DumpType; StringRef Extension = sys::path::extension(DObj->getFileName()); @@ -430,31 +453,61 @@ void DWARFContext::dump( } } - if (const auto *Off = shouldDump(Explicit, ".debug_frame", DIDT_ID_DebugFrame, - DObj->getFrameSection().Data)) - getDebugFrame()->dump(OS, getRegisterInfo(), *Off); + if (const Optional<uint64_t> *Off = + shouldDump(Explicit, ".debug_frame", DIDT_ID_DebugFrame, + DObj->getFrameSection().Data)) { + if (Expected<const DWARFDebugFrame *> DF = getDebugFrame()) + (*DF)->dump(OS, getRegisterInfo(), *Off); + else + RecoverableErrorHandler(DF.takeError()); + } - if (const auto *Off = shouldDump(Explicit, ".eh_frame", DIDT_ID_DebugFrame, - DObj->getEHFrameSection().Data)) - getEHFrame()->dump(OS, getRegisterInfo(), *Off); + if (const Optional<uint64_t> *Off = + shouldDump(Explicit, ".eh_frame", DIDT_ID_DebugFrame, + DObj->getEHFrameSection().Data)) { + if (Expected<const DWARFDebugFrame *> DF = getEHFrame()) + (*DF)->dump(OS, getRegisterInfo(), *Off); + else + RecoverableErrorHandler(DF.takeError()); + } - if (DumpType & DIDT_DebugMacro) { - 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); - } + if (shouldDump(Explicit, ".debug_macro", DIDT_ID_DebugMacro, + DObj->getMacroSection().Data)) { + if (auto Macro = getDebugMacro()) + Macro->dump(OS); + } + + if (shouldDump(Explicit, ".debug_macro.dwo", DIDT_ID_DebugMacro, + DObj->getMacroDWOSection())) { + if (auto MacroDWO = getDebugMacroDWO()) + MacroDWO->dump(OS); + } + + if (shouldDump(Explicit, ".debug_macinfo", DIDT_ID_DebugMacro, + DObj->getMacinfoSection())) { + if (auto Macinfo = getDebugMacinfo()) + Macinfo->dump(OS); + } + + if (shouldDump(Explicit, ".debug_macinfo.dwo", DIDT_ID_DebugMacro, + DObj->getMacinfoDWOSection())) { + if (auto MacinfoDWO = getDebugMacinfoDWO()) + MacinfoDWO->dump(OS); } if (shouldDump(Explicit, ".debug_aranges", DIDT_ID_DebugAranges, DObj->getArangesSection())) { uint64_t offset = 0; - DataExtractor arangesData(DObj->getArangesSection(), isLittleEndian(), 0); + DWARFDataExtractor arangesData(DObj->getArangesSection(), isLittleEndian(), + 0); DWARFDebugArangeSet set; - while (set.extract(arangesData, &offset)) + while (arangesData.isValidOffset(offset)) { + if (Error E = set.extract(arangesData, &offset)) { + RecoverableErrorHandler(std::move(E)); + break; + } set.dump(OS); + } } auto DumpLineSection = [&](DWARFDebugLine::SectionParser Parser, @@ -462,18 +515,13 @@ void DWARFContext::dump( Optional<uint64_t> DumpOffset) { while (!Parser.done()) { if (DumpOffset && Parser.getOffset() != *DumpOffset) { - Parser.skip(dumpWarning); + Parser.skip(DumpOpts.WarningHandler, DumpOpts.WarningHandler); continue; } OS << "debug_line[" << format("0x%8.8" PRIx64, Parser.getOffset()) << "]\n"; - if (DumpOpts.Verbose) { - Parser.parseNext(dumpWarning, dumpWarning, &OS); - } else { - DWARFDebugLine::LineTable LineTable = - Parser.parseNext(dumpWarning, dumpWarning); - LineTable.dump(OS, DumpOpts); - } + Parser.parseNext(DumpOpts.WarningHandler, DumpOpts.WarningHandler, &OS, + DumpOpts.Verbose); } }; @@ -555,7 +603,7 @@ void DWARFContext::dump( DWARFDebugRangeList rangeList; while (rangesData.isValidOffset(offset)) { if (Error E = rangeList.extract(rangesData, &offset)) { - WithColor::error() << toString(std::move(E)) << '\n'; + DumpOpts.RecoverableErrorHandler(std::move(E)); break; } rangeList.dump(OS); @@ -585,39 +633,44 @@ void DWARFContext::dump( } if (shouldDump(Explicit, ".debug_pubnames", DIDT_ID_DebugPubnames, - DObj->getPubnamesSection().Data)) - DWARFDebugPubTable(*DObj, DObj->getPubnamesSection(), isLittleEndian(), false) - .dump(OS); + DObj->getPubnamesSection().Data)) { + DWARFDataExtractor PubTableData(*DObj, DObj->getPubnamesSection(), + isLittleEndian(), 0); + dumpPubTableSection(OS, DumpOpts, PubTableData, /*GnuStyle=*/false); + } if (shouldDump(Explicit, ".debug_pubtypes", DIDT_ID_DebugPubtypes, - DObj->getPubtypesSection().Data)) - DWARFDebugPubTable(*DObj, DObj->getPubtypesSection(), isLittleEndian(), false) - .dump(OS); + DObj->getPubtypesSection().Data)) { + DWARFDataExtractor PubTableData(*DObj, DObj->getPubtypesSection(), + isLittleEndian(), 0); + dumpPubTableSection(OS, DumpOpts, PubTableData, /*GnuStyle=*/false); + } if (shouldDump(Explicit, ".debug_gnu_pubnames", DIDT_ID_DebugGnuPubnames, - DObj->getGnuPubnamesSection().Data)) - DWARFDebugPubTable(*DObj, DObj->getGnuPubnamesSection(), isLittleEndian(), - true /* GnuStyle */) - .dump(OS); + DObj->getGnuPubnamesSection().Data)) { + DWARFDataExtractor PubTableData(*DObj, DObj->getGnuPubnamesSection(), + isLittleEndian(), 0); + dumpPubTableSection(OS, DumpOpts, PubTableData, /*GnuStyle=*/true); + } if (shouldDump(Explicit, ".debug_gnu_pubtypes", DIDT_ID_DebugGnuPubtypes, - DObj->getGnuPubtypesSection().Data)) - DWARFDebugPubTable(*DObj, DObj->getGnuPubtypesSection(), isLittleEndian(), - true /* GnuStyle */) - .dump(OS); + DObj->getGnuPubtypesSection().Data)) { + DWARFDataExtractor PubTableData(*DObj, DObj->getGnuPubtypesSection(), + isLittleEndian(), 0); + dumpPubTableSection(OS, DumpOpts, PubTableData, /*GnuStyle=*/true); + } if (shouldDump(Explicit, ".debug_str_offsets", DIDT_ID_DebugStrOffsets, DObj->getStrOffsetsSection().Data)) - dumpStringOffsetsSection(OS, "debug_str_offsets", *DObj, - DObj->getStrOffsetsSection(), - DObj->getStrSection(), normal_units(), - isLittleEndian(), getMaxVersion()); + dumpStringOffsetsSection( + OS, DumpOpts, "debug_str_offsets", *DObj, DObj->getStrOffsetsSection(), + DObj->getStrSection(), normal_units(), isLittleEndian()); if (shouldDump(ExplicitDWO, ".debug_str_offsets.dwo", DIDT_ID_DebugStrOffsets, DObj->getStrOffsetsDWOSection().Data)) - dumpStringOffsetsSection(OS, "debug_str_offsets.dwo", *DObj, + dumpStringOffsetsSection(OS, DumpOpts, "debug_str_offsets.dwo", *DObj, DObj->getStrOffsetsDWOSection(), DObj->getStrDWOSection(), dwo_units(), - isLittleEndian(), getMaxDWOVersion()); + isLittleEndian()); if (shouldDump(Explicit, ".gdb_index", DIDT_ID_GdbIndex, DObj->getGdbIndexSection())) { @@ -711,7 +764,7 @@ const DWARFUnitIndex &DWARFContext::getTUIndex() { DataExtractor TUIndexData(DObj->getTUIndexSection(), isLittleEndian(), 0); - TUIndex = std::make_unique<DWARFUnitIndex>(DW_SECT_TYPES); + TUIndex = std::make_unique<DWARFUnitIndex>(DW_SECT_EXT_TYPES); TUIndex->parse(TUIndexData); return *TUIndex; } @@ -770,7 +823,7 @@ const DWARFDebugAranges *DWARFContext::getDebugAranges() { return Aranges.get(); } -const DWARFDebugFrame *DWARFContext::getDebugFrame() { +Expected<const DWARFDebugFrame *> DWARFContext::getDebugFrame() { if (DebugFrame) return DebugFrame.get(); @@ -785,41 +838,50 @@ const DWARFDebugFrame *DWARFContext::getDebugFrame() { // http://lists.dwarfstd.org/htdig.cgi/dwarf-discuss-dwarfstd.org/2011-December/001173.html DWARFDataExtractor debugFrameData(*DObj, DObj->getFrameSection(), isLittleEndian(), DObj->getAddressSize()); - DebugFrame.reset(new DWARFDebugFrame(getArch(), false /* IsEH */)); - DebugFrame->parse(debugFrameData); + auto DF = std::make_unique<DWARFDebugFrame>(getArch(), /*IsEH=*/false); + if (Error E = DF->parse(debugFrameData)) + return std::move(E); + + DebugFrame.swap(DF); return DebugFrame.get(); } -const DWARFDebugFrame *DWARFContext::getEHFrame() { +Expected<const DWARFDebugFrame *> DWARFContext::getEHFrame() { if (EHFrame) return EHFrame.get(); DWARFDataExtractor debugFrameData(*DObj, DObj->getEHFrameSection(), isLittleEndian(), DObj->getAddressSize()); - DebugFrame.reset(new DWARFDebugFrame(getArch(), true /* IsEH */)); - DebugFrame->parse(debugFrameData); + + auto DF = std::make_unique<DWARFDebugFrame>(getArch(), /*IsEH=*/true); + if (Error E = DF->parse(debugFrameData)) + return std::move(E); + DebugFrame.swap(DF); return DebugFrame.get(); } -const DWARFDebugMacro *DWARFContext::getDebugMacroDWO() { - if (MacroDWO) - return MacroDWO.get(); +const DWARFDebugMacro *DWARFContext::getDebugMacro() { + if (!Macro) + Macro = parseMacroOrMacinfo(MacroSection); + return Macro.get(); +} - DataExtractor MacinfoDWOData(DObj->getMacinfoDWOSection(), isLittleEndian(), - 0); - MacroDWO.reset(new DWARFDebugMacro()); - MacroDWO->parse(MacinfoDWOData); +const DWARFDebugMacro *DWARFContext::getDebugMacroDWO() { + if (!MacroDWO) + MacroDWO = parseMacroOrMacinfo(MacroDwoSection); return MacroDWO.get(); } -const DWARFDebugMacro *DWARFContext::getDebugMacro() { - if (Macro) - return Macro.get(); +const DWARFDebugMacro *DWARFContext::getDebugMacinfo() { + if (!Macinfo) + Macinfo = parseMacroOrMacinfo(MacinfoSection); + return Macinfo.get(); +} - DataExtractor MacinfoData(DObj->getMacinfoSection(), isLittleEndian(), 0); - Macro.reset(new DWARFDebugMacro()); - Macro->parse(MacinfoData); - return Macro.get(); +const DWARFDebugMacro *DWARFContext::getDebugMacinfoDWO() { + if (!MacinfoDWO) + MacinfoDWO = parseMacroOrMacinfo(MacinfoDwoSection); + return MacinfoDWO.get(); } template <typename T> @@ -865,16 +927,16 @@ const AppleAcceleratorTable &DWARFContext::getAppleObjC() { const DWARFDebugLine::LineTable * DWARFContext::getLineTableForUnit(DWARFUnit *U) { Expected<const DWARFDebugLine::LineTable *> ExpectedLineTable = - getLineTableForUnit(U, dumpWarning); + getLineTableForUnit(U, WarningHandler); if (!ExpectedLineTable) { - dumpWarning(ExpectedLineTable.takeError()); + WarningHandler(ExpectedLineTable.takeError()); return nullptr; } return *ExpectedLineTable; } Expected<const DWARFDebugLine::LineTable *> DWARFContext::getLineTableForUnit( - DWARFUnit *U, function_ref<void(Error)> RecoverableErrorCallback) { + DWARFUnit *U, function_ref<void(Error)> RecoverableErrorHandler) { if (!Line) Line.reset(new DWARFDebugLine); @@ -899,7 +961,7 @@ Expected<const DWARFDebugLine::LineTable *> DWARFContext::getLineTableForUnit( DWARFDataExtractor lineData(*DObj, U->getLineSection(), isLittleEndian(), U->getAddressByteSize()); return Line->getOrParseLineTable(lineData, stmtOffset, *this, U, - RecoverableErrorCallback); + RecoverableErrorHandler); } void DWARFContext::parseNormalUnits() { @@ -910,7 +972,7 @@ void DWARFContext::parseNormalUnits() { }); NormalUnits.finishedInfoUnits(); DObj->forEachTypesSections([&](const DWARFSection &S) { - NormalUnits.addUnitsForSection(*this, S, DW_SECT_TYPES); + NormalUnits.addUnitsForSection(*this, S, DW_SECT_EXT_TYPES); }); } @@ -922,7 +984,7 @@ void DWARFContext::parseDWOUnits(bool Lazy) { }); DWOUnits.finishedInfoUnits(); DObj->forEachTypesDWOSections([&](const DWARFSection &S) { - DWOUnits.addUnitsForDWOSection(*this, S, DW_SECT_TYPES, Lazy); + DWOUnits.addUnitsForDWOSection(*this, S, DW_SECT_EXT_TYPES, Lazy); }); } @@ -1418,11 +1480,6 @@ static bool isRelocScattered(const object::ObjectFile &Obj, return MachObj->isRelocationScattered(RelocInfo); } -ErrorPolicy DWARFContext::defaultErrorHandler(Error E) { - WithColor::error() << toString(std::move(E)) << '\n'; - return ErrorPolicy::Continue; -} - namespace { struct DWARFSectionMap final : public DWARFSection { RelocAddrMap Relocs; @@ -1467,6 +1524,7 @@ class DWARFObjInMemory final : public DWARFObject { DWARFSectionMap PubtypesSection; DWARFSectionMap GnuPubnamesSection; DWARFSectionMap GnuPubtypesSection; + DWARFSectionMap MacroSection; DWARFSectionMap *mapNameToDWARFSection(StringRef Name) { return StringSwitch<DWARFSectionMap *>(Name) @@ -1494,6 +1552,7 @@ class DWARFObjInMemory final : public DWARFObject { .Case("apple_namespaces", &AppleNamespacesSection) .Case("apple_namespac", &AppleNamespacesSection) .Case("apple_objc", &AppleObjCSection) + .Case("debug_macro", &MacroSection) .Default(nullptr); } @@ -1502,6 +1561,7 @@ class DWARFObjInMemory final : public DWARFObject { StringRef StrSection; StringRef MacinfoSection; StringRef MacinfoDWOSection; + StringRef MacroDWOSection; StringRef AbbrevDWOSection; StringRef StrDWOSection; StringRef CUIndexSection; @@ -1522,6 +1582,7 @@ class DWARFObjInMemory final : public DWARFObject { .Case("debug_str", &StrSection) .Case("debug_macinfo", &MacinfoSection) .Case("debug_macinfo.dwo", &MacinfoDWOSection) + .Case("debug_macro.dwo", &MacroDWOSection) .Case("debug_abbrev.dwo", &AbbrevDWOSection) .Case("debug_str.dwo", &StrDWOSection) .Case("debug_cu_index", &CUIndexSection) @@ -1574,7 +1635,7 @@ public: } } DWARFObjInMemory(const object::ObjectFile &Obj, const LoadedObjectInfo *L, - function_ref<ErrorPolicy(Error)> HandleError) + function_ref<void(Error)> HandleError, function_ref<void(Error)> HandleWarning ) : IsLittleEndian(Obj.isLittleEndian()), AddressSize(Obj.getBytesInAddress()), FileName(Obj.getFileName()), Obj(&Obj) { @@ -1601,10 +1662,8 @@ public: StringRef Data; Expected<section_iterator> SecOrErr = Section.getRelocatedSection(); if (!SecOrErr) { - ErrorPolicy EP = HandleError(createError( - "failed to get relocated section: ", SecOrErr.takeError())); - if (EP == ErrorPolicy::Halt) - return; + HandleError(createError("failed to get relocated section: ", + SecOrErr.takeError())); continue; } @@ -1622,10 +1681,8 @@ public: } if (auto Err = maybeDecompress(Section, Name, Data)) { - ErrorPolicy EP = HandleError(createError( - "failed to decompress '" + Name + "', ", std::move(Err))); - if (EP == ErrorPolicy::Halt) - return; + HandleError(createError("failed to decompress '" + Name + "', ", + std::move(Err))); continue; } @@ -1726,8 +1783,7 @@ public: Expected<SymInfo> SymInfoOrErr = getSymbolInfo(Obj, Reloc, L, AddrCache); if (!SymInfoOrErr) { - if (HandleError(SymInfoOrErr.takeError()) == ErrorPolicy::Halt) - return; + HandleError(SymInfoOrErr.takeError()); continue; } @@ -1747,10 +1803,8 @@ public: if (!I.second) { RelocAddrEntry &entry = I.first->getSecond(); if (entry.Reloc2) { - ErrorPolicy EP = HandleError(createError( + HandleError(createError( "At most two relocations per offset are supported")); - if (EP == ErrorPolicy::Halt) - return; } entry.Reloc2 = Reloc; entry.SymbolValue2 = SymInfoOrErr->Address; @@ -1758,11 +1812,10 @@ public: } else { SmallString<32> Type; Reloc.getTypeName(Type); - ErrorPolicy EP = HandleError( + // FIXME: Support more relocations & change this to an error + HandleWarning( createError("failed to compute relocation: " + Type + ", ", errorCodeToError(object_error::parse_failed))); - if (EP == ErrorPolicy::Halt) - return; } } } @@ -1847,6 +1900,8 @@ public: const DWARFSection &getRnglistsSection() const override { return RnglistsSection; } + const DWARFSection &getMacroSection() const override { return MacroSection; } + StringRef getMacroDWOSection() const override { return MacroDWOSection; } StringRef getMacinfoSection() const override { return MacinfoSection; } StringRef getMacinfoDWOSection() const override { return MacinfoDWOSection; } const DWARFSection &getPubnamesSection() const override { return PubnamesSection; } @@ -1890,18 +1945,25 @@ public: std::unique_ptr<DWARFContext> DWARFContext::create(const object::ObjectFile &Obj, const LoadedObjectInfo *L, - function_ref<ErrorPolicy(Error)> HandleError, - std::string DWPName) { - auto DObj = std::make_unique<DWARFObjInMemory>(Obj, L, HandleError); - return std::make_unique<DWARFContext>(std::move(DObj), std::move(DWPName)); + std::string DWPName, + std::function<void(Error)> RecoverableErrorHandler, + std::function<void(Error)> WarningHandler) { + auto DObj = + std::make_unique<DWARFObjInMemory>(Obj, L, RecoverableErrorHandler, WarningHandler); + return std::make_unique<DWARFContext>(std::move(DObj), std::move(DWPName), + RecoverableErrorHandler, + WarningHandler); } std::unique_ptr<DWARFContext> DWARFContext::create(const StringMap<std::unique_ptr<MemoryBuffer>> &Sections, - uint8_t AddrSize, bool isLittleEndian) { + uint8_t AddrSize, bool isLittleEndian, + std::function<void(Error)> RecoverableErrorHandler, + std::function<void(Error)> WarningHandler) { auto DObj = std::make_unique<DWARFObjInMemory>(Sections, AddrSize, isLittleEndian); - return std::make_unique<DWARFContext>(std::move(DObj), ""); + return std::make_unique<DWARFContext>( + std::move(DObj), "", RecoverableErrorHandler, WarningHandler); } Error DWARFContext::loadRegisterInfo(const object::ObjectFile &Obj) { @@ -1924,19 +1986,9 @@ Error DWARFContext::loadRegisterInfo(const object::ObjectFile &Obj) { uint8_t DWARFContext::getCUAddrSize() { // In theory, different compile units may have different address byte // sizes, but for simplicity we just use the address byte size of the - // last compile unit. In practice the address size field is repeated across + // first compile unit. In practice the address size field is repeated across // various DWARF headers (at least in version 5) to make it easier to dump // them independently, not to enable varying the address size. - uint8_t Addr = 0; - for (const auto &CU : compile_units()) { - Addr = CU->getAddressByteSize(); - break; - } - return Addr; -} - -void DWARFContext::dumpWarning(Error Warning) { - handleAllErrors(std::move(Warning), [](ErrorInfoBase &Info) { - WithColor::warning() << Info.message() << '\n'; - }); + unit_iterator_range CUs = compile_units(); + return CUs.empty() ? 0 : (*CUs.begin())->getAddressByteSize(); } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp index 53e676bc70310..886fe1dff9769 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp @@ -7,11 +7,42 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" -#include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" using namespace llvm; +std::pair<uint64_t, dwarf::DwarfFormat> +DWARFDataExtractor::getInitialLength(uint64_t *Off, Error *Err) const { + ErrorAsOutParameter ErrAsOut(Err); + if (Err && *Err) + return {0, dwarf::DWARF32}; + + Cursor C(*Off); + uint64_t Length = getRelocatedValue(C, 4); + dwarf::DwarfFormat Format = dwarf::DWARF32; + if (Length == dwarf::DW_LENGTH_DWARF64) { + Length = getRelocatedValue(C, 8); + Format = dwarf::DWARF64; + } else if (Length >= dwarf::DW_LENGTH_lo_reserved) { + cantFail(C.takeError()); + if (Err) + *Err = createStringError( + errc::invalid_argument, + "unsupported reserved unit length of value 0x%8.8" PRIx64, Length); + return {0, dwarf::DWARF32}; + } + + if (C) { + *Off = C.tell(); + return {Length, Format}; + } + if (Err) + *Err = C.takeError(); + else + consumeError(C.takeError()); + return {0, dwarf::DWARF32}; +} + uint64_t DWARFDataExtractor::getRelocatedValue(uint32_t Size, uint64_t *Off, uint64_t *SecNdx, Error *Err) const { @@ -19,9 +50,11 @@ uint64_t DWARFDataExtractor::getRelocatedValue(uint32_t Size, uint64_t *Off, *SecNdx = object::SectionedAddress::UndefSection; if (!Section) return getUnsigned(Off, Size, Err); + + ErrorAsOutParameter ErrAsOut(Err); Optional<RelocAddrEntry> E = Obj->find(*Section, *Off); uint64_t A = getUnsigned(Off, Size, Err); - if (!E) + if (!E || (Err && *Err)) return A; if (SecNdx) *SecNdx = E->SectionIndex; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp index f71543799e286..dcf2aefeb39f6 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugAddr.cpp @@ -12,144 +12,144 @@ using namespace llvm; -void DWARFDebugAddrTable::clear() { - HeaderData = {}; +Error DWARFDebugAddrTable::extractAddresses(const DWARFDataExtractor &Data, + uint64_t *OffsetPtr, + uint64_t EndOffset) { + assert(EndOffset >= *OffsetPtr); + uint64_t DataSize = EndOffset - *OffsetPtr; + assert(Data.isValidOffsetForDataOfSize(*OffsetPtr, DataSize)); + if (AddrSize != 4 && AddrSize != 8) + return createStringError(errc::not_supported, + "address table at offset 0x%" PRIx64 + " has unsupported address size %" PRIu8 + " (4 and 8 are supported)", + Offset, AddrSize); + if (DataSize % AddrSize != 0) { + invalidateLength(); + return createStringError(errc::invalid_argument, + "address table at offset 0x%" PRIx64 + " contains data of size 0x%" PRIx64 + " which is not a multiple of addr size %" PRIu8, + Offset, DataSize, AddrSize); + } Addrs.clear(); - invalidateLength(); + size_t Count = DataSize / AddrSize; + Addrs.reserve(Count); + while (Count--) + Addrs.push_back(Data.getRelocatedValue(AddrSize, OffsetPtr)); + return Error::success(); } -Error DWARFDebugAddrTable::extract(DWARFDataExtractor Data, - uint64_t *OffsetPtr, - uint16_t Version, - uint8_t AddrSize, - std::function<void(Error)> WarnCallback) { - clear(); - HeaderOffset = *OffsetPtr; - // Read and verify the length field. - if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t))) +Error DWARFDebugAddrTable::extractV5(const DWARFDataExtractor &Data, + uint64_t *OffsetPtr, uint8_t CUAddrSize, + std::function<void(Error)> WarnCallback) { + Offset = *OffsetPtr; + llvm::Error Err = Error::success(); + std::tie(Length, Format) = Data.getInitialLength(OffsetPtr, &Err); + if (Err) { + invalidateLength(); return createStringError(errc::invalid_argument, - "section is not large enough to contain a " - ".debug_addr table length at offset 0x%" - PRIx64, *OffsetPtr); - uint16_t UnitVersion; - if (Version == 0) { - WarnCallback(createStringError(errc::invalid_argument, - "DWARF version is not defined in CU," - " assuming version 5")); - UnitVersion = 5; - } else { - UnitVersion = Version; + "parsing address table at offset 0x%" PRIx64 + ": %s", + Offset, toString(std::move(Err)).c_str()); + } + + if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, Length)) { + uint64_t DiagnosticLength = Length; + invalidateLength(); + return createStringError( + errc::invalid_argument, + "section is not large enough to contain an address table " + "at offset 0x%" PRIx64 " with a unit_length value of 0x%" PRIx64, + Offset, DiagnosticLength); } - // TODO: Add support for DWARF64. - Format = dwarf::DwarfFormat::DWARF32; - if (UnitVersion >= 5) { - HeaderData.Length = Data.getU32(OffsetPtr); - if (HeaderData.Length == dwarf::DW_LENGTH_DWARF64) { - invalidateLength(); - return createStringError(errc::not_supported, - "DWARF64 is not supported in .debug_addr at offset 0x%" PRIx64, - HeaderOffset); - } - if (HeaderData.Length + sizeof(uint32_t) < sizeof(Header)) { - uint32_t TmpLength = getLength(); - invalidateLength(); - return createStringError(errc::invalid_argument, - ".debug_addr table at offset 0x%" PRIx64 - " has too small length (0x%" PRIx32 - ") to contain a complete header", - HeaderOffset, TmpLength); - } - uint64_t End = HeaderOffset + getLength(); - if (!Data.isValidOffsetForDataOfSize(HeaderOffset, End - HeaderOffset)) { - uint32_t TmpLength = getLength(); - invalidateLength(); - return createStringError(errc::invalid_argument, - "section is not large enough to contain a .debug_addr table " - "of length 0x%" PRIx32 " at offset 0x%" PRIx64, - TmpLength, HeaderOffset); - } - - HeaderData.Version = Data.getU16(OffsetPtr); - HeaderData.AddrSize = Data.getU8(OffsetPtr); - HeaderData.SegSize = Data.getU8(OffsetPtr); - DataSize = getDataSize(); - } else { - HeaderData.Version = UnitVersion; - HeaderData.AddrSize = AddrSize; - // TODO: Support for non-zero SegSize. - HeaderData.SegSize = 0; - DataSize = Data.size(); + uint64_t EndOffset = *OffsetPtr + Length; + // Ensure that we can read the remaining header fields. + if (Length < 4) { + uint64_t DiagnosticLength = Length; + invalidateLength(); + return createStringError( + errc::invalid_argument, + "address table at offset 0x%" PRIx64 + " has a unit_length value of 0x%" PRIx64 + ", which is too small to contain a complete header", + Offset, DiagnosticLength); } - // Perform basic validation of the remaining header fields. + Version = Data.getU16(OffsetPtr); + AddrSize = Data.getU8(OffsetPtr); + SegSize = Data.getU8(OffsetPtr); - // We support DWARF version 5 for now as well as pre-DWARF5 - // implementations of .debug_addr table, which doesn't contain a header - // and consists only of a series of addresses. - if (HeaderData.Version > 5) { - return createStringError(errc::not_supported, "version %" PRIu16 - " of .debug_addr section at offset 0x%" PRIx64 " is not supported", - HeaderData.Version, HeaderOffset); - } - // FIXME: For now we just treat version mismatch as an error, - // however the correct way to associate a .debug_addr table - // with a .debug_info table is to look at the DW_AT_addr_base - // attribute in the info table. - if (HeaderData.Version != UnitVersion) - return createStringError(errc::invalid_argument, - ".debug_addr table at offset 0x%" PRIx64 - " has version %" PRIu16 - " which is different from the version suggested" - " by the DWARF unit header: %" PRIu16, - HeaderOffset, HeaderData.Version, UnitVersion); - if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8) + // Perform a basic validation of the header fields. + if (Version != 5) return createStringError(errc::not_supported, - ".debug_addr table at offset 0x%" PRIx64 - " has unsupported address size %" PRIu8, - HeaderOffset, HeaderData.AddrSize); - if (HeaderData.AddrSize != AddrSize && AddrSize != 0) - return createStringError(errc::invalid_argument, - ".debug_addr table at offset 0x%" PRIx64 - " has address size %" PRIu8 - " which is different from CU address size %" PRIu8, - HeaderOffset, HeaderData.AddrSize, AddrSize); - + "address table at offset 0x%" PRIx64 + " has unsupported version %" PRIu16, + Offset, Version); // TODO: add support for non-zero segment selector size. - if (HeaderData.SegSize != 0) + if (SegSize != 0) return createStringError(errc::not_supported, - ".debug_addr table at offset 0x%" PRIx64 - " has unsupported segment selector size %" PRIu8, - HeaderOffset, HeaderData.SegSize); - if (DataSize % HeaderData.AddrSize != 0) { - invalidateLength(); - return createStringError(errc::invalid_argument, - ".debug_addr table at offset 0x%" PRIx64 - " contains data of size %" PRIu32 - " which is not a multiple of addr size %" PRIu8, - HeaderOffset, DataSize, HeaderData.AddrSize); + "address table at offset 0x%" PRIx64 + " has unsupported segment selector size %" PRIu8, + Offset, SegSize); + + if (Error Err = extractAddresses(Data, OffsetPtr, EndOffset)) + return Err; + if (CUAddrSize && AddrSize != CUAddrSize) { + WarnCallback(createStringError( + errc::invalid_argument, + "address table at offset 0x%" PRIx64 " has address size %" PRIu8 + " which is different from CU address size %" PRIu8, + Offset, AddrSize, CUAddrSize)); } - Data.setAddressSize(HeaderData.AddrSize); - uint32_t AddrCount = DataSize / HeaderData.AddrSize; - for (uint32_t I = 0; I < AddrCount; ++I) - if (HeaderData.AddrSize == 4) - Addrs.push_back(Data.getU32(OffsetPtr)); - else - Addrs.push_back(Data.getU64(OffsetPtr)); return Error::success(); } +Error DWARFDebugAddrTable::extractPreStandard(const DWARFDataExtractor &Data, + uint64_t *OffsetPtr, + uint16_t CUVersion, + uint8_t CUAddrSize) { + assert(CUVersion > 0 && CUVersion < 5); + + Offset = *OffsetPtr; + Length = 0; + Version = CUVersion; + AddrSize = CUAddrSize; + SegSize = 0; + + return extractAddresses(Data, OffsetPtr, Data.size()); +} + +Error DWARFDebugAddrTable::extract(const DWARFDataExtractor &Data, + uint64_t *OffsetPtr, + uint16_t CUVersion, + uint8_t CUAddrSize, + std::function<void(Error)> WarnCallback) { + if (CUVersion > 0 && CUVersion < 5) + return extractPreStandard(Data, OffsetPtr, CUVersion, CUAddrSize); + if (CUVersion == 0) + WarnCallback(createStringError(errc::invalid_argument, + "DWARF version is not defined in CU," + " assuming version 5")); + return extractV5(Data, OffsetPtr, CUAddrSize, WarnCallback); +} + void DWARFDebugAddrTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { if (DumpOpts.Verbose) - OS << format("0x%8.8" PRIx32 ": ", HeaderOffset); - OS << format("Addr Section: length = 0x%8.8" PRIx32 - ", version = 0x%4.4" PRIx16 ", " - "addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8 "\n", - HeaderData.Length, HeaderData.Version, HeaderData.AddrSize, - HeaderData.SegSize); + OS << format("0x%8.8" PRIx64 ": ", Offset); + if (Length) { + int OffsetDumpWidth = 2 * dwarf::getDwarfOffsetByteSize(Format); + OS << "Address table header: " + << format("length = 0x%0*" PRIx64, OffsetDumpWidth, Length) + << ", format = " << dwarf::FormatString(Format) + << format(", version = 0x%4.4" PRIx16, Version) + << format(", addr_size = 0x%2.2" PRIx8, AddrSize) + << format(", seg_size = 0x%2.2" PRIx8, SegSize) << "\n"; + } if (Addrs.size() > 0) { - const char *AddrFmt = (HeaderData.AddrSize == 4) ? "0x%8.8" PRIx64 "\n" - : "0x%16.16" PRIx64 "\n"; + const char *AddrFmt = + (AddrSize == 4) ? "0x%8.8" PRIx64 "\n" : "0x%16.16" PRIx64 "\n"; OS << "Addrs: [\n"; for (uint64_t Addr : Addrs) OS << format(AddrFmt, Addr); @@ -162,21 +162,13 @@ Expected<uint64_t> DWARFDebugAddrTable::getAddrEntry(uint32_t Index) const { return Addrs[Index]; return createStringError(errc::invalid_argument, "Index %" PRIu32 " is out of range of the " - ".debug_addr table at offset 0x%" PRIx64, - Index, HeaderOffset); + "address table at offset 0x%" PRIx64, + Index, Offset); } -uint32_t DWARFDebugAddrTable::getLength() const { - if (HeaderData.Length == 0) - return 0; - // TODO: DWARF64 support. - return HeaderData.Length + sizeof(uint32_t); +Optional<uint64_t> DWARFDebugAddrTable::getFullLength() const { + if (Length == 0) + return None; + return Length + dwarf::getUnitLengthFieldByteSize(Format); } -uint32_t DWARFDebugAddrTable::getDataSize() const { - if (DataSize != 0) - return DataSize; - if (getLength() == 0) - return 0; - return getLength() - getHeaderSize(); -} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp index 200b2d52a02b9..608fc0388af08 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugArangeSet.cpp @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include <cassert> @@ -29,80 +31,141 @@ void DWARFDebugArangeSet::clear() { ArangeDescriptors.clear(); } -bool -DWARFDebugArangeSet::extract(DataExtractor data, uint64_t *offset_ptr) { - if (data.isValidOffset(*offset_ptr)) { - ArangeDescriptors.clear(); - Offset = *offset_ptr; - - // 7.20 Address Range Table - // - // Each set of entries in the table of address ranges contained in - // the .debug_aranges section begins with a header consisting of: a - // 4-byte length containing the length of the set of entries for this - // compilation unit, not including the length field itself; a 2-byte - // version identifier containing the value 2 for DWARF Version 2; a - // 4-byte offset into the.debug_infosection; a 1-byte unsigned integer - // containing the size in bytes of an address (or the offset portion of - // an address for segmented addressing) on the target system; and a - // 1-byte unsigned integer containing the size in bytes of a segment - // descriptor on the target system. This header is followed by a series - // of tuples. Each tuple consists of an address and a length, each in - // the size appropriate for an address on the target architecture. - HeaderData.Length = data.getU32(offset_ptr); - HeaderData.Version = data.getU16(offset_ptr); - HeaderData.CuOffset = data.getU32(offset_ptr); - HeaderData.AddrSize = data.getU8(offset_ptr); - HeaderData.SegSize = data.getU8(offset_ptr); - - // Perform basic validation of the header fields. - if (!data.isValidOffsetForDataOfSize(Offset, HeaderData.Length) || - (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8)) { - clear(); - return false; - } - - // The first tuple following the header in each set begins at an offset - // that is a multiple of the size of a single tuple (that is, twice the - // size of an address). The header is padded, if necessary, to the - // appropriate boundary. - const uint32_t header_size = *offset_ptr - Offset; - const uint32_t tuple_size = HeaderData.AddrSize * 2; - uint32_t first_tuple_offset = 0; - while (first_tuple_offset < header_size) - first_tuple_offset += tuple_size; - - *offset_ptr = Offset + first_tuple_offset; - - Descriptor arangeDescriptor; - - static_assert(sizeof(arangeDescriptor.Address) == - sizeof(arangeDescriptor.Length), - "Different datatypes for addresses and sizes!"); - assert(sizeof(arangeDescriptor.Address) >= HeaderData.AddrSize); - - while (data.isValidOffset(*offset_ptr)) { - arangeDescriptor.Address = data.getUnsigned(offset_ptr, HeaderData.AddrSize); - arangeDescriptor.Length = data.getUnsigned(offset_ptr, HeaderData.AddrSize); +Error DWARFDebugArangeSet::extract(DWARFDataExtractor data, + uint64_t *offset_ptr) { + assert(data.isValidOffset(*offset_ptr)); + ArangeDescriptors.clear(); + Offset = *offset_ptr; + + // 7.21 Address Range Table (extract) + // Each set of entries in the table of address ranges contained in + // the .debug_aranges section begins with a header containing: + // 1. unit_length (initial length) + // A 4-byte (32-bit DWARF) or 12-byte (64-bit DWARF) length containing + // the length of the set of entries for this compilation unit, + // not including the length field itself. + // 2. version (uhalf) + // The value in this field is 2. + // 3. debug_info_offset (section offset) + // A 4-byte (32-bit DWARF) or 8-byte (64-bit DWARF) offset into the + // .debug_info section of the compilation unit header. + // 4. address_size (ubyte) + // 5. segment_selector_size (ubyte) + // This header is followed by a series of tuples. Each tuple consists of + // a segment, an address and a length. The segment selector size is given by + // the segment_selector_size field of the header; the address and length + // size are each given by the address_size field of the header. Each set of + // tuples is terminated by a 0 for the segment, a 0 for the address and 0 + // for the length. If the segment_selector_size field in the header is zero, + // the segment selectors are omitted from all tuples, including + // the terminating tuple. + + Error Err = Error::success(); + std::tie(HeaderData.Length, HeaderData.Format) = + data.getInitialLength(offset_ptr, &Err); + HeaderData.Version = data.getU16(offset_ptr, &Err); + HeaderData.CuOffset = data.getUnsigned( + offset_ptr, dwarf::getDwarfOffsetByteSize(HeaderData.Format), &Err); + HeaderData.AddrSize = data.getU8(offset_ptr, &Err); + HeaderData.SegSize = data.getU8(offset_ptr, &Err); + if (Err) { + return createStringError(errc::invalid_argument, + "parsing address ranges table at offset 0x%" PRIx64 + ": %s", + Offset, toString(std::move(Err)).c_str()); + } + // Perform basic validation of the header fields. + uint64_t full_length = + dwarf::getUnitLengthFieldByteSize(HeaderData.Format) + HeaderData.Length; + if (!data.isValidOffsetForDataOfSize(Offset, full_length)) + return createStringError(errc::invalid_argument, + "the length of address range table at offset " + "0x%" PRIx64 " exceeds section size", + Offset); + if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8) + return createStringError(errc::invalid_argument, + "address range table at offset 0x%" PRIx64 + " has unsupported address size: %d " + "(4 and 8 supported)", + Offset, HeaderData.AddrSize); + if (HeaderData.SegSize != 0) + return createStringError(errc::not_supported, + "non-zero segment selector size in address range " + "table at offset 0x%" PRIx64 " is not supported", + Offset); + + // The first tuple following the header in each set begins at an offset that + // is a multiple of the size of a single tuple (that is, twice the size of + // an address because we do not support non-zero segment selector sizes). + // Therefore, the full length should also be a multiple of the tuple size. + const uint32_t tuple_size = HeaderData.AddrSize * 2; + if (full_length % tuple_size != 0) + return createStringError( + errc::invalid_argument, + "address range table at offset 0x%" PRIx64 + " has length that is not a multiple of the tuple size", + Offset); + + // The header is padded, if necessary, to the appropriate boundary. + const uint32_t header_size = *offset_ptr - Offset; + uint32_t first_tuple_offset = 0; + while (first_tuple_offset < header_size) + first_tuple_offset += tuple_size; + + // There should be space for at least one tuple. + if (full_length <= first_tuple_offset) + return createStringError( + errc::invalid_argument, + "address range table at offset 0x%" PRIx64 + " has an insufficient length to contain any entries", + Offset); + + *offset_ptr = Offset + first_tuple_offset; + + Descriptor arangeDescriptor; + + static_assert(sizeof(arangeDescriptor.Address) == + sizeof(arangeDescriptor.Length), + "Different datatypes for addresses and sizes!"); + assert(sizeof(arangeDescriptor.Address) >= HeaderData.AddrSize); + + uint64_t end_offset = Offset + full_length; + while (*offset_ptr < end_offset) { + arangeDescriptor.Address = data.getUnsigned(offset_ptr, HeaderData.AddrSize); + arangeDescriptor.Length = data.getUnsigned(offset_ptr, HeaderData.AddrSize); + + if (arangeDescriptor.Length == 0) { // Each set of tuples is terminated by a 0 for the address and 0 // for the length. - if (arangeDescriptor.Address || arangeDescriptor.Length) - ArangeDescriptors.push_back(arangeDescriptor); - else - break; // We are done if we get a zero address and length + if (arangeDescriptor.Address == 0 && *offset_ptr == end_offset) + return ErrorSuccess(); + return createStringError( + errc::invalid_argument, + "address range table at offset 0x%" PRIx64 + " has an invalid tuple (length = 0) at offset 0x%" PRIx64, + Offset, *offset_ptr - tuple_size); } - return !ArangeDescriptors.empty(); + ArangeDescriptors.push_back(arangeDescriptor); } - return false; + + return createStringError(errc::invalid_argument, + "address range table at offset 0x%" PRIx64 + " is not terminated by null entry", + Offset); } void DWARFDebugArangeSet::dump(raw_ostream &OS) const { - OS << format("Address Range Header: length = 0x%8.8x, version = 0x%4.4x, ", - HeaderData.Length, HeaderData.Version) - << format("cu_offset = 0x%8.8x, addr_size = 0x%2.2x, seg_size = 0x%2.2x\n", - HeaderData.CuOffset, HeaderData.AddrSize, HeaderData.SegSize); + int OffsetDumpWidth = 2 * dwarf::getDwarfOffsetByteSize(HeaderData.Format); + OS << "Address Range Header: " + << format("length = 0x%0*" PRIx64 ", ", OffsetDumpWidth, HeaderData.Length) + << "format = " << dwarf::FormatString(HeaderData.Format) << ", " + << format("version = 0x%4.4x, ", HeaderData.Version) + << format("cu_offset = 0x%0*" PRIx64 ", ", OffsetDumpWidth, + HeaderData.CuOffset) + << format("addr_size = 0x%2.2x, ", HeaderData.AddrSize) + << format("seg_size = 0x%2.2x\n", HeaderData.SegSize); for (const auto &Desc : ArangeDescriptors) { Desc.dump(OS, HeaderData.AddrSize); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp index fa157e8688515..e8ed63075055e 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugAranges.cpp @@ -11,7 +11,6 @@ #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" #include "llvm/Support/DataExtractor.h" -#include "llvm/Support/WithColor.h" #include <algorithm> #include <cassert> #include <cstdint> @@ -20,13 +19,19 @@ using namespace llvm; -void DWARFDebugAranges::extract(DataExtractor DebugArangesData) { +void DWARFDebugAranges::extract( + DWARFDataExtractor DebugArangesData, + function_ref<void(Error)> RecoverableErrorHandler) { if (!DebugArangesData.isValidOffset(0)) return; uint64_t Offset = 0; DWARFDebugArangeSet Set; - while (Set.extract(DebugArangesData, &Offset)) { + while (DebugArangesData.isValidOffset(Offset)) { + if (Error E = Set.extract(DebugArangesData, &Offset)) { + RecoverableErrorHandler(std::move(E)); + return; + } uint64_t CUOffset = Set.getCompileUnitDIEOffset(); for (const auto &Desc : Set.descriptors()) { uint64_t LowPC = Desc.Address; @@ -43,9 +48,9 @@ void DWARFDebugAranges::generate(DWARFContext *CTX) { return; // Extract aranges from .debug_aranges section. - DataExtractor ArangesData(CTX->getDWARFObj().getArangesSection(), - CTX->isLittleEndian(), 0); - extract(ArangesData); + DWARFDataExtractor ArangesData(CTX->getDWARFObj().getArangesSection(), + CTX->isLittleEndian(), 0); + extract(ArangesData, CTX->getRecoverableErrorHandler()); // Generate aranges from DIEs: even if .debug_aranges section is present, // it may describe only a small subset of compilation units, so we need to @@ -55,7 +60,7 @@ void DWARFDebugAranges::generate(DWARFContext *CTX) { if (ParsedCUOffsets.insert(CUOffset).second) { Expected<DWARFAddressRangesVector> CURanges = CU->collectAddressRanges(); if (!CURanges) - WithColor::error() << toString(CURanges.takeError()) << '\n'; + CTX->getRecoverableErrorHandler()(CURanges.takeError()); else for (const auto &R : *CURanges) appendRange(CUOffset, R.LowPC, R.HighPC); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index 81b00f65741b5..0a1b75592290c 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -36,123 +36,130 @@ const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f; Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset, uint64_t EndOffset) { - while (*Offset < EndOffset) { - uint8_t Opcode = Data.getRelocatedValue(1, Offset); - // Some instructions have a primary opcode encoded in the top bits. - uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK; + DataExtractor::Cursor C(*Offset); + while (C && C.tell() < EndOffset) { + uint8_t Opcode = Data.getRelocatedValue(C, 1); + if (!C) + break; - if (Primary) { + // Some instructions have a primary opcode encoded in the top bits. + if (uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK) { // If it's a primary opcode, the first operand is encoded in the bottom // bits of the opcode itself. uint64_t Op1 = Opcode & DWARF_CFI_PRIMARY_OPERAND_MASK; switch (Primary) { - default: - return createStringError(errc::illegal_byte_sequence, - "Invalid primary CFI opcode 0x%" PRIx8, - Primary); case DW_CFA_advance_loc: case DW_CFA_restore: addInstruction(Primary, Op1); break; case DW_CFA_offset: - addInstruction(Primary, Op1, Data.getULEB128(Offset)); + addInstruction(Primary, Op1, Data.getULEB128(C)); break; - } - } else { - // Extended opcode - its value is Opcode itself. - switch (Opcode) { default: - return createStringError(errc::illegal_byte_sequence, - "Invalid extended CFI opcode 0x%" PRIx8, - Opcode); - case DW_CFA_nop: - case DW_CFA_remember_state: - case DW_CFA_restore_state: - case DW_CFA_GNU_window_save: - // No operands - addInstruction(Opcode); - break; - case DW_CFA_set_loc: - // Operands: Address - addInstruction(Opcode, Data.getRelocatedAddress(Offset)); - break; - case DW_CFA_advance_loc1: - // Operands: 1-byte delta - addInstruction(Opcode, Data.getRelocatedValue(1, Offset)); - break; - case DW_CFA_advance_loc2: - // Operands: 2-byte delta - addInstruction(Opcode, Data.getRelocatedValue(2, Offset)); - break; - case DW_CFA_advance_loc4: - // Operands: 4-byte delta - addInstruction(Opcode, Data.getRelocatedValue(4, Offset)); - break; - case DW_CFA_restore_extended: - case DW_CFA_undefined: - case DW_CFA_same_value: - case DW_CFA_def_cfa_register: - case DW_CFA_def_cfa_offset: - case DW_CFA_GNU_args_size: - // Operands: ULEB128 - addInstruction(Opcode, Data.getULEB128(Offset)); - break; - case DW_CFA_def_cfa_offset_sf: - // Operands: SLEB128 - addInstruction(Opcode, Data.getSLEB128(Offset)); - break; - case DW_CFA_offset_extended: - case DW_CFA_register: - case DW_CFA_def_cfa: - case DW_CFA_val_offset: { - // Operands: ULEB128, ULEB128 - // Note: We can not embed getULEB128 directly into function - // argument list. getULEB128 changes Offset and order of evaluation - // for arguments is unspecified. - auto op1 = Data.getULEB128(Offset); - auto op2 = Data.getULEB128(Offset); - addInstruction(Opcode, op1, op2); - break; - } - case DW_CFA_offset_extended_sf: - case DW_CFA_def_cfa_sf: - case DW_CFA_val_offset_sf: { - // Operands: ULEB128, SLEB128 - // Note: see comment for the previous case - auto op1 = Data.getULEB128(Offset); - auto op2 = (uint64_t)Data.getSLEB128(Offset); - addInstruction(Opcode, op1, op2); - break; - } - case DW_CFA_def_cfa_expression: { - uint32_t ExprLength = Data.getULEB128(Offset); - addInstruction(Opcode, 0); - DataExtractor Extractor( - Data.getData().slice(*Offset, *Offset + ExprLength), - Data.isLittleEndian(), Data.getAddressSize()); - Instructions.back().Expression = DWARFExpression( - Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION); - *Offset += ExprLength; - break; - } - case DW_CFA_expression: - case DW_CFA_val_expression: { - auto RegNum = Data.getULEB128(Offset); - auto BlockLength = Data.getULEB128(Offset); - addInstruction(Opcode, RegNum, 0); - DataExtractor Extractor( - Data.getData().slice(*Offset, *Offset + BlockLength), - Data.isLittleEndian(), Data.getAddressSize()); - Instructions.back().Expression = DWARFExpression( - Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION); - *Offset += BlockLength; - break; - } + llvm_unreachable("invalid primary CFI opcode"); } + continue; + } + + // Extended opcode - its value is Opcode itself. + switch (Opcode) { + default: + return createStringError(errc::illegal_byte_sequence, + "invalid extended CFI opcode 0x%" PRIx8, Opcode); + case DW_CFA_nop: + case DW_CFA_remember_state: + case DW_CFA_restore_state: + case DW_CFA_GNU_window_save: + // No operands + addInstruction(Opcode); + break; + case DW_CFA_set_loc: + // Operands: Address + addInstruction(Opcode, Data.getRelocatedAddress(C)); + break; + case DW_CFA_advance_loc1: + // Operands: 1-byte delta + addInstruction(Opcode, Data.getRelocatedValue(C, 1)); + break; + case DW_CFA_advance_loc2: + // Operands: 2-byte delta + addInstruction(Opcode, Data.getRelocatedValue(C, 2)); + break; + case DW_CFA_advance_loc4: + // Operands: 4-byte delta + addInstruction(Opcode, Data.getRelocatedValue(C, 4)); + break; + case DW_CFA_restore_extended: + case DW_CFA_undefined: + case DW_CFA_same_value: + case DW_CFA_def_cfa_register: + case DW_CFA_def_cfa_offset: + case DW_CFA_GNU_args_size: + // Operands: ULEB128 + addInstruction(Opcode, Data.getULEB128(C)); + break; + case DW_CFA_def_cfa_offset_sf: + // Operands: SLEB128 + addInstruction(Opcode, Data.getSLEB128(C)); + break; + case DW_CFA_offset_extended: + case DW_CFA_register: + case DW_CFA_def_cfa: + case DW_CFA_val_offset: { + // Operands: ULEB128, ULEB128 + // Note: We can not embed getULEB128 directly into function + // argument list. getULEB128 changes Offset and order of evaluation + // for arguments is unspecified. + uint64_t op1 = Data.getULEB128(C); + uint64_t op2 = Data.getULEB128(C); + addInstruction(Opcode, op1, op2); + break; + } + case DW_CFA_offset_extended_sf: + case DW_CFA_def_cfa_sf: + case DW_CFA_val_offset_sf: { + // Operands: ULEB128, SLEB128 + // Note: see comment for the previous case + uint64_t op1 = Data.getULEB128(C); + uint64_t op2 = (uint64_t)Data.getSLEB128(C); + addInstruction(Opcode, op1, op2); + break; + } + case DW_CFA_def_cfa_expression: { + uint64_t ExprLength = Data.getULEB128(C); + addInstruction(Opcode, 0); + StringRef Expression = Data.getBytes(C, ExprLength); + + DataExtractor Extractor(Expression, Data.isLittleEndian(), + Data.getAddressSize()); + // Note. We do not pass the DWARF format to DWARFExpression, because + // DW_OP_call_ref, the only operation which depends on the format, is + // prohibited in call frame instructions, see sec. 6.4.2 in DWARFv5. + Instructions.back().Expression = + DWARFExpression(Extractor, Data.getAddressSize()); + break; + } + case DW_CFA_expression: + case DW_CFA_val_expression: { + uint64_t RegNum = Data.getULEB128(C); + addInstruction(Opcode, RegNum, 0); + + uint64_t BlockLength = Data.getULEB128(C); + StringRef Expression = Data.getBytes(C, BlockLength); + DataExtractor Extractor(Expression, Data.isLittleEndian(), + Data.getAddressSize()); + // Note. We do not pass the DWARF format to DWARFExpression, because + // DW_OP_call_ref, the only operation which depends on the format, is + // prohibited in call frame instructions, see sec. 6.4.2 in DWARFv5. + Instructions.back().Expression = + DWARFExpression(Extractor, Data.getAddressSize()); + break; + } } } - return Error::success(); + *Offset = C.tell(); + return C.takeError(); } namespace { @@ -285,12 +292,33 @@ void CFIProgram::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH, } } +// Returns the CIE identifier to be used by the requested format. +// CIE ids for .debug_frame sections are defined in Section 7.24 of DWARFv5. +// For CIE ID in .eh_frame sections see +// https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +constexpr uint64_t getCIEId(bool IsDWARF64, bool IsEH) { + if (IsEH) + return 0; + if (IsDWARF64) + return DW64_CIE_ID; + return DW_CIE_ID; +} + void CIE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { - OS << format("%08x %08x %08x CIE", (uint32_t)Offset, (uint32_t)Length, - DW_CIE_ID) - << "\n"; - OS << format(" Version: %d\n", Version); - OS << " Augmentation: \"" << Augmentation << "\"\n"; + // A CIE with a zero length is a terminator entry in the .eh_frame section. + if (IsEH && Length == 0) { + OS << format("%08" PRIx64, Offset) << " ZERO terminator\n"; + return; + } + + OS << format("%08" PRIx64, Offset) + << format(" %0*" PRIx64, IsDWARF64 ? 16 : 8, Length) + << format(" %0*" PRIx64, IsDWARF64 && !IsEH ? 16 : 8, + getCIEId(IsDWARF64, IsEH)) + << " CIE\n" + << " Format: " << FormatString(IsDWARF64) << "\n" + << format(" Version: %d\n", Version) + << " Augmentation: \"" << Augmentation << "\"\n"; if (Version >= 4) { OS << format(" Address size: %u\n", (uint32_t)AddressSize); OS << format(" Segment desc size: %u\n", @@ -313,11 +341,17 @@ void CIE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { } void FDE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { - OS << format("%08x %08x %08x FDE ", (uint32_t)Offset, (uint32_t)Length, - (int32_t)LinkedCIEOffset); - OS << format("cie=%08x pc=%08x...%08x\n", (int32_t)LinkedCIEOffset, - (uint32_t)InitialLocation, - (uint32_t)InitialLocation + (uint32_t)AddressRange); + OS << format("%08" PRIx64, Offset) + << format(" %0*" PRIx64, IsDWARF64 ? 16 : 8, Length) + << format(" %0*" PRIx64, IsDWARF64 && !IsEH ? 16 : 8, CIEPointer) + << " FDE cie="; + if (LinkedCIE) + OS << format("%08" PRIx64, LinkedCIE->getOffset()); + else + OS << "<invalid offset>"; + OS << format(" pc=%08" PRIx64 "...%08" PRIx64 "\n", InitialLocation, + InitialLocation + AddressRange); + OS << " Format: " << FormatString(IsDWARF64) << "\n"; if (LSDAAddress) OS << format(" LSDA Address: %016" PRIx64 "\n", *LSDAAddress); CFIs.dump(OS, MRI, IsEH); @@ -340,36 +374,28 @@ static void LLVM_ATTRIBUTE_UNUSED dumpDataAux(DataExtractor Data, errs() << "\n"; } -// This is a workaround for old compilers which do not allow -// noreturn attribute usage in lambdas. Once the support for those -// compilers are phased out, we can remove this and return back to -// a ReportError lambda: [StartOffset](const char *ErrorMsg). -static void LLVM_ATTRIBUTE_NORETURN ReportError(uint64_t StartOffset, - const char *ErrorMsg) { - std::string Str; - raw_string_ostream OS(Str); - OS << format(ErrorMsg, StartOffset); - OS.flush(); - report_fatal_error(Str); -} - -void DWARFDebugFrame::parse(DWARFDataExtractor Data) { +Error DWARFDebugFrame::parse(DWARFDataExtractor Data) { uint64_t Offset = 0; DenseMap<uint64_t, CIE *> CIEs; while (Data.isValidOffset(Offset)) { uint64_t StartOffset = Offset; - bool IsDWARF64 = false; - uint64_t Length = Data.getRelocatedValue(4, &Offset); - uint64_t Id; + uint64_t Length; + DwarfFormat Format; + std::tie(Length, Format) = Data.getInitialLength(&Offset); + bool IsDWARF64 = Format == DWARF64; - if (Length == dwarf::DW_LENGTH_DWARF64) { - // DWARF-64 is distinguished by the first 32 bits of the initial length - // field being 0xffffffff. Then, the next 64 bits are the actual entry - // length. - IsDWARF64 = true; - Length = Data.getRelocatedValue(8, &Offset); + // If the Length is 0, then this CIE is a terminator. We add it because some + // dumper tools might need it to print something special for such entries + // (e.g. llvm-objdump --dwarf=frames prints "ZERO terminator"). + if (Length == 0) { + auto Cie = std::make_unique<CIE>( + IsDWARF64, StartOffset, 0, 0, SmallString<8>(), 0, 0, 0, 0, 0, + SmallString<8>(), 0, 0, None, None, Arch); + CIEs[StartOffset] = Cie.get(); + Entries.push_back(std::move(Cie)); + break; } // At this point, Offset points to the next field after Length. @@ -380,14 +406,21 @@ void DWARFDebugFrame::parse(DWARFDataExtractor Data) { uint64_t EndStructureOffset = Offset + Length; // The Id field's size depends on the DWARF format - Id = Data.getUnsigned(&Offset, (IsDWARF64 && !IsEH) ? 8 : 4); - bool IsCIE = - ((IsDWARF64 && Id == DW64_CIE_ID) || Id == DW_CIE_ID || (IsEH && !Id)); + Error Err = Error::success(); + uint64_t Id = Data.getRelocatedValue((IsDWARF64 && !IsEH) ? 8 : 4, &Offset, + /*SectionIndex=*/nullptr, &Err); + if (Err) + return Err; - if (IsCIE) { + if (Id == getCIEId(IsDWARF64, IsEH)) { uint8_t Version = Data.getU8(&Offset); const char *Augmentation = Data.getCStr(&Offset); StringRef AugmentationString(Augmentation ? Augmentation : ""); + // TODO: we should provide a way to report a warning and continue dumping. + if (IsEH && Version != 1) + return createStringError(errc::not_supported, + "unsupported CIE version: %" PRIu8, Version); + uint8_t AddressSize = Version < 4 ? Data.getAddressSize() : Data.getU8(&Offset); Data.setAddressSize(AddressSize); @@ -411,61 +444,66 @@ void DWARFDebugFrame::parse(DWARFDataExtractor Data) { // Walk the augmentation string to get all the augmentation data. for (unsigned i = 0, e = AugmentationString.size(); i != e; ++i) { switch (AugmentationString[i]) { - default: - ReportError( - StartOffset, - "Unknown augmentation character in entry at %" PRIx64); - case 'L': - LSDAPointerEncoding = Data.getU8(&Offset); - break; - case 'P': { - if (Personality) - ReportError(StartOffset, - "Duplicate personality in entry at %" PRIx64); - PersonalityEncoding = Data.getU8(&Offset); - Personality = Data.getEncodedPointer( - &Offset, *PersonalityEncoding, - EHFrameAddress ? EHFrameAddress + Offset : 0); - break; - } - case 'R': - FDEPointerEncoding = Data.getU8(&Offset); - break; - case 'S': - // Current frame is a signal trampoline. - break; - case 'z': - if (i) - ReportError(StartOffset, - "'z' must be the first character at %" PRIx64); - // Parse the augmentation length first. We only parse it if - // the string contains a 'z'. - AugmentationLength = Data.getULEB128(&Offset); - StartAugmentationOffset = Offset; - EndAugmentationOffset = Offset + *AugmentationLength; - break; - case 'B': - // B-Key is used for signing functions associated with this - // augmentation string - break; + default: + return createStringError( + errc::invalid_argument, + "unknown augmentation character in entry at 0x%" PRIx64, + StartOffset); + case 'L': + LSDAPointerEncoding = Data.getU8(&Offset); + break; + case 'P': { + if (Personality) + return createStringError( + errc::invalid_argument, + "duplicate personality in entry at 0x%" PRIx64, StartOffset); + PersonalityEncoding = Data.getU8(&Offset); + Personality = Data.getEncodedPointer( + &Offset, *PersonalityEncoding, + EHFrameAddress ? EHFrameAddress + Offset : 0); + break; + } + case 'R': + FDEPointerEncoding = Data.getU8(&Offset); + break; + case 'S': + // Current frame is a signal trampoline. + break; + case 'z': + if (i) + return createStringError( + errc::invalid_argument, + "'z' must be the first character at 0x%" PRIx64, StartOffset); + // Parse the augmentation length first. We only parse it if + // the string contains a 'z'. + AugmentationLength = Data.getULEB128(&Offset); + StartAugmentationOffset = Offset; + EndAugmentationOffset = Offset + *AugmentationLength; + break; + case 'B': + // B-Key is used for signing functions associated with this + // augmentation string + break; } } if (AugmentationLength.hasValue()) { if (Offset != EndAugmentationOffset) - ReportError(StartOffset, - "Parsing augmentation data at %" PRIx64 " failed"); - + return createStringError(errc::invalid_argument, + "parsing augmentation data at 0x%" PRIx64 + " failed", + StartOffset); AugmentationData = Data.getData().slice(StartAugmentationOffset, EndAugmentationOffset); } } auto Cie = std::make_unique<CIE>( - StartOffset, Length, Version, AugmentationString, AddressSize, - SegmentDescriptorSize, CodeAlignmentFactor, DataAlignmentFactor, - ReturnAddressRegister, AugmentationData, FDEPointerEncoding, - LSDAPointerEncoding, Personality, PersonalityEncoding, Arch); + IsDWARF64, StartOffset, Length, Version, AugmentationString, + AddressSize, SegmentDescriptorSize, CodeAlignmentFactor, + DataAlignmentFactor, ReturnAddressRegister, AugmentationData, + FDEPointerEncoding, LSDAPointerEncoding, Personality, + PersonalityEncoding, Arch); CIEs[StartOffset] = Cie.get(); Entries.emplace_back(std::move(Cie)); } else { @@ -479,9 +517,10 @@ void DWARFDebugFrame::parse(DWARFDataExtractor Data) { if (IsEH) { // The address size is encoded in the CIE we reference. if (!Cie) - ReportError(StartOffset, "Parsing FDE data at %" PRIx64 - " failed due to missing CIE"); - + return createStringError(errc::invalid_argument, + "parsing FDE data at 0x%" PRIx64 + " failed due to missing CIE", + StartOffset); if (auto Val = Data.getEncodedPointer( &Offset, Cie->getFDEPointerEncoding(), EHFrameAddress ? EHFrameAddress + Offset : 0)) { @@ -507,28 +546,32 @@ void DWARFDebugFrame::parse(DWARFDataExtractor Data) { } if (Offset != EndAugmentationOffset) - ReportError(StartOffset, - "Parsing augmentation data at %" PRIx64 " failed"); + return createStringError(errc::invalid_argument, + "parsing augmentation data at 0x%" PRIx64 + " failed", + StartOffset); } } else { InitialLocation = Data.getRelocatedAddress(&Offset); AddressRange = Data.getRelocatedAddress(&Offset); } - Entries.emplace_back(new FDE(StartOffset, Length, CIEPointer, - InitialLocation, AddressRange, - Cie, LSDAAddress, Arch)); + Entries.emplace_back(new FDE(IsDWARF64, StartOffset, Length, CIEPointer, + InitialLocation, AddressRange, Cie, + LSDAAddress, Arch)); } if (Error E = - Entries.back()->cfis().parse(Data, &Offset, EndStructureOffset)) { - report_fatal_error(toString(std::move(E))); - } + Entries.back()->cfis().parse(Data, &Offset, EndStructureOffset)) + return E; if (Offset != EndStructureOffset) - ReportError(StartOffset, - "Parsing entry instructions at %" PRIx64 " failed"); + return createStringError( + errc::invalid_argument, + "parsing entry instructions at 0x%" PRIx64 " failed", StartOffset); } + + return Error::success(); } FrameEntry *DWARFDebugFrame::getEntryAtOffset(uint64_t Offset) const { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp index 11adb1e476404..3ca21e97888cf 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp @@ -16,6 +16,7 @@ #include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -41,6 +42,10 @@ using ContentDescriptors = SmallVector<ContentDescriptor, 4>; } // end anonymous namespace +static bool versionIsSupported(uint16_t Version) { + return Version >= 2 && Version <= 5; +} + void DWARFDebugLine::ContentTypeTracker::trackContentType( dwarf::LineNumberEntryFormat ContentType) { switch (ContentType) { @@ -99,13 +104,21 @@ void DWARFDebugLine::Prologue::clear() { void DWARFDebugLine::Prologue::dump(raw_ostream &OS, DIDumpOptions DumpOptions) const { + if (!totalLengthIsValid()) + return; + int OffsetDumpWidth = 2 * dwarf::getDwarfOffsetByteSize(FormParams.Format); OS << "Line table prologue:\n" - << format(" total_length: 0x%8.8" PRIx64 "\n", TotalLength) + << format(" total_length: 0x%0*" PRIx64 "\n", OffsetDumpWidth, + TotalLength) + << " format: " << dwarf::FormatString(FormParams.Format) << "\n" << format(" version: %u\n", getVersion()); + if (!versionIsSupported(getVersion())) + return; if (getVersion() >= 5) OS << format(" address_size: %u\n", getAddressSize()) << format(" seg_select_size: %u\n", SegSelectorSize); - OS << format(" prologue_length: 0x%8.8" PRIx64 "\n", PrologueLength) + OS << format(" prologue_length: 0x%0*" PRIx64 "\n", OffsetDumpWidth, + PrologueLength) << format(" min_inst_length: %u\n", MinInstLength) << format(getVersion() >= 4 ? "max_ops_per_inst: %u\n" : "", MaxOpsPerInst) << format(" default_is_stmt: %u\n", DefaultIsStmt) @@ -114,8 +127,9 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS, << format(" opcode_base: %u\n", OpcodeBase); for (uint32_t I = 0; I != StandardOpcodeLengths.size(); ++I) - OS << format("standard_opcode_lengths[%s] = %u\n", - LNStandardString(I + 1).data(), StandardOpcodeLengths[I]); + OS << formatv("standard_opcode_lengths[{0}] = {1}\n", + static_cast<dwarf::LineNumberOps>(I + 1), + StandardOpcodeLengths[I]); if (!IncludeDirectories.empty()) { // DWARF v5 starts directory indexes at 0. @@ -153,14 +167,21 @@ void DWARFDebugLine::Prologue::dump(raw_ostream &OS, } // Parse v2-v4 directory and file tables. -static void +static Error parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, - uint64_t *OffsetPtr, uint64_t EndPrologueOffset, + uint64_t *OffsetPtr, DWARFDebugLine::ContentTypeTracker &ContentTypes, std::vector<DWARFFormValue> &IncludeDirectories, std::vector<DWARFDebugLine::FileNameEntry> &FileNames) { - while (*OffsetPtr < EndPrologueOffset) { - StringRef S = DebugLineData.getCStrRef(OffsetPtr); + while (true) { + Error Err = Error::success(); + StringRef S = DebugLineData.getCStrRef(OffsetPtr, &Err); + if (Err) { + consumeError(std::move(Err)); + return createStringError(errc::invalid_argument, + "include directories table was not null " + "terminated before the end of the prologue"); + } if (S.empty()) break; DWARFFormValue Dir = @@ -168,21 +189,33 @@ parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, IncludeDirectories.push_back(Dir); } - while (*OffsetPtr < EndPrologueOffset) { - StringRef Name = DebugLineData.getCStrRef(OffsetPtr); - if (Name.empty()) + ContentTypes.HasModTime = true; + ContentTypes.HasLength = true; + + while (true) { + Error Err = Error::success(); + StringRef Name = DebugLineData.getCStrRef(OffsetPtr, &Err); + if (!Err && Name.empty()) break; + DWARFDebugLine::FileNameEntry FileEntry; FileEntry.Name = DWARFFormValue::createFromPValue(dwarf::DW_FORM_string, Name.data()); - FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr); - FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); - FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); + FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr, &Err); + FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr, &Err); + FileEntry.Length = DebugLineData.getULEB128(OffsetPtr, &Err); + + if (Err) { + consumeError(std::move(Err)); + return createStringError( + errc::invalid_argument, + "file names table was not null terminated before " + "the end of the prologue"); + } FileNames.push_back(FileEntry); } - ContentTypes.HasModTime = true; - ContentTypes.HasLength = true; + return Error::success(); } // Parse v5 directory/file entry content descriptions. @@ -191,14 +224,15 @@ parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, static llvm::Expected<ContentDescriptors> parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, DWARFDebugLine::ContentTypeTracker *ContentTypes) { + Error Err = Error::success(); ContentDescriptors Descriptors; - int FormatCount = DebugLineData.getU8(OffsetPtr); + int FormatCount = DebugLineData.getU8(OffsetPtr, &Err); bool HasPath = false; - for (int I = 0; I != FormatCount; ++I) { + for (int I = 0; I != FormatCount && !Err; ++I) { ContentDescriptor Descriptor; Descriptor.Type = - dwarf::LineNumberEntryFormat(DebugLineData.getULEB128(OffsetPtr)); - Descriptor.Form = dwarf::Form(DebugLineData.getULEB128(OffsetPtr)); + dwarf::LineNumberEntryFormat(DebugLineData.getULEB128(OffsetPtr, &Err)); + Descriptor.Form = dwarf::Form(DebugLineData.getULEB128(OffsetPtr, &Err)); if (Descriptor.Type == dwarf::DW_LNCT_path) HasPath = true; if (ContentTypes) @@ -206,6 +240,11 @@ parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, Descriptors.push_back(Descriptor); } + if (Err) + return createStringError(errc::invalid_argument, + "failed to parse entry content descriptors: %s", + toString(std::move(Err)).c_str()); + if (!HasPath) return createStringError(errc::invalid_argument, "failed to parse entry content descriptions" @@ -227,8 +266,8 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, 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) { + uint64_t DirEntryCount = DebugLineData.getULEB128(OffsetPtr); + for (uint64_t I = 0; I != DirEntryCount; ++I) { for (auto Descriptor : *DirDescriptors) { DWARFFormValue Value(Descriptor.Form); switch (Descriptor.Type) { @@ -236,14 +275,14 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) return createStringError(errc::invalid_argument, "failed to parse directory entry because " - "extracting the form value failed."); + "extracting the form value failed"); IncludeDirectories.push_back(Value); break; default: if (!Value.skipValue(DebugLineData, OffsetPtr, FormParams)) return createStringError(errc::invalid_argument, "failed to parse directory entry because " - "skipping the form value failed."); + "skipping the form value failed"); } } } @@ -255,15 +294,15 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, 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) { + uint64_t FileEntryCount = DebugLineData.getULEB128(OffsetPtr); + for (uint64_t I = 0; I != FileEntryCount; ++I) { DWARFDebugLine::FileNameEntry FileEntry; for (auto Descriptor : *FileDescriptors) { DWARFFormValue Value(Descriptor.Form); if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) return createStringError(errc::invalid_argument, "failed to parse file entry because " - "extracting the form value failed."); + "extracting the form value failed"); switch (Descriptor.Type) { case DW_LNCT_path: FileEntry.Name = Value; @@ -297,78 +336,114 @@ parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, return Error::success(); } -Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, - uint64_t *OffsetPtr, - const DWARFContext &Ctx, - const DWARFUnit *U) { +uint64_t DWARFDebugLine::Prologue::getLength() const { + uint64_t Length = PrologueLength + sizeofTotalLength() + + sizeof(getVersion()) + sizeofPrologueLength(); + if (getVersion() >= 5) + Length += 2; // Address + Segment selector sizes. + return Length; +} + +Error DWARFDebugLine::Prologue::parse( + DWARFDataExtractor DebugLineData, uint64_t *OffsetPtr, + function_ref<void(Error)> RecoverableErrorHandler, const DWARFContext &Ctx, + const DWARFUnit *U) { const uint64_t PrologueOffset = *OffsetPtr; clear(); - TotalLength = DebugLineData.getRelocatedValue(4, OffsetPtr); - if (TotalLength == dwarf::DW_LENGTH_DWARF64) { - FormParams.Format = dwarf::DWARF64; - TotalLength = DebugLineData.getU64(OffsetPtr); - } else if (TotalLength >= dwarf::DW_LENGTH_lo_reserved) { - return createStringError(errc::invalid_argument, + DataExtractor::Cursor Cursor(*OffsetPtr); + std::tie(TotalLength, FormParams.Format) = + DebugLineData.getInitialLength(Cursor); + + DebugLineData = + DWARFDataExtractor(DebugLineData, Cursor.tell() + TotalLength); + FormParams.Version = DebugLineData.getU16(Cursor); + if (Cursor && !versionIsSupported(getVersion())) { + // Treat this error as unrecoverable - we cannot be sure what any of + // the data represents including the length field, so cannot skip it or make + // any reasonable assumptions. + *OffsetPtr = Cursor.tell(); + return createStringError( + errc::not_supported, "parsing line table prologue at offset 0x%8.8" PRIx64 - " unsupported reserved unit length found of value 0x%8.8" PRIx64, - PrologueOffset, TotalLength); + ": unsupported version %" PRIu16, + PrologueOffset, getVersion()); } - FormParams.Version = DebugLineData.getU16(OffsetPtr); - if (getVersion() < 2) - return createStringError(errc::not_supported, - "parsing line table prologue at offset 0x%8.8" PRIx64 - " found unsupported version 0x%2.2" PRIx16, - PrologueOffset, getVersion()); if (getVersion() >= 5) { - FormParams.AddrSize = DebugLineData.getU8(OffsetPtr); - assert((DebugLineData.getAddressSize() == 0 || + FormParams.AddrSize = DebugLineData.getU8(Cursor); + assert((!Cursor || DebugLineData.getAddressSize() == 0 || DebugLineData.getAddressSize() == getAddressSize()) && "Line table header and data extractor disagree"); - SegSelectorSize = DebugLineData.getU8(OffsetPtr); + SegSelectorSize = DebugLineData.getU8(Cursor); } PrologueLength = - DebugLineData.getRelocatedValue(sizeofPrologueLength(), OffsetPtr); - const uint64_t EndPrologueOffset = PrologueLength + *OffsetPtr; - MinInstLength = DebugLineData.getU8(OffsetPtr); + DebugLineData.getRelocatedValue(Cursor, sizeofPrologueLength()); + const uint64_t EndPrologueOffset = PrologueLength + Cursor.tell(); + DebugLineData = DWARFDataExtractor(DebugLineData, EndPrologueOffset); + MinInstLength = DebugLineData.getU8(Cursor); if (getVersion() >= 4) - MaxOpsPerInst = DebugLineData.getU8(OffsetPtr); - DefaultIsStmt = DebugLineData.getU8(OffsetPtr); - LineBase = DebugLineData.getU8(OffsetPtr); - LineRange = DebugLineData.getU8(OffsetPtr); - OpcodeBase = DebugLineData.getU8(OffsetPtr); - - StandardOpcodeLengths.reserve(OpcodeBase - 1); - for (uint32_t I = 1; I < OpcodeBase; ++I) { - uint8_t OpLen = DebugLineData.getU8(OffsetPtr); - StandardOpcodeLengths.push_back(OpLen); + MaxOpsPerInst = DebugLineData.getU8(Cursor); + DefaultIsStmt = DebugLineData.getU8(Cursor); + LineBase = DebugLineData.getU8(Cursor); + LineRange = DebugLineData.getU8(Cursor); + OpcodeBase = DebugLineData.getU8(Cursor); + + if (Cursor && OpcodeBase == 0) { + // If the opcode base is 0, we cannot read the standard opcode lengths (of + // which there are supposed to be one fewer than the opcode base). Assume + // there are no standard opcodes and continue parsing. + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "parsing line table prologue at offset 0x%8.8" PRIx64 + " found opcode base of 0. Assuming no standard opcodes", + PrologueOffset)); + } else if (Cursor) { + StandardOpcodeLengths.reserve(OpcodeBase - 1); + for (uint32_t I = 1; I < OpcodeBase; ++I) { + uint8_t OpLen = DebugLineData.getU8(Cursor); + StandardOpcodeLengths.push_back(OpLen); + } } - if (getVersion() >= 5) { - if (Error E = - parseV5DirFileTables(DebugLineData, OffsetPtr, FormParams, Ctx, U, - ContentTypes, IncludeDirectories, FileNames)) { - return joinErrors( - createStringError( - errc::invalid_argument, - "parsing line table prologue at 0x%8.8" PRIx64 - " found an invalid directory or file table description at" - " 0x%8.8" PRIx64, - PrologueOffset, *OffsetPtr), - std::move(E)); - } - } else - parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, - ContentTypes, IncludeDirectories, FileNames); + *OffsetPtr = Cursor.tell(); + // A corrupt file name or directory table does not prevent interpretation of + // the main line program, so check the cursor state now so that its errors can + // be handled separately. + if (!Cursor) + return createStringError( + errc::invalid_argument, + "parsing line table prologue at offset 0x%8.8" PRIx64 ": %s", + PrologueOffset, toString(Cursor.takeError()).c_str()); + + Error E = + getVersion() >= 5 + ? parseV5DirFileTables(DebugLineData, OffsetPtr, FormParams, Ctx, U, + ContentTypes, IncludeDirectories, FileNames) + : parseV2DirFileTables(DebugLineData, OffsetPtr, ContentTypes, + IncludeDirectories, FileNames); + if (E) { + RecoverableErrorHandler(joinErrors( + createStringError( + errc::invalid_argument, + "parsing line table prologue at 0x%8.8" PRIx64 + " found an invalid directory or file table description at" + " 0x%8.8" PRIx64, + PrologueOffset, *OffsetPtr), + std::move(E))); + return Error::success(); + } - if (*OffsetPtr != EndPrologueOffset) - return createStringError(errc::invalid_argument, - "parsing line table prologue at 0x%8.8" PRIx64 - " should have ended at 0x%8.8" PRIx64 - " but it ended at 0x%8.8" PRIx64, - PrologueOffset, EndPrologueOffset, *OffsetPtr); + assert(*OffsetPtr <= EndPrologueOffset); + if (*OffsetPtr != EndPrologueOffset) { + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "unknown data in line table prologue at offset 0x%8.8" PRIx64 + ": parsing ended (at offset 0x%8.8" PRIx64 + ") before reaching the prologue end at offset 0x%8.8" PRIx64, + PrologueOffset, *OffsetPtr, EndPrologueOffset)); + } return Error::success(); } @@ -396,10 +471,12 @@ void DWARFDebugLine::Row::reset(bool DefaultIsStmt) { EpilogueBegin = false; } -void DWARFDebugLine::Row::dumpTableHeader(raw_ostream &OS) { - OS << "Address Line Column File ISA Discriminator Flags\n" - << "------------------ ------ ------ ------ --- ------------- " - "-------------\n"; +void DWARFDebugLine::Row::dumpTableHeader(raw_ostream &OS, unsigned Indent) { + OS.indent(Indent) + << "Address Line Column File ISA Discriminator Flags\n"; + OS.indent(Indent) + << "------------------ ------ ------ ------ --- ------------- " + "-------------\n"; } void DWARFDebugLine::Row::dump(raw_ostream &OS) const { @@ -430,7 +507,7 @@ void DWARFDebugLine::LineTable::dump(raw_ostream &OS, if (!Rows.empty()) { OS << '\n'; - Row::dumpTableHeader(OS); + Row::dumpTableHeader(OS, 0); for (const Row &R : Rows) { R.dump(OS); } @@ -447,8 +524,10 @@ void DWARFDebugLine::LineTable::clear() { Sequences.clear(); } -DWARFDebugLine::ParsingState::ParsingState(struct LineTable *LT) - : LineTable(LT) { +DWARFDebugLine::ParsingState::ParsingState( + struct LineTable *LT, uint64_t TableOffset, + function_ref<void(Error)> ErrorHandler) + : LineTable(LT), LineTableOffset(TableOffset), ErrorHandler(ErrorHandler) { resetRowAndSequence(); } @@ -488,7 +567,7 @@ DWARFDebugLine::getLineTable(uint64_t Offset) const { Expected<const DWARFDebugLine::LineTable *> DWARFDebugLine::getOrParseLineTable( DWARFDataExtractor &DebugLineData, uint64_t Offset, const DWARFContext &Ctx, - const DWARFUnit *U, function_ref<void(Error)> RecoverableErrorCallback) { + const DWARFUnit *U, function_ref<void(Error)> RecoverableErrorHandler) { if (!DebugLineData.isValidOffset(Offset)) return createStringError(errc::invalid_argument, "offset 0x%8.8" PRIx64 " is not a valid debug line section offset", @@ -499,32 +578,163 @@ Expected<const DWARFDebugLine::LineTable *> DWARFDebugLine::getOrParseLineTable( LineTable *LT = &Pos.first->second; if (Pos.second) { if (Error Err = - LT->parse(DebugLineData, &Offset, Ctx, U, RecoverableErrorCallback)) + LT->parse(DebugLineData, &Offset, Ctx, U, RecoverableErrorHandler)) return std::move(Err); return LT; } return LT; } +static StringRef getOpcodeName(uint8_t Opcode, uint8_t OpcodeBase) { + assert(Opcode != 0); + if (Opcode < OpcodeBase) + return LNStandardString(Opcode); + return "special"; +} + +uint64_t DWARFDebugLine::ParsingState::advanceAddr(uint64_t OperationAdvance, + uint8_t Opcode, + uint64_t OpcodeOffset) { + StringRef OpcodeName = getOpcodeName(Opcode, LineTable->Prologue.OpcodeBase); + // For versions less than 4, the MaxOpsPerInst member is set to 0, as the + // maximum_operations_per_instruction field wasn't introduced until DWARFv4. + // Don't warn about bad values in this situation. + if (ReportAdvanceAddrProblem && LineTable->Prologue.getVersion() >= 4 && + LineTable->Prologue.MaxOpsPerInst != 1) + ErrorHandler(createStringError( + errc::not_supported, + "line table program at offset 0x%8.8" PRIx64 + " contains a %s opcode at offset 0x%8.8" PRIx64 + ", but the prologue maximum_operations_per_instruction value is %" PRId8 + ", which is unsupported. Assuming a value of 1 instead", + LineTableOffset, OpcodeName.data(), OpcodeOffset, + LineTable->Prologue.MaxOpsPerInst)); + if (ReportAdvanceAddrProblem && LineTable->Prologue.MinInstLength == 0) + ErrorHandler( + createStringError(errc::invalid_argument, + "line table program at offset 0x%8.8" PRIx64 + " contains a %s opcode at offset 0x%8.8" PRIx64 + ", but the prologue minimum_instruction_length value " + "is 0, which prevents any address advancing", + LineTableOffset, OpcodeName.data(), OpcodeOffset)); + ReportAdvanceAddrProblem = false; + uint64_t AddrOffset = OperationAdvance * LineTable->Prologue.MinInstLength; + Row.Address.Address += AddrOffset; + return AddrOffset; +} + +DWARFDebugLine::ParsingState::AddrAndAdjustedOpcode +DWARFDebugLine::ParsingState::advanceAddrForOpcode(uint8_t Opcode, + uint64_t OpcodeOffset) { + assert(Opcode == DW_LNS_const_add_pc || + Opcode >= LineTable->Prologue.OpcodeBase); + if (ReportBadLineRange && LineTable->Prologue.LineRange == 0) { + StringRef OpcodeName = + getOpcodeName(Opcode, LineTable->Prologue.OpcodeBase); + ErrorHandler( + createStringError(errc::not_supported, + "line table program at offset 0x%8.8" PRIx64 + " contains a %s opcode at offset 0x%8.8" PRIx64 + ", but the prologue line_range value is 0. The " + "address and line will not be adjusted", + LineTableOffset, OpcodeName.data(), OpcodeOffset)); + ReportBadLineRange = false; + } + + uint8_t OpcodeValue = Opcode; + if (Opcode == DW_LNS_const_add_pc) + OpcodeValue = 255; + uint8_t AdjustedOpcode = OpcodeValue - LineTable->Prologue.OpcodeBase; + uint64_t OperationAdvance = + LineTable->Prologue.LineRange != 0 + ? AdjustedOpcode / LineTable->Prologue.LineRange + : 0; + uint64_t AddrOffset = advanceAddr(OperationAdvance, Opcode, OpcodeOffset); + return {AddrOffset, AdjustedOpcode}; +} + +DWARFDebugLine::ParsingState::AddrAndLineDelta +DWARFDebugLine::ParsingState::handleSpecialOpcode(uint8_t Opcode, + uint64_t OpcodeOffset) { + // A special opcode value is chosen based on the amount that needs + // to be added to the line and address registers. The maximum line + // increment for a special opcode is the value of the line_base + // field in the header, plus the value of the line_range field, + // minus 1 (line base + line range - 1). If the desired line + // increment is greater than the maximum line increment, a standard + // opcode must be used instead of a special opcode. The "address + // advance" is calculated by dividing the desired address increment + // by the minimum_instruction_length field from the header. The + // special opcode is then calculated using the following formula: + // + // opcode = (desired line increment - line_base) + + // (line_range * address advance) + opcode_base + // + // If the resulting opcode is greater than 255, a standard opcode + // must be used instead. + // + // To decode a special opcode, subtract the opcode_base from the + // opcode itself to give the adjusted opcode. The amount to + // increment the address register is the result of the adjusted + // opcode divided by the line_range multiplied by the + // minimum_instruction_length field from the header. That is: + // + // address increment = (adjusted opcode / line_range) * + // minimum_instruction_length + // + // The amount to increment the line register is the line_base plus + // the result of the adjusted opcode modulo the line_range. That is: + // + // line increment = line_base + (adjusted opcode % line_range) + + DWARFDebugLine::ParsingState::AddrAndAdjustedOpcode AddrAdvanceResult = + advanceAddrForOpcode(Opcode, OpcodeOffset); + int32_t LineOffset = 0; + if (LineTable->Prologue.LineRange != 0) + LineOffset = + LineTable->Prologue.LineBase + + (AddrAdvanceResult.AdjustedOpcode % LineTable->Prologue.LineRange); + Row.Line += LineOffset; + return {AddrAdvanceResult.AddrDelta, LineOffset}; +} + +/// Parse a ULEB128 using the specified \p Cursor. \returns the parsed value on +/// success, or None if \p Cursor is in a failing state. +template <typename T> +static Optional<T> parseULEB128(DWARFDataExtractor &Data, + DataExtractor::Cursor &Cursor) { + T Value = Data.getULEB128(Cursor); + if (Cursor) + return Value; + return None; +} + Error DWARFDebugLine::LineTable::parse( DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, const DWARFContext &Ctx, const DWARFUnit *U, - function_ref<void(Error)> RecoverableErrorCallback, raw_ostream *OS) { + function_ref<void(Error)> RecoverableErrorHandler, raw_ostream *OS, + bool Verbose) { + assert((OS || !Verbose) && "cannot have verbose output without stream"); const uint64_t DebugLineOffset = *OffsetPtr; clear(); - Error PrologueErr = Prologue.parse(DebugLineData, OffsetPtr, Ctx, U); + Error PrologueErr = + Prologue.parse(DebugLineData, OffsetPtr, RecoverableErrorHandler, Ctx, U); if (OS) { - // The presence of OS signals verbose dumping. DIDumpOptions DumpOptions; - DumpOptions.Verbose = true; + DumpOptions.Verbose = Verbose; Prologue.dump(*OS, DumpOptions); } - if (PrologueErr) + if (PrologueErr) { + // Ensure there is a blank line after the prologue to clearly delineate it + // from later dumps. + if (OS) + *OS << "\n"; return PrologueErr; + } uint64_t ProgramLength = Prologue.TotalLength + Prologue.sizeofTotalLength(); if (!DebugLineData.isValidOffsetForDataOfSize(DebugLineOffset, @@ -532,7 +742,7 @@ Error DWARFDebugLine::LineTable::parse( assert(DebugLineData.size() > DebugLineOffset && "prologue parsing should handle invalid offset"); uint64_t BytesRemaining = DebugLineData.size() - DebugLineOffset; - RecoverableErrorCallback( + RecoverableErrorHandler( 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 @@ -542,41 +752,62 @@ Error DWARFDebugLine::LineTable::parse( ProgramLength = BytesRemaining; } + // Create a DataExtractor which can only see the data up to the end of the + // table, to prevent reading past the end. const uint64_t EndOffset = DebugLineOffset + ProgramLength; + DWARFDataExtractor TableData(DebugLineData, EndOffset); // See if we should tell the data extractor the address size. - if (DebugLineData.getAddressSize() == 0) - DebugLineData.setAddressSize(Prologue.getAddressSize()); + if (TableData.getAddressSize() == 0) + TableData.setAddressSize(Prologue.getAddressSize()); else assert(Prologue.getAddressSize() == 0 || - Prologue.getAddressSize() == DebugLineData.getAddressSize()); + Prologue.getAddressSize() == TableData.getAddressSize()); - ParsingState State(this); + ParsingState State(this, DebugLineOffset, RecoverableErrorHandler); + *OffsetPtr = DebugLineOffset + Prologue.getLength(); + if (OS && *OffsetPtr < EndOffset) { + *OS << '\n'; + Row::dumpTableHeader(*OS, /*Indent=*/Verbose ? 12 : 0); + } while (*OffsetPtr < EndOffset) { - if (OS) + DataExtractor::Cursor Cursor(*OffsetPtr); + + if (Verbose) *OS << format("0x%08.08" PRIx64 ": ", *OffsetPtr); - uint8_t Opcode = DebugLineData.getU8(OffsetPtr); + uint64_t OpcodeOffset = *OffsetPtr; + uint8_t Opcode = TableData.getU8(Cursor); + size_t RowCount = Rows.size(); - if (OS) + if (Cursor && Verbose) *OS << format("%02.02" PRIx8 " ", Opcode); if (Opcode == 0) { // Extended Opcodes always start with a zero opcode followed by // a uleb128 length so you can skip ones you don't know about - uint64_t Len = DebugLineData.getULEB128(OffsetPtr); - uint64_t ExtOffset = *OffsetPtr; + uint64_t Len = TableData.getULEB128(Cursor); + uint64_t ExtOffset = Cursor.tell(); // Tolerate zero-length; assume length is correct and soldier on. if (Len == 0) { - if (OS) + if (Cursor && Verbose) *OS << "Badly formed extended line op (length 0)\n"; + if (!Cursor) { + if (Verbose) + *OS << "\n"; + RecoverableErrorHandler(Cursor.takeError()); + } + *OffsetPtr = Cursor.tell(); continue; } - uint8_t SubOpcode = DebugLineData.getU8(OffsetPtr); - if (OS) + uint8_t SubOpcode = TableData.getU8(Cursor); + // OperandOffset will be the same as ExtOffset, if it was not possible to + // read the SubOpcode. + uint64_t OperandOffset = Cursor.tell(); + if (Verbose) *OS << LNExtendedString(SubOpcode); switch (SubOpcode) { case DW_LNE_end_sequence: @@ -588,11 +819,15 @@ Error DWARFDebugLine::LineTable::parse( // address is that of the byte after the last target machine instruction // of the sequence. State.Row.EndSequence = true; - if (OS) { + // No need to test the Cursor is valid here, since it must be to get + // into this code path - if it were invalid, the default case would be + // followed. + if (Verbose) { *OS << "\n"; OS->indent(12); - State.Row.dump(*OS); } + if (OS) + State.Row.dump(*OS); State.appendRowToMatrix(); State.resetRowAndSequence(); break; @@ -608,25 +843,39 @@ Error DWARFDebugLine::LineTable::parse( // Make sure the extractor knows the address size. If not, infer it // from the size of the operand. { - uint8_t ExtractorAddressSize = DebugLineData.getAddressSize(); - if (ExtractorAddressSize != Len - 1 && ExtractorAddressSize != 0) - RecoverableErrorCallback(createStringError( + uint8_t ExtractorAddressSize = TableData.getAddressSize(); + uint64_t OpcodeAddressSize = Len - 1; + if (ExtractorAddressSize != OpcodeAddressSize && + ExtractorAddressSize != 0) + RecoverableErrorHandler(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); - 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); + // address size. If the size is unsupported, give up trying to read + // the address and continue to the next opcode. + if (OpcodeAddressSize != 1 && OpcodeAddressSize != 2 && + OpcodeAddressSize != 4 && OpcodeAddressSize != 8) { + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "address size 0x%2.2" PRIx64 + " of DW_LNE_set_address opcode at offset 0x%8.8" PRIx64 + " is unsupported", + OpcodeAddressSize, ExtOffset)); + TableData.skip(Cursor, OpcodeAddressSize); + } else { + TableData.setAddressSize(OpcodeAddressSize); + State.Row.Address.Address = TableData.getRelocatedAddress( + Cursor, &State.Row.Address.SectionIndex); + + // Restore the address size if the extractor already had it. + if (ExtractorAddressSize != 0) + TableData.setAddressSize(ExtractorAddressSize); + } - if (OS) + if (Cursor && Verbose) *OS << format(" (0x%16.16" PRIx64 ")", State.Row.Address.Address); } break; @@ -654,14 +903,14 @@ Error DWARFDebugLine::LineTable::parse( // the file register of the state machine. { FileNameEntry FileEntry; - const char *Name = DebugLineData.getCStr(OffsetPtr); + const char *Name = TableData.getCStr(Cursor); FileEntry.Name = DWARFFormValue::createFromPValue(dwarf::DW_FORM_string, Name); - FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr); - FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); - FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); + FileEntry.DirIdx = TableData.getULEB128(Cursor); + FileEntry.ModTime = TableData.getULEB128(Cursor); + FileEntry.Length = TableData.getULEB128(Cursor); Prologue.FileNames.push_back(FileEntry); - if (OS) + if (Cursor && Verbose) *OS << " (" << Name << ", dir=" << FileEntry.DirIdx << ", mod_time=" << format("(0x%16.16" PRIx64 ")", FileEntry.ModTime) << ", length=" << FileEntry.Length << ")"; @@ -669,41 +918,63 @@ Error DWARFDebugLine::LineTable::parse( break; case DW_LNE_set_discriminator: - State.Row.Discriminator = DebugLineData.getULEB128(OffsetPtr); - if (OS) + State.Row.Discriminator = TableData.getULEB128(Cursor); + if (Cursor && Verbose) *OS << " (" << State.Row.Discriminator << ")"; break; default: - if (OS) + if (Cursor && Verbose) *OS << format("Unrecognized extended op 0x%02.02" PRIx8, SubOpcode) << format(" length %" PRIx64, Len); // Len doesn't include the zero opcode byte or the length itself, but // it does include the sub_opcode, so we have to adjust for that. - (*OffsetPtr) += Len - 1; + TableData.skip(Cursor, Len - 1); break; } - // Make sure the stated and parsed lengths are the same. - // Otherwise we have an unparseable line-number program. - if (*OffsetPtr - ExtOffset != Len) - return createStringError(errc::illegal_byte_sequence, - "unexpected line op length at offset 0x%8.8" PRIx64 - " expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx64, - ExtOffset, Len, *OffsetPtr - ExtOffset); + // Make sure the length as recorded in the table and the standard length + // for the opcode match. If they don't, continue from the end as claimed + // by the table. Similarly, continue from the claimed end in the event of + // a parsing error. + uint64_t End = ExtOffset + Len; + if (Cursor && Cursor.tell() != End) + RecoverableErrorHandler(createStringError( + errc::illegal_byte_sequence, + "unexpected line op length at offset 0x%8.8" PRIx64 + " expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx64, + ExtOffset, Len, Cursor.tell() - ExtOffset)); + if (!Cursor && Verbose) { + DWARFDataExtractor::Cursor ByteCursor(OperandOffset); + uint8_t Byte = TableData.getU8(ByteCursor); + if (ByteCursor) { + *OS << " (<parsing error>"; + do { + *OS << format(" %2.2" PRIx8, Byte); + Byte = TableData.getU8(ByteCursor); + } while (ByteCursor); + *OS << ")"; + } + + // The only parse failure in this case should be if the end was reached. + // In that case, throw away the error, as the main Cursor's error will + // be sufficient. + consumeError(ByteCursor.takeError()); + } + *OffsetPtr = End; } else if (Opcode < Prologue.OpcodeBase) { - if (OS) + if (Verbose) *OS << LNStandardString(Opcode); switch (Opcode) { // Standard Opcodes case DW_LNS_copy: // Takes no arguments. Append a row to the matrix using the // current values of the state-machine registers. - if (OS) { + if (Verbose) { *OS << "\n"; OS->indent(12); - State.Row.dump(*OS); - *OS << "\n"; } + if (OS) + State.Row.dump(*OS); State.appendRowToMatrix(); break; @@ -711,11 +982,11 @@ Error DWARFDebugLine::LineTable::parse( // Takes a single unsigned LEB128 operand, multiplies it by the // min_inst_length field of the prologue, and adds the // result to the address register of the state machine. - { + if (Optional<uint64_t> Operand = + parseULEB128<uint64_t>(TableData, Cursor)) { uint64_t AddrOffset = - DebugLineData.getULEB128(OffsetPtr) * Prologue.MinInstLength; - State.Row.Address.Address += AddrOffset; - if (OS) + State.advanceAddr(*Operand, Opcode, OpcodeOffset); + if (Verbose) *OS << " (" << AddrOffset << ")"; } break; @@ -723,25 +994,36 @@ Error DWARFDebugLine::LineTable::parse( case DW_LNS_advance_line: // Takes a single signed LEB128 operand and adds that value to // the line register of the state machine. - State.Row.Line += DebugLineData.getSLEB128(OffsetPtr); - if (OS) - *OS << " (" << State.Row.Line << ")"; + { + int64_t LineDelta = TableData.getSLEB128(Cursor); + if (Cursor) { + State.Row.Line += LineDelta; + if (Verbose) + *OS << " (" << State.Row.Line << ")"; + } + } break; case DW_LNS_set_file: // Takes a single unsigned LEB128 operand and stores it in the file // register of the state machine. - State.Row.File = DebugLineData.getULEB128(OffsetPtr); - if (OS) - *OS << " (" << State.Row.File << ")"; + if (Optional<uint16_t> File = + parseULEB128<uint16_t>(TableData, Cursor)) { + State.Row.File = *File; + if (Verbose) + *OS << " (" << State.Row.File << ")"; + } break; case DW_LNS_set_column: // Takes a single unsigned LEB128 operand and stores it in the // column register of the state machine. - State.Row.Column = DebugLineData.getULEB128(OffsetPtr); - if (OS) - *OS << " (" << State.Row.Column << ")"; + if (Optional<uint16_t> Column = + parseULEB128<uint16_t>(TableData, Cursor)) { + State.Row.Column = *Column; + if (Verbose) + *OS << " (" << State.Row.Column << ")"; + } break; case DW_LNS_negate_stmt: @@ -769,13 +1051,10 @@ Error DWARFDebugLine::LineTable::parse( // than twice that range will it need to use both DW_LNS_advance_pc // and a special opcode, requiring three or more bytes. { - uint8_t AdjustOpcode = 255 - Prologue.OpcodeBase; uint64_t AddrOffset = - (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; - State.Row.Address.Address += AddrOffset; - if (OS) - *OS - << format(" (0x%16.16" PRIx64 ")", AddrOffset); + State.advanceAddrForOpcode(Opcode, OpcodeOffset).AddrDelta; + if (Verbose) + *OS << format(" (0x%16.16" PRIx64 ")", AddrOffset); } break; @@ -790,11 +1069,13 @@ Error DWARFDebugLine::LineTable::parse( // requires the use of DW_LNS_advance_pc. Such assemblers, however, // can use DW_LNS_fixed_advance_pc instead, sacrificing compression. { - uint16_t PCOffset = DebugLineData.getRelocatedValue(2, OffsetPtr); - State.Row.Address.Address += PCOffset; - if (OS) - *OS - << format(" (0x%4.4" PRIx16 ")", PCOffset); + uint16_t PCOffset = + TableData.getRelocatedValue(Cursor, 2); + if (Cursor) { + State.Row.Address.Address += PCOffset; + if (Verbose) + *OS << format(" (0x%4.4" PRIx16 ")", PCOffset); + } } break; @@ -812,10 +1093,12 @@ Error DWARFDebugLine::LineTable::parse( case DW_LNS_set_isa: // Takes a single unsigned LEB128 operand and stores it in the - // column register of the state machine. - State.Row.Isa = DebugLineData.getULEB128(OffsetPtr); - if (OS) - *OS << " (" << (uint64_t)State.Row.Isa << ")"; + // ISA register of the state machine. + if (Optional<uint8_t> Isa = parseULEB128<uint8_t>(TableData, Cursor)) { + State.Row.Isa = *Isa; + if (Verbose) + *OS << " (" << (uint64_t)State.Row.Isa << ")"; + } break; default: @@ -824,73 +1107,72 @@ Error DWARFDebugLine::LineTable::parse( // as a multiple of LEB128 operands for each opcode. { assert(Opcode - 1U < Prologue.StandardOpcodeLengths.size()); + if (Verbose) + *OS << "Unrecognized standard opcode"; uint8_t OpcodeLength = Prologue.StandardOpcodeLengths[Opcode - 1]; + std::vector<uint64_t> Operands; for (uint8_t I = 0; I < OpcodeLength; ++I) { - uint64_t Value = DebugLineData.getULEB128(OffsetPtr); - if (OS) - *OS << format("Skipping ULEB128 value: 0x%16.16" PRIx64 ")\n", - Value); + if (Optional<uint64_t> Value = + parseULEB128<uint64_t>(TableData, Cursor)) + Operands.push_back(*Value); + else + break; + } + if (Verbose && !Operands.empty()) { + *OS << " (operands: "; + bool First = true; + for (uint64_t Value : Operands) { + if (!First) + *OS << ", "; + First = false; + *OS << format("0x%16.16" PRIx64, Value); + } + if (Verbose) + *OS << ')'; } } break; } + + *OffsetPtr = Cursor.tell(); } else { - // Special Opcodes - - // A special opcode value is chosen based on the amount that needs - // to be added to the line and address registers. The maximum line - // increment for a special opcode is the value of the line_base - // field in the header, plus the value of the line_range field, - // minus 1 (line base + line range - 1). If the desired line - // increment is greater than the maximum line increment, a standard - // opcode must be used instead of a special opcode. The "address - // advance" is calculated by dividing the desired address increment - // by the minimum_instruction_length field from the header. The - // special opcode is then calculated using the following formula: - // - // opcode = (desired line increment - line_base) + - // (line_range * address advance) + opcode_base - // - // If the resulting opcode is greater than 255, a standard opcode - // must be used instead. - // - // To decode a special opcode, subtract the opcode_base from the - // opcode itself to give the adjusted opcode. The amount to - // increment the address register is the result of the adjusted - // opcode divided by the line_range multiplied by the - // minimum_instruction_length field from the header. That is: - // - // address increment = (adjusted opcode / line_range) * - // minimum_instruction_length - // - // The amount to increment the line register is the line_base plus - // the result of the adjusted opcode modulo the line_range. That is: - // - // line increment = line_base + (adjusted opcode % line_range) - - uint8_t AdjustOpcode = Opcode - Prologue.OpcodeBase; - uint64_t AddrOffset = - (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; - int32_t LineOffset = - Prologue.LineBase + (AdjustOpcode % Prologue.LineRange); - State.Row.Line += LineOffset; - State.Row.Address.Address += AddrOffset; - - if (OS) { - *OS << "address += " << AddrOffset << ", line += " << LineOffset + // Special Opcodes. + ParsingState::AddrAndLineDelta Delta = + State.handleSpecialOpcode(Opcode, OpcodeOffset); + + if (Verbose) { + *OS << "address += " << Delta.Address << ", line += " << Delta.Line << "\n"; OS->indent(12); - State.Row.dump(*OS); } + if (OS) + State.Row.dump(*OS); State.appendRowToMatrix(); + *OffsetPtr = Cursor.tell(); } - if(OS) + + // When a row is added to the matrix, it is also dumped, which includes a + // new line already, so don't add an extra one. + if (Verbose && Rows.size() == RowCount) *OS << "\n"; + + // Most parse failures other than when parsing extended opcodes are due to + // failures to read ULEBs. Bail out of parsing, since we don't know where to + // continue reading from as there is no stated length for such byte + // sequences. Print the final trailing new line if needed before doing so. + if (!Cursor && Opcode != 0) { + if (Verbose) + *OS << "\n"; + return Cursor.takeError(); + } + + if (!Cursor) + RecoverableErrorHandler(Cursor.takeError()); } if (!State.Sequence.Empty) - RecoverableErrorCallback(createStringError( + RecoverableErrorHandler(createStringError( errc::illegal_byte_sequence, "last sequence in debug line table at offset 0x%8.8" PRIx64 " is not terminated", @@ -907,6 +1189,11 @@ Error DWARFDebugLine::LineTable::parse( // rudimentary sequences for address ranges [0x0, 0xsomething). } + // Terminate the table with a final blank line to clearly delineate it from + // later dumps. + if (OS) + *OS << "\n"; + return Error::success(); } @@ -1054,9 +1341,13 @@ bool DWARFDebugLine::Prologue::getFileNameByIndex( if (!Name) return false; StringRef FileName = *Name; - if (Kind != FileLineInfoKind::AbsoluteFilePath || + if (Kind == FileLineInfoKind::RawValue || isPathAbsoluteOnWindowsOrPosix(FileName)) { - Result = FileName; + Result = std::string(FileName); + return true; + } + if (Kind == FileLineInfoKind::BaseNameOnly) { + Result = std::string(llvm::sys::path::filename(FileName)); return true; } @@ -1064,23 +1355,31 @@ bool DWARFDebugLine::Prologue::getFileNameByIndex( StringRef IncludeDir; // Be defensive about the contents of Entry. if (getVersion() >= 5) { - if (Entry.DirIdx < IncludeDirectories.size()) + // DirIdx 0 is the compilation directory, so don't include it for + // relative names. + if ((Entry.DirIdx != 0 || Kind != FileLineInfoKind::RelativeFilePath) && + Entry.DirIdx < IncludeDirectories.size()) IncludeDir = IncludeDirectories[Entry.DirIdx].getAsCString().getValue(); } else { if (0 < Entry.DirIdx && Entry.DirIdx <= IncludeDirectories.size()) IncludeDir = IncludeDirectories[Entry.DirIdx - 1].getAsCString().getValue(); - - // We may still need to append compilation directory of compile unit. - // We know that FileName is not absolute, the only way to have an - // absolute path at this point would be if IncludeDir is absolute. - if (!CompDir.empty() && !isPathAbsoluteOnWindowsOrPosix(IncludeDir)) - sys::path::append(FilePath, Style, CompDir); } + // For absolute paths only, include the compilation directory of compile unit. + // We know that FileName is not absolute, the only way to have an absolute + // path at this point would be if IncludeDir is absolute. + if (Kind == FileLineInfoKind::AbsoluteFilePath && !CompDir.empty() && + !isPathAbsoluteOnWindowsOrPosix(IncludeDir)) + sys::path::append(FilePath, Style, CompDir); + + assert((Kind == FileLineInfoKind::AbsoluteFilePath || + Kind == FileLineInfoKind::RelativeFilePath) && + "invalid FileLineInfo Kind"); + // sys::path::append skips empty strings. sys::path::append(FilePath, Style, IncludeDir, FileName); - Result = FilePath.str(); + Result = std::string(FilePath.str()); return true; } @@ -1131,34 +1430,36 @@ DWARFDebugLine::SectionParser::SectionParser(DWARFDataExtractor &Data, } bool DWARFDebugLine::Prologue::totalLengthIsValid() const { - return TotalLength == dwarf::DW_LENGTH_DWARF64 || - TotalLength < dwarf::DW_LENGTH_lo_reserved; + return TotalLength != 0u; } DWARFDebugLine::LineTable DWARFDebugLine::SectionParser::parseNext( - function_ref<void(Error)> RecoverableErrorCallback, - function_ref<void(Error)> UnrecoverableErrorCallback, raw_ostream *OS) { + function_ref<void(Error)> RecoverableErrorHandler, + function_ref<void(Error)> UnrecoverableErrorHandler, raw_ostream *OS, + bool Verbose) { assert(DebugLineData.isValidOffset(Offset) && "parsing should have terminated"); DWARFUnit *U = prepareToParse(Offset); uint64_t OldOffset = Offset; LineTable LT; if (Error Err = LT.parse(DebugLineData, &Offset, Context, U, - RecoverableErrorCallback, OS)) - UnrecoverableErrorCallback(std::move(Err)); + RecoverableErrorHandler, OS, Verbose)) + UnrecoverableErrorHandler(std::move(Err)); moveToNextTable(OldOffset, LT.Prologue); return LT; } void DWARFDebugLine::SectionParser::skip( - function_ref<void(Error)> ErrorCallback) { + function_ref<void(Error)> RecoverableErrorHandler, + function_ref<void(Error)> UnrecoverableErrorHandler) { assert(DebugLineData.isValidOffset(Offset) && "parsing should have terminated"); DWARFUnit *U = prepareToParse(Offset); uint64_t OldOffset = Offset; LineTable LT; - if (Error Err = LT.Prologue.parse(DebugLineData, &Offset, Context, U)) - ErrorCallback(std::move(Err)); + if (Error Err = LT.Prologue.parse(DebugLineData, &Offset, + RecoverableErrorHandler, Context, U)) + UnrecoverableErrorHandler(std::move(Err)); moveToNextTable(OldOffset, LT.Prologue); } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp index 0c5f9a9c54ec6..f381263644012 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -106,16 +106,15 @@ DWARFLocationInterpreter::Interpret(const DWARFLocationEntry &E) { } } -// 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 -// expression that LLVM doesn't produce. Guessing the wrong version means we -// won't be able to pretty print expressions in DWARF2 binaries produced by -// non-LLVM tools. static void dumpExpression(raw_ostream &OS, ArrayRef<uint8_t> Data, bool IsLittleEndian, unsigned AddressSize, const MCRegisterInfo *MRI, DWARFUnit *U) { - DWARFDataExtractor Extractor(toStringRef(Data), IsLittleEndian, AddressSize); - DWARFExpression(Extractor, dwarf::DWARF_VERSION, AddressSize).print(OS, MRI, U); + DWARFDataExtractor Extractor(Data, IsLittleEndian, AddressSize); + // Note. We do not pass any format to DWARFExpression, even if the + // corresponding unit is known. For now, there is only one operation, + // DW_OP_call_ref, which depends on the format; it is rarely used, and + // is unexpected in location tables. + DWARFExpression(Extractor, AddressSize).print(OS, MRI, U); } bool DWARFLocationTable::dumpLocationList(uint64_t *Offset, raw_ostream &OS, @@ -161,9 +160,7 @@ bool DWARFLocationTable::dumpLocationList(uint64_t *Offset, raw_ostream &OS, return true; }); if (E) { - OS << "\n"; - OS.indent(Indent); - OS << "error: " << toString(std::move(E)); + DumpOpts.RecoverableErrorHandler(std::move(E)); return false; } return true; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp index 8cb259ebc6222..f920d69cc43f2 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugMacro.cpp @@ -8,6 +8,8 @@ #include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" #include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <cstdint> @@ -15,10 +17,32 @@ using namespace llvm; using namespace dwarf; +DwarfFormat DWARFDebugMacro::MacroHeader::getDwarfFormat() const { + return Flags & MACRO_OFFSET_SIZE ? DWARF64 : DWARF32; +} + +uint8_t DWARFDebugMacro::MacroHeader::getOffsetByteSize() const { + return getDwarfOffsetByteSize(getDwarfFormat()); +} + +void DWARFDebugMacro::MacroHeader::dumpMacroHeader(raw_ostream &OS) const { + // FIXME: Add support for dumping opcode_operands_table + OS << format("macro header: version = 0x%04" PRIx16, Version) + << format(", flags = 0x%02" PRIx8, Flags) + << ", format = " << FormatString(getDwarfFormat()); + if (Flags & MACRO_DEBUG_LINE_OFFSET) + OS << format(", debug_line_offset = 0x%0*" PRIx64, 2 * getOffsetByteSize(), + DebugLineOffset); + OS << "\n"; +} + void DWARFDebugMacro::dump(raw_ostream &OS) const { unsigned IndLevel = 0; for (const auto &Macros : MacroLists) { - for (const Entry &E : Macros) { + OS << format("0x%08" PRIx64 ":\n", Macros.Offset); + if (Macros.Header.Version >= 5) + Macros.Header.dumpMacroHeader(OS); + for (const Entry &E : Macros.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) @@ -27,22 +51,40 @@ void DWARFDebugMacro::dump(raw_ostream &OS) const { for (unsigned I = 0; I < IndLevel; I++) OS << " "; IndLevel += (E.Type == DW_MACINFO_start_file); - - WithColor(OS, HighlightColor::Macro).get() << MacinfoString(E.Type); + // Based on which version we are handling choose appropriate macro forms. + if (Macros.Header.Version >= 5) + WithColor(OS, HighlightColor::Macro).get() << MacroString(E.Type); + else + WithColor(OS, HighlightColor::Macro).get() << MacinfoString(E.Type); switch (E.Type) { default: - // Got a corrupted ".debug_macinfo" section (invalid macinfo type). + // Got a corrupted ".debug_macinfo/.debug_macro" section (invalid + // macinfo type). break; - case DW_MACINFO_define: - case DW_MACINFO_undef: + // debug_macro and debug_macinfo share some common encodings. + // DW_MACRO_define == DW_MACINFO_define + // DW_MACRO_undef == DW_MACINFO_undef + // DW_MACRO_start_file == DW_MACINFO_start_file + // DW_MACRO_end_file == DW_MACINFO_end_file + // For readability/uniformity we are using DW_MACRO_*. + case DW_MACRO_define: + case DW_MACRO_undef: + case DW_MACRO_define_strp: + case DW_MACRO_undef_strp: + case DW_MACRO_define_strx: + case DW_MACRO_undef_strx: OS << " - lineno: " << E.Line; OS << " macro: " << E.MacroStr; break; - case DW_MACINFO_start_file: + case DW_MACRO_start_file: OS << " - lineno: " << E.Line; OS << " filenum: " << E.File; break; - case DW_MACINFO_end_file: + case DW_MACRO_import: + OS << format(" - import offset: 0x%0*" PRIx64, + 2 * Macros.Header.getOffsetByteSize(), E.ImportOffset); + break; + case DW_MACRO_end_file: break; case DW_MACINFO_vendor_ext: OS << " - constant: " << E.ExtConstant; @@ -51,26 +93,46 @@ void DWARFDebugMacro::dump(raw_ostream &OS) const { } OS << "\n"; } - OS << "\n"; } } -void DWARFDebugMacro::parse(DataExtractor data) { +Error DWARFDebugMacro::parseImpl( + Optional<DWARFUnitVector::iterator_range> Units, + Optional<DataExtractor> StringExtractor, DWARFDataExtractor Data, + bool IsMacro) { uint64_t Offset = 0; MacroList *M = nullptr; - while (data.isValidOffset(Offset)) { + using MacroToUnitsMap = DenseMap<uint64_t, DWARFUnit *>; + MacroToUnitsMap MacroToUnits; + if (IsMacro && Data.isValidOffset(Offset)) { + // Keep a mapping from Macro contribution to CUs, this will + // be needed while retrieving macro from DW_MACRO_define_strx form. + for (const auto &U : Units.getValue()) + if (auto CUDIE = U->getUnitDIE()) + // Skip units which does not contibutes to macro section. + if (auto MacroOffset = toSectionOffset(CUDIE.find(DW_AT_macros))) + MacroToUnits.try_emplace(*MacroOffset, U.get()); + } + while (Data.isValidOffset(Offset)) { if (!M) { MacroLists.emplace_back(); M = &MacroLists.back(); + M->Offset = Offset; + if (IsMacro) { + auto Err = M->Header.parseMacroHeader(Data, &Offset); + if (Err) + return Err; + } } // A macro list entry consists of: - M->emplace_back(); - Entry &E = M->back(); + M->Macros.emplace_back(); + Entry &E = M->Macros.back(); // 1. Macinfo type - E.Type = data.getULEB128(&Offset); + E.Type = Data.getULEB128(&Offset); if (E.Type == 0) { - // Reached end of a ".debug_macinfo" section contribution. + // Reached end of a ".debug_macinfo/debug_macro" section contribution. + M = nullptr; continue; } @@ -79,28 +141,99 @@ 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; - return; - case DW_MACINFO_define: - case DW_MACINFO_undef: + return Error::success(); + // debug_macro and debug_macinfo share some common encodings. + // DW_MACRO_define == DW_MACINFO_define + // DW_MACRO_undef == DW_MACINFO_undef + // DW_MACRO_start_file == DW_MACINFO_start_file + // DW_MACRO_end_file == DW_MACINFO_end_file + // For readibility/uniformity we are using DW_MACRO_*. + case DW_MACRO_define: + case DW_MACRO_undef: + // 2. Source line + E.Line = Data.getULEB128(&Offset); + // 3. Macro string + E.MacroStr = Data.getCStr(&Offset); + break; + case DW_MACRO_define_strp: + case DW_MACRO_undef_strp: { + if (!IsMacro) { + // DW_MACRO_define_strp is a new form introduced in DWARFv5, it is + // not supported in debug_macinfo[.dwo] sections. Assume it as an + // invalid entry, push it and halt parsing. + E.Type = DW_MACINFO_invalid; + return Error::success(); + } + uint64_t StrOffset = 0; // 2. Source line - E.Line = data.getULEB128(&Offset); + E.Line = Data.getULEB128(&Offset); // 3. Macro string - E.MacroStr = data.getCStr(&Offset); + StrOffset = + Data.getRelocatedValue(M->Header.getOffsetByteSize(), &Offset); + assert(StringExtractor && "String Extractor not found"); + E.MacroStr = StringExtractor->getCStr(&StrOffset); + break; + } + case DW_MACRO_define_strx: + case DW_MACRO_undef_strx: { + if (!IsMacro) { + // DW_MACRO_define_strx is a new form introduced in DWARFv5, it is + // not supported in debug_macinfo[.dwo] sections. Assume it as an + // invalid entry, push it and halt parsing. + E.Type = DW_MACINFO_invalid; + return Error::success(); + } + E.Line = Data.getULEB128(&Offset); + auto MacroContributionOffset = MacroToUnits.find(M->Offset); + if (MacroContributionOffset == MacroToUnits.end()) + return createStringError(errc::invalid_argument, + "Macro contribution of the unit not found"); + Optional<uint64_t> StrOffset = + MacroContributionOffset->second->getStringOffsetSectionItem( + Data.getULEB128(&Offset)); + if (!StrOffset) + return createStringError( + errc::invalid_argument, + "String offsets contribution of the unit not found"); + E.MacroStr = + MacroContributionOffset->second->getStringExtractor().getCStr( + &*StrOffset); break; - case DW_MACINFO_start_file: + } + case DW_MACRO_start_file: // 2. Source line - E.Line = data.getULEB128(&Offset); + E.Line = Data.getULEB128(&Offset); // 3. Source file id - E.File = data.getULEB128(&Offset); + E.File = Data.getULEB128(&Offset); + break; + case DW_MACRO_end_file: break; - case DW_MACINFO_end_file: + case DW_MACRO_import: + E.ImportOffset = + Data.getRelocatedValue(M->Header.getOffsetByteSize(), &Offset); break; case DW_MACINFO_vendor_ext: // 2. Vendor extension constant - E.ExtConstant = data.getULEB128(&Offset); + E.ExtConstant = Data.getULEB128(&Offset); // 3. Vendor extension string - E.ExtStr = data.getCStr(&Offset); + E.ExtStr = Data.getCStr(&Offset); break; } } + return Error::success(); +} + +Error DWARFDebugMacro::MacroHeader::parseMacroHeader(DWARFDataExtractor Data, + uint64_t *Offset) { + Version = Data.getU16(Offset); + uint8_t FlagData = Data.getU8(Offset); + + // FIXME: Add support for parsing opcode_operands_table + if (FlagData & MACRO_OPCODE_OPERANDS_TABLE) + return createStringError(errc::not_supported, + "opcode_operands_table is not supported"); + Flags = FlagData; + if (Flags & MACRO_DEBUG_LINE_OFFSET) + DebugLineOffset = Data.getUnsigned(Offset, getOffsetByteSize()); + return Error::success(); } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp index ab71b239cb679..5031acdb54efc 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugPubTable.cpp @@ -11,6 +11,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include <cstdint> @@ -18,44 +19,92 @@ using namespace llvm; using namespace dwarf; -DWARFDebugPubTable::DWARFDebugPubTable(const DWARFObject &Obj, - const DWARFSection &Sec, - bool LittleEndian, bool GnuStyle) - : GnuStyle(GnuStyle) { - DWARFDataExtractor PubNames(Obj, Sec, LittleEndian, 0); +void DWARFDebugPubTable::extract( + DWARFDataExtractor Data, bool GnuStyle, + function_ref<void(Error)> RecoverableErrorHandler) { + this->GnuStyle = GnuStyle; + Sets.clear(); uint64_t Offset = 0; - while (PubNames.isValidOffset(Offset)) { + while (Data.isValidOffset(Offset)) { + uint64_t SetOffset = Offset; Sets.push_back({}); - Set &SetData = Sets.back(); + Set &NewSet = Sets.back(); - SetData.Length = PubNames.getU32(&Offset); - SetData.Version = PubNames.getU16(&Offset); - SetData.Offset = PubNames.getRelocatedValue(4, &Offset); - SetData.Size = PubNames.getU32(&Offset); + DataExtractor::Cursor C(Offset); + std::tie(NewSet.Length, NewSet.Format) = Data.getInitialLength(C); + if (!C) { + // Drop the newly added set because it does not contain anything useful + // to dump. + Sets.pop_back(); + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "name lookup table at offset 0x%" PRIx64 " parsing failed: %s", + SetOffset, toString(C.takeError()).c_str())); + return; + } + + Offset = C.tell() + NewSet.Length; + DWARFDataExtractor SetData(Data, Offset); + const unsigned OffsetSize = dwarf::getDwarfOffsetByteSize(NewSet.Format); - while (Offset < Sec.Data.size()) { - uint32_t DieRef = PubNames.getU32(&Offset); + NewSet.Version = SetData.getU16(C); + NewSet.Offset = SetData.getRelocatedValue(C, OffsetSize); + NewSet.Size = SetData.getUnsigned(C, OffsetSize); + + if (!C) { + // Preserve the newly added set because at least some fields of the header + // are read and can be dumped. + RecoverableErrorHandler( + createStringError(errc::invalid_argument, + "name lookup table at offset 0x%" PRIx64 + " does not have a complete header: %s", + SetOffset, toString(C.takeError()).c_str())); + continue; + } + + while (C) { + uint64_t DieRef = SetData.getUnsigned(C, OffsetSize); if (DieRef == 0) break; - uint8_t IndexEntryValue = GnuStyle ? PubNames.getU8(&Offset) : 0; - StringRef Name = PubNames.getCStrRef(&Offset); - SetData.Entries.push_back( - {DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name}); + uint8_t IndexEntryValue = GnuStyle ? SetData.getU8(C) : 0; + StringRef Name = SetData.getCStrRef(C); + if (C) + NewSet.Entries.push_back( + {DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name}); + } + + if (!C) { + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "name lookup table at offset 0x%" PRIx64 " parsing failed: %s", + SetOffset, toString(C.takeError()).c_str())); + continue; } + if (C.tell() != Offset) + RecoverableErrorHandler(createStringError( + errc::invalid_argument, + "name lookup table at offset 0x%" PRIx64 + " has a terminator at offset 0x%" PRIx64 + " before the expected end at 0x%" PRIx64, + SetOffset, C.tell() - OffsetSize, Offset - OffsetSize)); } } void DWARFDebugPubTable::dump(raw_ostream &OS) const { for (const Set &S : Sets) { - OS << "length = " << format("0x%08x", S.Length); - OS << " version = " << format("0x%04x", S.Version); - OS << " unit_offset = " << format("0x%08" PRIx64, S.Offset); - OS << " unit_size = " << format("0x%08x", S.Size) << '\n'; + int OffsetDumpWidth = 2 * dwarf::getDwarfOffsetByteSize(S.Format); + OS << "length = " << format("0x%0*" PRIx64, OffsetDumpWidth, S.Length); + OS << ", format = " << dwarf::FormatString(S.Format); + OS << ", version = " << format("0x%04x", S.Version); + OS << ", unit_offset = " + << format("0x%0*" PRIx64, OffsetDumpWidth, S.Offset); + OS << ", unit_size = " << format("0x%0*" PRIx64, OffsetDumpWidth, S.Size) + << '\n'; OS << (GnuStyle ? "Offset Linkage Kind Name\n" : "Offset Name\n"); for (const Entry &E : S.Entries) { - OS << format("0x%8.8" PRIx64 " ", E.SecOffset); + OS << format("0x%0*" PRIx64 " ", OffsetDumpWidth, E.SecOffset); if (GnuStyle) { StringRef EntryLinkage = GDBIndexEntryLinkageString(E.Descriptor.Linkage); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp index c1dc3b68c6ab2..81a6b5dcd5e7e 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -79,7 +79,7 @@ static void dumpLocation(raw_ostream &OS, DWARFFormValue &FormValue, ArrayRef<uint8_t> Expr = *FormValue.getAsBlock(); DataExtractor Data(StringRef((const char *)Expr.data(), Expr.size()), Ctx.isLittleEndian(), 0); - DWARFExpression(Data, U->getVersion(), U->getAddressByteSize()) + DWARFExpression(Data, U->getAddressByteSize(), U->getFormParams().Format) .print(OS, MRI, U); return; } @@ -317,8 +317,9 @@ static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, dumpRanges(Obj, OS, RangesOrError.get(), U->getAddressByteSize(), sizeof(BaseIndent) + Indent + 4, DumpOpts); else - WithColor::error() << "decoding address ranges: " - << toString(RangesOrError.takeError()) << '\n'; + DumpOpts.RecoverableErrorHandler(createStringError( + errc::invalid_argument, "decoding address ranges: %s", + toString(RangesOrError.takeError()).c_str())); } OS << ")\n"; @@ -356,7 +357,7 @@ DWARFDie::find(ArrayRef<dwarf::Attribute> Attrs) const { Optional<DWARFFormValue> DWARFDie::findRecursively(ArrayRef<dwarf::Attribute> Attrs) const { - std::vector<DWARFDie> Worklist; + SmallVector<DWARFDie, 3> Worklist; Worklist.push_back(*this); // Keep track if DIEs already seen to prevent infinite recursion. @@ -531,14 +532,26 @@ const char *DWARFDie::getName(DINameKind Kind) const { return nullptr; // Try to get mangled name only if it was asked for. if (Kind == DINameKind::LinkageName) { - if (auto Name = dwarf::toString( - findRecursively({DW_AT_MIPS_linkage_name, DW_AT_linkage_name}), - nullptr)) + if (auto Name = getLinkageName()) return Name; } - if (auto Name = dwarf::toString(findRecursively(DW_AT_name), nullptr)) - return Name; - return nullptr; + return getShortName(); +} + +const char *DWARFDie::getShortName() const { + if (!isValid()) + return nullptr; + + return dwarf::toString(findRecursively(dwarf::DW_AT_name), nullptr); +} + +const char *DWARFDie::getLinkageName() const { + if (!isValid()) + return nullptr; + + return dwarf::toString(findRecursively({dwarf::DW_AT_MIPS_linkage_name, + dwarf::DW_AT_linkage_name}), + nullptr); } uint64_t DWARFDie::getDeclLine() const { diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp index 7d817d8a99257..de5e11e084f47 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -8,7 +8,6 @@ #include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" -#include "llvm/BinaryFormat/Dwarf.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/Support/Format.h" #include <cassert> @@ -94,7 +93,7 @@ static DescVector getDescriptions() { 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); + Desc(Op::Dwarf4, Op::SizeLEB, Op::WasmLocationArg); 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); @@ -103,6 +102,8 @@ static DescVector getDescriptions() { Descriptions[DW_OP_convert] = Desc(Op::Dwarf5, Op::BaseTypeRef); Descriptions[DW_OP_entry_value] = Desc(Op::Dwarf5, Op::SizeLEB); + Descriptions[DW_OP_regval_type] = + Desc(Op::Dwarf5, Op::SizeLEB, Op::BaseTypeRef); return Descriptions; } @@ -116,19 +117,15 @@ static DWARFExpression::Operation::Description getOpDesc(unsigned OpCode) { return Descriptions[OpCode]; } -static uint8_t getRefAddrSize(uint8_t AddrSize, uint16_t Version) { - return (Version == 2) ? AddrSize : 4; -} - -bool DWARFExpression::Operation::extract(DataExtractor Data, uint16_t Version, - uint8_t AddressSize, uint64_t Offset) { +bool DWARFExpression::Operation::extract(DataExtractor Data, + uint8_t AddressSize, uint64_t Offset, + Optional<DwarfFormat> Format) { + EndOffset = Offset; Opcode = Data.getU8(&Offset); Desc = getOpDesc(Opcode); - if (Desc.Version == Operation::DwarfNA) { - EndOffset = Offset; + if (Desc.Version == Operation::DwarfNA) return false; - } for (unsigned Operand = 0; Operand < 2; ++Operand) { unsigned Size = Desc.Op[Operand]; @@ -157,24 +154,13 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, uint16_t Version, Operands[Operand] = Data.getU64(&Offset); break; case Operation::SizeAddr: - if (AddressSize == 8) { - Operands[Operand] = Data.getU64(&Offset); - } else if (AddressSize == 4) { - Operands[Operand] = Data.getU32(&Offset); - } else { - assert(AddressSize == 2); - Operands[Operand] = Data.getU16(&Offset); - } + Operands[Operand] = Data.getUnsigned(&Offset, AddressSize); break; case Operation::SizeRefAddr: - if (getRefAddrSize(AddressSize, Version) == 8) { - Operands[Operand] = Data.getU64(&Offset); - } else if (getRefAddrSize(AddressSize, Version) == 4) { - Operands[Operand] = Data.getU32(&Offset); - } else { - assert(getRefAddrSize(AddressSize, Version) == 2); - Operands[Operand] = Data.getU16(&Offset); - } + if (!Format) + return false; + Operands[Operand] = + Data.getUnsigned(&Offset, dwarf::getDwarfOffsetByteSize(*Format)); break; case Operation::SizeLEB: if (Signed) @@ -185,6 +171,19 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, uint16_t Version, case Operation::BaseTypeRef: Operands[Operand] = Data.getULEB128(&Offset); break; + case Operation::WasmLocationArg: + assert(Operand == 1); + switch (Operands[0]) { + case 0: case 1: case 2: + Operands[Operand] = Data.getULEB128(&Offset); + break; + case 3: // global as uint32 + Operands[Operand] = Data.getU32(&Offset); + break; + default: + return false; // Unknown Wasm location + } + break; case Operation::SizeBlock: // We need a size, so this cannot be the first operand if (Operand == 0) @@ -204,7 +203,21 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, uint16_t Version, return true; } -static bool prettyPrintRegisterOp(raw_ostream &OS, uint8_t Opcode, +static void prettyPrintBaseTypeRef(DWARFUnit *U, raw_ostream &OS, + uint64_t Operands[2], unsigned Operand) { + assert(Operand < 2 && "operand out of bounds"); + auto Die = U->getDIEForOffset(U->getOffset() + Operands[Operand]); + if (Die && Die.getTag() == dwarf::DW_TAG_base_type) { + OS << format(" (0x%08" PRIx64 ")", U->getOffset() + Operands[Operand]); + if (auto Name = Die.find(dwarf::DW_AT_name)) + OS << " \"" << Name->getAsCString() << "\""; + } else { + OS << format(" <invalid base_type ref: 0x%" PRIx64 ">", + Operands[Operand]); + } +} + +static bool prettyPrintRegisterOp(DWARFUnit *U, raw_ostream &OS, uint8_t Opcode, uint64_t Operands[2], const MCRegisterInfo *MRI, bool isEH) { if (!MRI) @@ -213,7 +226,8 @@ static bool prettyPrintRegisterOp(raw_ostream &OS, uint8_t Opcode, uint64_t DwarfRegNum; unsigned OpNum = 0; - if (Opcode == DW_OP_bregx || Opcode == DW_OP_regx) + if (Opcode == DW_OP_bregx || Opcode == DW_OP_regx || + Opcode == DW_OP_regval_type) DwarfRegNum = Operands[OpNum++]; else if (Opcode >= DW_OP_breg0 && Opcode < DW_OP_bregx) DwarfRegNum = Opcode - DW_OP_breg0; @@ -227,6 +241,9 @@ static bool prettyPrintRegisterOp(raw_ostream &OS, uint8_t Opcode, OS << format(" %s%+" PRId64, RegName, Operands[OpNum]); else OS << ' ' << RegName; + + if (Opcode == DW_OP_regval_type) + prettyPrintBaseTypeRef(U, OS, Operands, 1); return true; } } @@ -250,8 +267,9 @@ bool DWARFExpression::Operation::print(raw_ostream &OS, if ((Opcode >= DW_OP_breg0 && Opcode <= DW_OP_breg31) || (Opcode >= DW_OP_reg0 && Opcode <= DW_OP_reg31) || - Opcode == DW_OP_bregx || Opcode == DW_OP_regx) - if (prettyPrintRegisterOp(OS, Opcode, Operands, RegInfo, isEH)) + Opcode == DW_OP_bregx || Opcode == DW_OP_regx || + Opcode == DW_OP_regval_type) + if (prettyPrintRegisterOp(U, OS, Opcode, Operands, RegInfo, isEH)) return true; for (unsigned Operand = 0; Operand < 2; ++Operand) { @@ -262,14 +280,21 @@ bool DWARFExpression::Operation::print(raw_ostream &OS, break; if (Size == Operation::BaseTypeRef && U) { - auto Die = U->getDIEForOffset(U->getOffset() + Operands[Operand]); - if (Die && Die.getTag() == dwarf::DW_TAG_base_type) { - OS << format(" (0x%08" PRIx64 ")", U->getOffset() + Operands[Operand]); - if (auto Name = Die.find(dwarf::DW_AT_name)) - OS << " \"" << Name->getAsCString() << "\""; - } else { - OS << format(" <invalid base_type ref: 0x%" PRIx64 ">", - Operands[Operand]); + // For DW_OP_convert the operand may be 0 to indicate that conversion to + // the generic type should be done. The same holds for DW_OP_reinterpret, + // which is currently not supported. + if (Opcode == DW_OP_convert && Operands[Operand] == 0) + OS << " 0x0"; + else + prettyPrintBaseTypeRef(U, OS, Operands, Operand); + } else if (Size == Operation::WasmLocationArg) { + assert(Operand == 1); + switch (Operands[0]) { + case 0: case 1: case 2: + case 3: // global as uint32 + OS << format(" 0x%" PRIx64, Operands[Operand]); + break; + default: assert(false); } } else if (Size == Operation::SizeBlock) { uint64_t Offset = Operands[Operand]; @@ -324,6 +349,12 @@ bool DWARFExpression::Operation::verify(DWARFUnit *U) { break; if (Size == Operation::BaseTypeRef) { + // For DW_OP_convert the operand may be 0 to indicate that conversion to + // the generic type should be done, so don't look up a base type in that + // case. The same holds for DW_OP_reinterpret, which is currently not + // supported. + if (Opcode == DW_OP_convert && Operands[Operand] == 0) + continue; auto Die = U->getDIEForOffset(U->getOffset() + Operands[Operand]); if (!Die || Die.getTag() != dwarf::DW_TAG_base_type) { Error = true; @@ -343,4 +374,126 @@ bool DWARFExpression::verify(DWARFUnit *U) { return true; } +/// A user-facing string representation of a DWARF expression. This might be an +/// Address expression, in which case it will be implicitly dereferenced, or a +/// Value expression. +struct PrintedExpr { + enum ExprKind { + Address, + Value, + }; + ExprKind Kind; + SmallString<16> String; + + PrintedExpr(ExprKind K = Address) : Kind(K) {} +}; + +static bool printCompactDWARFExpr(raw_ostream &OS, DWARFExpression::iterator I, + const DWARFExpression::iterator E, + const MCRegisterInfo &MRI) { + SmallVector<PrintedExpr, 4> Stack; + + while (I != E) { + DWARFExpression::Operation &Op = *I; + uint8_t Opcode = Op.getCode(); + switch (Opcode) { + case dwarf::DW_OP_regx: { + // DW_OP_regx: A register, with the register num given as an operand. + // Printed as the plain register name. + uint64_t DwarfRegNum = Op.getRawOperand(0); + Optional<unsigned> LLVMRegNum = MRI.getLLVMRegNum(DwarfRegNum, false); + if (!LLVMRegNum) { + OS << "<unknown register " << DwarfRegNum << ">"; + return false; + } + raw_svector_ostream S(Stack.emplace_back(PrintedExpr::Value).String); + S << MRI.getName(*LLVMRegNum); + break; + } + case dwarf::DW_OP_bregx: { + int DwarfRegNum = Op.getRawOperand(0); + int64_t Offset = Op.getRawOperand(1); + Optional<unsigned> LLVMRegNum = MRI.getLLVMRegNum(DwarfRegNum, false); + if (!LLVMRegNum) { + OS << "<unknown register " << DwarfRegNum << ">"; + return false; + } + raw_svector_ostream S(Stack.emplace_back().String); + S << MRI.getName(*LLVMRegNum); + if (Offset) + S << format("%+" PRId64, Offset); + break; + } + case dwarf::DW_OP_entry_value: + case dwarf::DW_OP_GNU_entry_value: { + // DW_OP_entry_value contains a sub-expression which must be rendered + // separately. + uint64_t SubExprLength = Op.getRawOperand(0); + DWARFExpression::iterator SubExprEnd = I.skipBytes(SubExprLength); + ++I; + raw_svector_ostream S(Stack.emplace_back().String); + S << "entry("; + printCompactDWARFExpr(S, I, SubExprEnd, MRI); + S << ")"; + I = SubExprEnd; + continue; + } + case dwarf::DW_OP_stack_value: { + // The top stack entry should be treated as the actual value of tne + // variable, rather than the address of the variable in memory. + assert(!Stack.empty()); + Stack.back().Kind = PrintedExpr::Value; + break; + } + default: + if (Opcode >= dwarf::DW_OP_reg0 && Opcode <= dwarf::DW_OP_reg31) { + // DW_OP_reg<N>: A register, with the register num implied by the + // opcode. Printed as the plain register name. + uint64_t DwarfRegNum = Opcode - dwarf::DW_OP_reg0; + Optional<unsigned> LLVMRegNum = MRI.getLLVMRegNum(DwarfRegNum, false); + if (!LLVMRegNum) { + OS << "<unknown register " << DwarfRegNum << ">"; + return false; + } + raw_svector_ostream S(Stack.emplace_back(PrintedExpr::Value).String); + S << MRI.getName(*LLVMRegNum); + } else if (Opcode >= dwarf::DW_OP_breg0 && + Opcode <= dwarf::DW_OP_breg31) { + int DwarfRegNum = Opcode - dwarf::DW_OP_breg0; + int64_t Offset = Op.getRawOperand(0); + Optional<unsigned> LLVMRegNum = MRI.getLLVMRegNum(DwarfRegNum, false); + if (!LLVMRegNum) { + OS << "<unknown register " << DwarfRegNum << ">"; + return false; + } + raw_svector_ostream S(Stack.emplace_back().String); + S << MRI.getName(*LLVMRegNum); + if (Offset) + S << format("%+" PRId64, Offset); + } else { + // If we hit an unknown operand, we don't know its effect on the stack, + // so bail out on the whole expression. + OS << "<unknown op " << dwarf::OperationEncodingString(Opcode) << " (" + << (int)Opcode << ")>"; + return false; + } + break; + } + ++I; + } + + assert(Stack.size() == 1 && "expected one value on stack"); + + if (Stack.front().Kind == PrintedExpr::Address) + OS << "[" << Stack.front().String << "]"; + else + OS << Stack.front().String; + + return true; +} + +bool DWARFExpression::printCompact(raw_ostream &OS, const MCRegisterInfo &MRI) { + return printCompactDWARFExpr(OS, begin(), end(), MRI); +} + } // namespace llvm diff --git a/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp b/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp index e97ae81345b80..a7da5acc380b5 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp @@ -241,11 +241,13 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, Ctx = &CU->getContext(); C = Ctx; U = CU; + Format = FP.Format; bool Indirect = false; bool IsBlock = false; Value.data = nullptr; // Read the value for the form into value and follow and DW_FORM_indirect // instances we run into + Error Err = Error::success(); do { Indirect = false; switch (Form) { @@ -253,24 +255,25 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, case DW_FORM_ref_addr: { uint16_t Size = (Form == DW_FORM_addr) ? FP.AddrSize : FP.getRefAddrByteSize(); - Value.uval = Data.getRelocatedValue(Size, OffsetPtr, &Value.SectionIndex); + Value.uval = + Data.getRelocatedValue(Size, OffsetPtr, &Value.SectionIndex, &Err); break; } case DW_FORM_exprloc: case DW_FORM_block: - Value.uval = Data.getULEB128(OffsetPtr); + Value.uval = Data.getULEB128(OffsetPtr, &Err); IsBlock = true; break; case DW_FORM_block1: - Value.uval = Data.getU8(OffsetPtr); + Value.uval = Data.getU8(OffsetPtr, &Err); IsBlock = true; break; case DW_FORM_block2: - Value.uval = Data.getU16(OffsetPtr); + Value.uval = Data.getU16(OffsetPtr, &Err); IsBlock = true; break; case DW_FORM_block4: - Value.uval = Data.getU32(OffsetPtr); + Value.uval = Data.getU32(OffsetPtr, &Err); IsBlock = true; break; case DW_FORM_data1: @@ -278,28 +281,28 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, case DW_FORM_flag: case DW_FORM_strx1: case DW_FORM_addrx1: - Value.uval = Data.getU8(OffsetPtr); + Value.uval = Data.getU8(OffsetPtr, &Err); break; case DW_FORM_data2: case DW_FORM_ref2: case DW_FORM_strx2: case DW_FORM_addrx2: - Value.uval = Data.getU16(OffsetPtr); + Value.uval = Data.getU16(OffsetPtr, &Err); break; case DW_FORM_strx3: - Value.uval = Data.getU24(OffsetPtr); + Value.uval = Data.getU24(OffsetPtr, &Err); break; case DW_FORM_data4: case DW_FORM_ref4: case DW_FORM_ref_sup4: case DW_FORM_strx4: case DW_FORM_addrx4: - Value.uval = Data.getRelocatedValue(4, OffsetPtr); + Value.uval = Data.getRelocatedValue(4, OffsetPtr, nullptr, &Err); break; case DW_FORM_data8: case DW_FORM_ref8: case DW_FORM_ref_sup8: - Value.uval = Data.getRelocatedValue(8, OffsetPtr); + Value.uval = Data.getRelocatedValue(8, OffsetPtr, nullptr, &Err); break; case DW_FORM_data16: // Treat this like a 16-byte block. @@ -307,19 +310,23 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, IsBlock = true; break; case DW_FORM_sdata: - Value.sval = Data.getSLEB128(OffsetPtr); + Value.sval = Data.getSLEB128(OffsetPtr, &Err); break; case DW_FORM_udata: case DW_FORM_ref_udata: case DW_FORM_rnglistx: case DW_FORM_loclistx: - Value.uval = Data.getULEB128(OffsetPtr); + case DW_FORM_GNU_addr_index: + case DW_FORM_GNU_str_index: + case DW_FORM_addrx: + case DW_FORM_strx: + Value.uval = Data.getULEB128(OffsetPtr, &Err); break; case DW_FORM_string: - Value.cstr = Data.getCStr(OffsetPtr); + Value.cstr = Data.getCStr(OffsetPtr, &Err); break; case DW_FORM_indirect: - Form = static_cast<dwarf::Form>(Data.getULEB128(OffsetPtr)); + Form = static_cast<dwarf::Form>(Data.getULEB128(OffsetPtr, &Err)); Indirect = true; break; case DW_FORM_strp: @@ -328,39 +335,27 @@ bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, case DW_FORM_GNU_strp_alt: case DW_FORM_line_strp: case DW_FORM_strp_sup: { - Value.uval = - Data.getRelocatedValue(FP.getDwarfOffsetByteSize(), OffsetPtr); + Value.uval = Data.getRelocatedValue(FP.getDwarfOffsetByteSize(), + OffsetPtr, nullptr, &Err); break; } case DW_FORM_flag_present: Value.uval = 1; break; case DW_FORM_ref_sig8: - Value.uval = Data.getU64(OffsetPtr); - break; - case DW_FORM_GNU_addr_index: - case DW_FORM_GNU_str_index: - case DW_FORM_addrx: - case DW_FORM_strx: - Value.uval = Data.getULEB128(OffsetPtr); + Value.uval = Data.getU64(OffsetPtr, &Err); break; default: // DWARFFormValue::skipValue() will have caught this and caused all // DWARF DIEs to fail to be parsed, so this code is not be reachable. llvm_unreachable("unsupported form"); } - } while (Indirect); + } while (Indirect && !Err); - if (IsBlock) { - StringRef Str = Data.getData().substr(*OffsetPtr, Value.uval); - Value.data = nullptr; - if (!Str.empty()) { - Value.data = Str.bytes_begin(); - *OffsetPtr += Value.uval; - } - } + if (IsBlock) + Value.data = Data.getBytes(OffsetPtr, Value.uval, &Err).bytes_begin(); - return true; + return !errorToBool(std::move(Err)); } void DWARFFormValue::dumpSectionedAddress(raw_ostream &OS, @@ -392,6 +387,7 @@ void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { raw_ostream &AddrOS = DumpOpts.ShowAddresses ? WithColor(OS, HighlightColor::Address).get() : nulls(); + int OffsetDumpWidth = 2 * dwarf::getDwarfOffsetByteSize(Format); switch (Form) { case DW_FORM_addr: dumpSectionedAddress(AddrOS, DumpOpts, {Value.uval, Value.SectionIndex}); @@ -487,12 +483,13 @@ void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { break; case DW_FORM_strp: if (DumpOpts.Verbose) - OS << format(" .debug_str[0x%8.8x] = ", (uint32_t)UValue); + OS << format(" .debug_str[0x%0*" PRIx64 "] = ", OffsetDumpWidth, UValue); dumpString(OS); break; case DW_FORM_line_strp: if (DumpOpts.Verbose) - OS << format(" .debug_line_str[0x%8.8x] = ", (uint32_t)UValue); + OS << format(" .debug_line_str[0x%0*" PRIx64 "] = ", OffsetDumpWidth, + UValue); dumpString(OS); break; case DW_FORM_strx: @@ -556,9 +553,8 @@ void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { 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); + AddrOS << format("0x%0*" PRIx64, OffsetDumpWidth, UValue); break; default: diff --git a/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp index 269ea9f79a6e0..2124a49bef606 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFListTable.cpp @@ -18,34 +18,24 @@ using namespace llvm; Error DWARFListTableHeader::extract(DWARFDataExtractor Data, uint64_t *OffsetPtr) { HeaderOffset = *OffsetPtr; - // Read and verify the length field. - if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t))) - return createStringError(errc::invalid_argument, - "section is not large enough to contain a " - "%s table length at offset 0x%" PRIx64, - SectionName.data(), *OffsetPtr); - Format = dwarf::DwarfFormat::DWARF32; - uint8_t OffsetByteSize = 4; - HeaderData.Length = Data.getRelocatedValue(4, OffsetPtr); - if (HeaderData.Length == dwarf::DW_LENGTH_DWARF64) { - Format = dwarf::DwarfFormat::DWARF64; - OffsetByteSize = 8; - HeaderData.Length = Data.getU64(OffsetPtr); - } else if (HeaderData.Length >= dwarf::DW_LENGTH_lo_reserved) { - return createStringError(errc::invalid_argument, - "%s table at offset 0x%" PRIx64 - " has unsupported reserved unit length of value 0x%8.8" PRIx64, - SectionName.data(), HeaderOffset, HeaderData.Length); - } + Error Err = Error::success(); + + std::tie(HeaderData.Length, Format) = Data.getInitialLength(OffsetPtr, &Err); + if (Err) + return createStringError( + errc::invalid_argument, "parsing %s table at offset 0x%" PRIx64 ": %s", + SectionName.data(), HeaderOffset, toString(std::move(Err)).c_str()); + + uint8_t OffsetByteSize = Format == dwarf::DWARF64 ? 8 : 4; uint64_t FullLength = HeaderData.Length + dwarf::getUnitLengthFieldByteSize(Format); - assert(FullLength == length()); if (FullLength < getHeaderSize(Format)) return createStringError(errc::invalid_argument, "%s table at offset 0x%" PRIx64 " has too small length (0x%" PRIx64 ") to contain a complete header", SectionName.data(), HeaderOffset, FullLength); + assert(FullLength == length() && "Inconsistent calculation of length."); uint64_t End = HeaderOffset + FullLength; if (!Data.isValidOffsetForDataOfSize(HeaderOffset, FullLength)) return createStringError(errc::invalid_argument, @@ -89,20 +79,22 @@ Error DWARFListTableHeader::extract(DWARFDataExtractor Data, void DWARFListTableHeader::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { if (DumpOpts.Verbose) OS << format("0x%8.8" PRIx64 ": ", HeaderOffset); - OS << format( - "%s list header: length = 0x%8.8" PRIx64 ", version = 0x%4.4" PRIx16 ", " - "addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8 - ", offset_entry_count = " - "0x%8.8" PRIx32 "\n", - ListTypeString.data(), HeaderData.Length, HeaderData.Version, - HeaderData.AddrSize, HeaderData.SegSize, HeaderData.OffsetEntryCount); + int OffsetDumpWidth = 2 * dwarf::getDwarfOffsetByteSize(Format); + OS << format("%s list header: length = 0x%0*" PRIx64, ListTypeString.data(), + OffsetDumpWidth, HeaderData.Length) + << ", format = " << dwarf::FormatString(Format) + << format(", version = 0x%4.4" PRIx16 ", addr_size = 0x%2.2" PRIx8 + ", seg_size = 0x%2.2" PRIx8 + ", offset_entry_count = 0x%8.8" PRIx32 "\n", + HeaderData.Version, HeaderData.AddrSize, HeaderData.SegSize, + HeaderData.OffsetEntryCount); if (HeaderData.OffsetEntryCount > 0) { OS << "offsets: ["; for (const auto &Off : Offsets) { - OS << format("\n0x%8.8" PRIx64, Off); + OS << format("\n0x%0*" PRIx64, OffsetDumpWidth, Off); if (DumpOpts.Verbose) - OS << format(" => 0x%8.8" PRIx64, + OS << format(" => 0x%08" PRIx64, Off + HeaderOffset + getHeaderSize(Format)); } OS << "\n]\n"; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp index bb81090ba25cf..c219f34bbc313 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFTypeUnit.cpp @@ -20,25 +20,28 @@ using namespace llvm; void DWARFTypeUnit::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { DWARFDie TD = getDIEForOffset(getTypeOffset() + getOffset()); const char *Name = TD.getName(DINameKind::ShortName); + int OffsetDumpWidth = 2 * dwarf::getDwarfOffsetByteSize(getFormat()); if (DumpOpts.SummarizeTypes) { OS << "name = '" << Name << "'" - << " type_signature = " << format("0x%016" PRIx64, getTypeHash()) - << " length = " << format("0x%08" PRIx64, getLength()) << '\n'; + << ", type_signature = " << format("0x%016" PRIx64, getTypeHash()) + << ", length = " << format("0x%0*" PRIx64, OffsetDumpWidth, getLength()) + << '\n'; return; } OS << format("0x%08" PRIx64, getOffset()) << ": Type Unit:" - << " length = " << format("0x%08" PRIx64, getLength()) - << " version = " << format("0x%04x", getVersion()); + << " length = " << format("0x%0*" PRIx64, OffsetDumpWidth, getLength()) + << ", format = " << dwarf::FormatString(getFormat()) + << ", version = " << format("0x%04x", getVersion()); if (getVersion() >= 5) - OS << " unit_type = " << dwarf::UnitTypeString(getUnitType()); - OS << " abbr_offset = " + OS << ", unit_type = " << dwarf::UnitTypeString(getUnitType()); + OS << ", abbr_offset = " << format("0x%04" PRIx64, getAbbreviations()->getOffset()) - << " addr_size = " << format("0x%02x", getAddressByteSize()) - << " name = '" << Name << "'" - << " type_signature = " << format("0x%016" PRIx64, getTypeHash()) - << " type_offset = " << format("0x%04" PRIx64, getTypeOffset()) + << ", addr_size = " << format("0x%02x", getAddressByteSize()) + << ", name = '" << Name << "'" + << ", type_signature = " << format("0x%016" PRIx64, getTypeHash()) + << ", type_offset = " << format("0x%04" PRIx64, getTypeOffset()) << " (next unit at " << format("0x%08" PRIx64, getNextUnitOffset()) << ")\n"; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp index 7bb0194661615..a6d44f04e4680 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -21,7 +21,6 @@ #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Path.h" -#include "llvm/Support/WithColor.h" #include <algorithm> #include <cassert> #include <cstddef> @@ -74,12 +73,15 @@ void DWARFUnitVector::addUnitsImpl( DWARFDataExtractor Data(Obj, InfoSection, LE, 0); if (!Data.isValidOffset(Offset)) return nullptr; - const DWARFUnitIndex *Index = nullptr; - if (IsDWO) - Index = &getDWARFUnitIndex(Context, SectionKind); DWARFUnitHeader Header; - if (!Header.extract(Context, Data, &Offset, SectionKind, Index, - IndexEntry)) + if (!Header.extract(Context, Data, &Offset, SectionKind)) + return nullptr; + if (!IndexEntry && IsDWO) { + const DWARFUnitIndex &Index = getDWARFUnitIndex( + Context, Header.isTypeUnit() ? DW_SECT_EXT_TYPES : DW_SECT_INFO); + IndexEntry = Index.getFromOffset(Header.getOffset()); + } + if (IndexEntry && !Header.applyIndexEntry(IndexEntry)) return nullptr; std::unique_ptr<DWARFUnit> U; if (Header.isTypeUnit()) @@ -140,7 +142,7 @@ DWARFUnit *DWARFUnitVector::getUnitForOffset(uint64_t Offset) const { DWARFUnit * DWARFUnitVector::getUnitForIndexEntry(const DWARFUnitIndex::Entry &E) { - const auto *CUOff = E.getOffset(DW_SECT_INFO); + const auto *CUOff = E.getContribution(DW_SECT_INFO); if (!CUOff) return nullptr; @@ -182,20 +184,17 @@ DWARFUnit::DWARFUnit(DWARFContext &DC, const DWARFSection &Section, if (IsDWO) { // If we are reading a package file, we need to adjust the location list // data based on the index entries. - StringRef Data = LocSection->Data; + StringRef Data = Header.getVersion() >= 5 + ? Context.getDWARFObj().getLoclistsDWOSection().Data + : LocSection->Data; if (auto *IndexEntry = Header.getIndexEntry()) - if (const auto *C = IndexEntry->getOffset(DW_SECT_LOC)) + if (const auto *C = IndexEntry->getContribution( + Header.getVersion() >= 5 ? DW_SECT_LOCLISTS : DW_SECT_EXT_LOC)) Data = Data.substr(C->Offset, C->Length); - DWARFDataExtractor DWARFData = - Header.getVersion() >= 5 - ? DWARFDataExtractor(Context.getDWARFObj(), - Context.getDWARFObj().getLoclistsDWOSection(), - isLittleEndian, getAddressByteSize()) - : DWARFDataExtractor(Data, isLittleEndian, getAddressByteSize()); + DWARFDataExtractor DWARFData(Data, isLittleEndian, getAddressByteSize()); LocTable = std::make_unique<DWARFDebugLoclists>(DWARFData, Header.getVersion()); - } else if (Header.getVersion() >= 5) { LocTable = std::make_unique<DWARFDebugLoclists>( DWARFDataExtractor(Context.getDWARFObj(), @@ -255,20 +254,12 @@ Optional<uint64_t> DWARFUnit::getStringOffsetSectionItem(uint32_t Index) const { bool DWARFUnitHeader::extract(DWARFContext &Context, const DWARFDataExtractor &debug_info, uint64_t *offset_ptr, - DWARFSectionKind SectionKind, - const DWARFUnitIndex *Index, - const DWARFUnitIndex::Entry *Entry) { + DWARFSectionKind SectionKind) { Offset = *offset_ptr; Error Err = Error::success(); - IndexEntry = Entry; - if (!IndexEntry && Index) - IndexEntry = Index->getFromOffset(*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, &Err); - FormParams.Format = DWARF64; - } + IndexEntry = nullptr; + std::tie(Length, FormParams.Format) = + debug_info.getInitialLength(offset_ptr, &Err); FormParams.Version = debug_info.getU16(offset_ptr, &Err); if (FormParams.Version >= 5) { UnitType = debug_info.getU8(offset_ptr, &Err); @@ -281,22 +272,11 @@ bool DWARFUnitHeader::extract(DWARFContext &Context, 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) + if (SectionKind == DW_SECT_EXT_TYPES) UnitType = DW_UT_type; else UnitType = DW_UT_compile; } - if (IndexEntry) { - if (AbbrOffset) - return false; - auto *UnitContrib = IndexEntry->getOffset(); - if (!UnitContrib || UnitContrib->Length != (Length + 4)) - return false; - auto *AbbrEntry = IndexEntry->getOffset(DW_SECT_ABBREV); - if (!AbbrEntry) - return false; - AbbrOffset = AbbrEntry->Offset; - } if (isTypeUnit()) { TypeHash = debug_info.getU64(offset_ptr, &Err); TypeOffset = debug_info.getUnsigned( @@ -320,7 +300,7 @@ bool DWARFUnitHeader::extract(DWARFContext &Context, TypeOffset < getLength() + getUnitLengthFieldByteSize(); bool LengthOK = debug_info.isValidOffset(getNextUnitOffset() - 1); bool VersionOK = DWARFContext::isSupportedVersion(getVersion()); - bool AddrSizeOK = getAddressByteSize() == 4 || getAddressByteSize() == 8; + bool AddrSizeOK = DWARFContext::isAddressSizeSupported(getAddressByteSize()); if (!LengthOK || !VersionOK || !AddrSizeOK || !TypeOffsetOK) return false; @@ -330,6 +310,23 @@ bool DWARFUnitHeader::extract(DWARFContext &Context, return true; } +bool DWARFUnitHeader::applyIndexEntry(const DWARFUnitIndex::Entry *Entry) { + assert(Entry); + assert(!IndexEntry); + IndexEntry = Entry; + if (AbbrOffset) + return false; + auto *UnitContrib = IndexEntry->getContribution(); + if (!UnitContrib || + UnitContrib->Length != (getLength() + getUnitLengthFieldByteSize())) + return false; + auto *AbbrEntry = IndexEntry->getContribution(DW_SECT_ABBREV); + if (!AbbrEntry) + return false; + AbbrOffset = AbbrEntry->Offset; + return true; +} + // Parse the rangelist table header, including the optional array of offsets // following it (DWARF v5 and later). template<typename ListTableType> @@ -426,15 +423,17 @@ void DWARFUnit::extractDIEsToVector( // should always terminate at or before the start of the next compilation // unit header). if (DIEOffset > NextCUOffset) - WithColor::warning() << format("DWARF compile unit extends beyond its " - "bounds cu 0x%8.8" PRIx64 " " - "at 0x%8.8" PRIx64 "\n", - getOffset(), DIEOffset); + Context.getWarningHandler()( + createStringError(errc::invalid_argument, + "DWARF compile unit extends beyond its " + "bounds cu 0x%8.8" PRIx64 " " + "at 0x%8.8" PRIx64 "\n", + getOffset(), DIEOffset)); } void DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) { if (Error e = tryExtractDIEsIfNeeded(CUDieOnly)) - WithColor::error() << toString(std::move(e)); + Context.getRecoverableErrorHandler()(std::move(e)); } Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) { @@ -492,9 +491,17 @@ Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) { // DWARF v5 uses the .debug_rnglists and .debug_rnglists.dwo sections to // describe address ranges. if (getVersion() >= 5) { - if (IsDWO) - setRangesSection(&Context.getDWARFObj().getRnglistsDWOSection(), 0); - else + // In case of DWP, the base offset from the index has to be added. + uint64_t ContributionBaseOffset = 0; + if (IsDWO) { + if (auto *IndexEntry = Header.getIndexEntry()) + if (auto *Contrib = IndexEntry->getContribution(DW_SECT_RNGLISTS)) + ContributionBaseOffset = Contrib->Offset; + setRangesSection( + &Context.getDWARFObj().getRnglistsDWOSection(), + ContributionBaseOffset + + DWARFListTableHeader::getHeaderSize(Header.getFormat())); + } else setRangesSection(&Context.getDWARFObj().getRnglistsSection(), toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0)); if (RangeSection->Data.size()) { @@ -514,19 +521,26 @@ Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) { // In a split dwarf unit, there is no DW_AT_rnglists_base attribute. // Adjust RangeSectionBase to point past the table header. if (IsDWO && RngListTable) - RangeSectionBase = RngListTable->getHeaderSize(); + RangeSectionBase = + ContributionBaseOffset + 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(), + if (IsDWO) { + auto &DWOSection = Context.getDWARFObj().getLoclistsDWOSection(); + if (DWOSection.Data.empty()) + return Error::success(); + setLocSection(&DWOSection, DWARFListTableHeader::getHeaderSize(Header.getFormat())); - else + } else if (auto X = UnitDie.find(DW_AT_loclists_base)) { setLocSection(&Context.getDWARFObj().getLoclistsSection(), - toSectionOffset(UnitDie.find(DW_AT_loclists_base), 0)); + toSectionOffset(X, 0)); + } else { + return Error::success(); + } - if (LocSection->Data.size()) { + if (LocSection) { if (IsDWO) LoclistTableHeader.emplace(".debug_loclists.dwo", "locations"); else @@ -542,6 +556,9 @@ Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) { " list table with base = 0x%" PRIx64 "\n", Offset); Offset -= HeaderSize; + if (auto *IndexEntry = Header.getIndexEntry()) + if (const auto *Contrib = IndexEntry->getContribution(DW_SECT_LOCLISTS)) + Offset += Contrib->Offset; if (Error E = LoclistTableHeader->extract(Data, &Offset)) return createStringError(errc::invalid_argument, "parsing a loclist table: " + @@ -596,9 +613,10 @@ bool DWARFUnit::parseDWO() { RangesDA, RangeSectionBase, Header.getFormat())) DWO->RngListTable = TableOrError.get(); else - WithColor::error() << "parsing a range list table: " - << toString(TableOrError.takeError()) - << '\n'; + Context.getRecoverableErrorHandler()(createStringError( + errc::invalid_argument, "parsing a range list table: %s", + toString(TableOrError.takeError()).c_str())); + if (DWO->RngListTable) DWO->RangeSectionBase = DWO->RngListTable->getHeaderSize(); } else { @@ -759,7 +777,7 @@ const DWARFUnitIndex &llvm::getDWARFUnitIndex(DWARFContext &Context, DWARFSectionKind Kind) { if (Kind == DW_SECT_INFO) return Context.getCUIndex(); - assert(Kind == DW_SECT_TYPES); + assert(Kind == DW_SECT_EXT_TYPES); return Context.getTUIndex(); } @@ -944,18 +962,12 @@ parseDWARFStringOffsetsTableHeader(DWARFDataExtractor &DA, Expected<Optional<StrOffsetsContributionDescriptor>> DWARFUnit::determineStringOffsetsTableContribution(DWARFDataExtractor &DA) { - uint64_t Offset; - if (IsDWO) { - Offset = 0; - if (DA.getData().data() == nullptr) - return None; - } else { - auto OptOffset = toSectionOffset(getUnitDIE().find(DW_AT_str_offsets_base)); - if (!OptOffset) - return None; - Offset = *OptOffset; - } - auto DescOrError = parseDWARFStringOffsetsTableHeader(DA, Header.getFormat(), Offset); + assert(!IsDWO); + auto OptOffset = toSectionOffset(getUnitDIE().find(DW_AT_str_offsets_base)); + if (!OptOffset) + return None; + auto DescOrError = + parseDWARFStringOffsetsTableHeader(DA, Header.getFormat(), *OptOffset); if (!DescOrError) return DescOrError.takeError(); return *DescOrError; @@ -963,10 +975,11 @@ DWARFUnit::determineStringOffsetsTableContribution(DWARFDataExtractor &DA) { Expected<Optional<StrOffsetsContributionDescriptor>> DWARFUnit::determineStringOffsetsTableContributionDWO(DWARFDataExtractor & DA) { + assert(IsDWO); uint64_t Offset = 0; auto IndexEntry = Header.getIndexEntry(); const auto *C = - IndexEntry ? IndexEntry->getOffset(DW_SECT_STR_OFFSETS) : nullptr; + IndexEntry ? IndexEntry->getContribution(DW_SECT_STR_OFFSETS) : nullptr; if (C) Offset = C->Offset; if (getVersion() >= 5) { @@ -983,11 +996,10 @@ DWARFUnit::determineStringOffsetsTableContributionDWO(DWARFDataExtractor & DA) { // index table (in a package file). In a .dwo file it is simply // the length of the string offsets section. if (!IndexEntry) - return { - Optional<StrOffsetsContributionDescriptor>( - {0, StringOffsetSection.Data.size(), 4, DWARF32})}; + return {Optional<StrOffsetsContributionDescriptor>( + {0, StringOffsetSection.Data.size(), 4, Header.getFormat()})}; if (C) return {Optional<StrOffsetsContributionDescriptor>( - {C->Offset, C->Length, 4, DWARF32})}; + {C->Offset, C->Length, 4, Header.getFormat()})}; return None; } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp index f29c1e6cc5c74..3d4cecce27db1 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp @@ -17,19 +17,102 @@ using namespace llvm; +namespace { + +enum class DWARFSectionKindV2 { + DW_SECT_INFO = 1, + DW_SECT_TYPES = 2, + DW_SECT_ABBREV = 3, + DW_SECT_LINE = 4, + DW_SECT_LOC = 5, + DW_SECT_STR_OFFSETS = 6, + DW_SECT_MACINFO = 7, + DW_SECT_MACRO = 8, +}; + +} // namespace + +// Return true if the section identifier is defined in the DWARFv5 standard. +constexpr bool isKnownV5SectionID(uint32_t ID) { + return ID >= DW_SECT_INFO && ID <= DW_SECT_RNGLISTS && + ID != DW_SECT_EXT_TYPES; +} + +uint32_t llvm::serializeSectionKind(DWARFSectionKind Kind, + unsigned IndexVersion) { + if (IndexVersion == 5) { + assert(isKnownV5SectionID(Kind)); + return static_cast<uint32_t>(Kind); + } + assert(IndexVersion == 2); + switch (Kind) { +#define CASE(S,T) \ + case DW_SECT_##S: \ + return static_cast<uint32_t>(DWARFSectionKindV2::DW_SECT_##T) + CASE(INFO, INFO); + CASE(EXT_TYPES, TYPES); + CASE(ABBREV, ABBREV); + CASE(LINE, LINE); + CASE(EXT_LOC, LOC); + CASE(STR_OFFSETS, STR_OFFSETS); + CASE(EXT_MACINFO, MACINFO); + CASE(MACRO, MACRO); +#undef CASE + default: + // All other section kinds have no corresponding values in v2 indexes. + llvm_unreachable("Invalid DWARFSectionKind"); + } +} + +DWARFSectionKind llvm::deserializeSectionKind(uint32_t Value, + unsigned IndexVersion) { + if (IndexVersion == 5) + return isKnownV5SectionID(Value) + ? static_cast<DWARFSectionKind>(Value) + : DW_SECT_EXT_unknown; + assert(IndexVersion == 2); + switch (static_cast<DWARFSectionKindV2>(Value)) { +#define CASE(S,T) \ + case DWARFSectionKindV2::DW_SECT_##S: \ + return DW_SECT_##T + CASE(INFO, INFO); + CASE(TYPES, EXT_TYPES); + CASE(ABBREV, ABBREV); + CASE(LINE, LINE); + CASE(LOC, EXT_LOC); + CASE(STR_OFFSETS, STR_OFFSETS); + CASE(MACINFO, EXT_MACINFO); + CASE(MACRO, MACRO); +#undef CASE + } + return DW_SECT_EXT_unknown; +} + bool DWARFUnitIndex::Header::parse(DataExtractor IndexData, uint64_t *OffsetPtr) { + const uint64_t BeginOffset = *OffsetPtr; if (!IndexData.isValidOffsetForDataOfSize(*OffsetPtr, 16)) return false; + // GCC Debug Fission defines the version as an unsigned 32-bit field + // with value of 2, https://gcc.gnu.org/wiki/DebugFissionDWP. + // DWARFv5 defines the same space as an uhalf version field with value of 5 + // and a 2 bytes long padding, see Section 7.3.5.3. Version = IndexData.getU32(OffsetPtr); + if (Version != 2) { + *OffsetPtr = BeginOffset; + Version = IndexData.getU16(OffsetPtr); + if (Version != 5) + return false; + *OffsetPtr += 2; // Skip padding. + } NumColumns = IndexData.getU32(OffsetPtr); NumUnits = IndexData.getU32(OffsetPtr); NumBuckets = IndexData.getU32(OffsetPtr); - return Version <= 2; + return true; } void DWARFUnitIndex::Header::dump(raw_ostream &OS) const { - OS << format("version = %u slots = %u\n\n", Version, NumBuckets); + OS << format("version = %u, units = %u, slots = %u\n\n", Version, NumUnits, NumBuckets); } bool DWARFUnitIndex::parse(DataExtractor IndexData) { @@ -49,6 +132,10 @@ bool DWARFUnitIndex::parseImpl(DataExtractor IndexData) { if (!Header.parse(IndexData, &Offset)) return false; + // Fix InfoColumnKind: in DWARFv5, type units are in .debug_info.dwo. + if (Header.Version == 5) + InfoColumnKind = DW_SECT_INFO; + if (!IndexData.isValidOffsetForDataOfSize( Offset, Header.NumBuckets * (8 + 4) + (2 * Header.NumUnits + 1) * 4 * Header.NumColumns)) @@ -58,6 +145,7 @@ bool DWARFUnitIndex::parseImpl(DataExtractor IndexData) { auto Contribs = std::make_unique<Entry::SectionContribution *[]>(Header.NumUnits); ColumnKinds = std::make_unique<DWARFSectionKind[]>(Header.NumColumns); + RawSectionIds = std::make_unique<uint32_t[]>(Header.NumColumns); // Read Hash Table of Signatures for (unsigned i = 0; i != Header.NumBuckets; ++i) @@ -76,7 +164,8 @@ bool DWARFUnitIndex::parseImpl(DataExtractor IndexData) { // Read the Column Headers for (unsigned i = 0; i != Header.NumColumns; ++i) { - ColumnKinds[i] = static_cast<DWARFSectionKind>(IndexData.getU32(&Offset)); + RawSectionIds[i] = IndexData.getU32(&Offset); + ColumnKinds[i] = deserializeSectionKind(RawSectionIds[i], Header.Version); if (ColumnKinds[i] == InfoColumnKind) { if (InfoColumn != -1) return false; @@ -105,20 +194,21 @@ bool DWARFUnitIndex::parseImpl(DataExtractor IndexData) { } StringRef DWARFUnitIndex::getColumnHeader(DWARFSectionKind DS) { -#define CASE(DS) \ - case DW_SECT_##DS: \ - return #DS; switch (DS) { - CASE(INFO); - CASE(TYPES); - CASE(ABBREV); - CASE(LINE); - CASE(LOC); - CASE(STR_OFFSETS); - CASE(MACINFO); - CASE(MACRO); +#define HANDLE_DW_SECT(ID, NAME) \ + case DW_SECT_##NAME: \ + return #NAME; +#include "llvm/BinaryFormat/Dwarf.def" + case DW_SECT_EXT_TYPES: + return "TYPES"; + case DW_SECT_EXT_LOC: + return "LOC"; + case DW_SECT_EXT_MACINFO: + return "MACINFO"; + case DW_SECT_EXT_unknown: + return StringRef(); } - llvm_unreachable("unknown DWARFSectionKind"); + llvm_unreachable("Unknown DWARFSectionKind"); } void DWARFUnitIndex::dump(raw_ostream &OS) const { @@ -127,8 +217,14 @@ void DWARFUnitIndex::dump(raw_ostream &OS) const { Header.dump(OS); OS << "Index Signature "; - for (unsigned i = 0; i != Header.NumColumns; ++i) - OS << ' ' << left_justify(getColumnHeader(ColumnKinds[i]), 24); + for (unsigned i = 0; i != Header.NumColumns; ++i) { + DWARFSectionKind Kind = ColumnKinds[i]; + StringRef Name = getColumnHeader(Kind); + if (!Name.empty()) + OS << ' ' << left_justify(Name, 24); + else + OS << format(" Unknown: %-15" PRIu32, RawSectionIds[i]); + } OS << "\n----- ------------------"; for (unsigned i = 0; i != Header.NumColumns; ++i) OS << " ------------------------"; @@ -148,7 +244,7 @@ void DWARFUnitIndex::dump(raw_ostream &OS) const { } const DWARFUnitIndex::Entry::SectionContribution * -DWARFUnitIndex::Entry::getOffset(DWARFSectionKind Sec) const { +DWARFUnitIndex::Entry::getContribution(DWARFSectionKind Sec) const { uint32_t i = 0; for (; i != Index->Header.NumColumns; ++i) if (Index->ColumnKinds[i] == Sec) @@ -157,7 +253,7 @@ DWARFUnitIndex::Entry::getOffset(DWARFSectionKind Sec) const { } const DWARFUnitIndex::Entry::SectionContribution * -DWARFUnitIndex::Entry::getOffset() const { +DWARFUnitIndex::Entry::getContribution() const { return &Contributions[Index->InfoColumn]; } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp index 1fd6c1d7d2821..3a83317a73a3b 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -26,24 +26,26 @@ using namespace llvm; using namespace dwarf; using namespace object; -DWARFVerifier::DieRangeInfo::address_range_iterator +Optional<DWARFAddressRange> DWARFVerifier::DieRangeInfo::insert(const DWARFAddressRange &R) { auto Begin = Ranges.begin(); auto End = Ranges.end(); auto Pos = std::lower_bound(Begin, End, R); if (Pos != End) { - if (Pos->intersects(R)) - return std::move(Pos); - if (Pos != Begin) { - auto Iter = Pos - 1; - if (Iter->intersects(R)) - return std::move(Iter); - } + DWARFAddressRange Range(*Pos); + if (Pos->merge(R)) + return Range; + } + if (Pos != Begin) { + auto Iter = Pos - 1; + DWARFAddressRange Range(*Iter); + if (Iter->merge(R)) + return Range; } Ranges.insert(Pos, R); - return Ranges.end(); + return None; } DWARFVerifier::DieRangeInfo::die_range_info_iterator @@ -112,11 +114,9 @@ bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData, bool ValidAbbrevOffset = true; uint64_t OffsetStart = *Offset; - Length = DebugInfoData.getU32(Offset); - if (Length == dwarf::DW_LENGTH_DWARF64) { - Length = DebugInfoData.getU64(Offset); - isUnitDWARF64 = true; - } + DwarfFormat Format; + std::tie(Length, Format) = DebugInfoData.getInitialLength(Offset); + isUnitDWARF64 = Format == DWARF64; Version = DebugInfoData.getU16(Offset); if (Version >= 5) { @@ -135,7 +135,7 @@ bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData, ValidLength = DebugInfoData.isValidOffset(OffsetStart + Length + 3); ValidVersion = DWARFContext::isSupportedVersion(Version); - ValidAddrSize = AddrSize == 4 || AddrSize == 8; + ValidAddrSize = DWARFContext::isAddressSizeSupported(AddrSize); if (!ValidLength || !ValidVersion || !ValidAddrSize || !ValidAbbrevOffset || !ValidType) { Success = false; @@ -307,7 +307,7 @@ unsigned DWARFVerifier::verifyUnitSection(const DWARFSection &S, Unit = TypeUnitVector.addUnit(std::make_unique<DWARFTypeUnit>( DCtx, S, Header, DCtx.getDebugAbbrev(), &DObj.getRangesSection(), &DObj.getLocSection(), DObj.getStrSection(), - DObj.getStrOffsetsSection(), &DObj.getAppleObjCSection(), + DObj.getStrOffsetsSection(), &DObj.getAddrSection(), DObj.getLineSection(), DCtx.isLittleEndian(), false, TypeUnitVector)); break; @@ -321,7 +321,7 @@ unsigned DWARFVerifier::verifyUnitSection(const DWARFSection &S, Unit = CompileUnitVector.addUnit(std::make_unique<DWARFCompileUnit>( DCtx, S, Header, DCtx.getDebugAbbrev(), &DObj.getRangesSection(), &DObj.getLocSection(), DObj.getStrSection(), - DObj.getStrOffsetsSection(), &DObj.getAppleObjCSection(), + DObj.getStrOffsetsSection(), &DObj.getAddrSection(), DObj.getLineSection(), DCtx.isLittleEndian(), false, CompileUnitVector)); break; @@ -354,7 +354,7 @@ bool DWARFVerifier::handleDebugInfo() { OS << "Verifying .debug_types Unit Header Chain...\n"; DObj.forEachTypesSections([&](const DWARFSection &S) { - NumErrors += verifyUnitSection(S, DW_SECT_TYPES); + NumErrors += verifyUnitSection(S, DW_SECT_EXT_TYPES); }); return NumErrors == 0; } @@ -399,22 +399,30 @@ unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, // processing an object file. if (!IsObjectFile || IsMachOObject || Die.getTag() != DW_TAG_compile_unit) { + bool DumpDieAfterError = false; for (auto Range : Ranges) { if (!Range.valid()) { ++NumErrors; error() << "Invalid address range " << Range << "\n"; + DumpDieAfterError = true; continue; } - // Verify that ranges don't intersect. - const auto IntersectingRange = RI.insert(Range); - if (IntersectingRange != RI.Ranges.end()) { + // Verify that ranges don't intersect and also build up the DieRangeInfo + // address ranges. Don't break out of the loop below early, or we will + // think this DIE doesn't have all of the address ranges it is supposed + // to have. Compile units often have DW_AT_ranges that can contain one or + // more dead stripped address ranges which tend to all be at the same + // address: 0 or -1. + if (auto PrevRange = RI.insert(Range)) { ++NumErrors; - error() << "DIE has overlapping address ranges: " << Range << " and " - << *IntersectingRange << "\n"; - break; + error() << "DIE has overlapping ranges in DW_AT_ranges attribute: " + << *PrevRange << " and " << Range << '\n'; + DumpDieAfterError = true; } } + if (DumpDieAfterError) + dump(Die, 2) << '\n'; } // Verify that children don't intersect. @@ -459,8 +467,15 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, case DW_AT_ranges: // Make sure the offset in the DW_AT_ranges attribute is valid. if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { - if (*SectionOffset >= DObj.getRangesSection().Data.size()) - ReportError("DW_AT_ranges offset is beyond .debug_ranges bounds:"); + unsigned DwarfVersion = Die.getDwarfUnit()->getVersion(); + const DWARFSection &RangeSection = DwarfVersion < 5 + ? DObj.getRangesSection() + : DObj.getRnglistsSection(); + if (*SectionOffset >= RangeSection.Data.size()) + ReportError( + "DW_AT_ranges offset is beyond " + + StringRef(DwarfVersion < 5 ? ".debug_ranges" : ".debug_rnglists") + + " bounds: " + llvm::formatv("{0:x8}", *SectionOffset)); break; } ReportError("DIE has invalid DW_AT_ranges encoding:"); @@ -481,8 +496,8 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, DWARFUnit *U = Die.getDwarfUnit(); for (const auto &Entry : *Loc) { DataExtractor Data(toStringRef(Entry.Expr), DCtx.isLittleEndian(), 0); - DWARFExpression Expression(Data, U->getVersion(), - U->getAddressByteSize()); + DWARFExpression Expression(Data, U->getAddressByteSize(), + U->getFormParams().Format); bool Error = any_of(Expression, [](DWARFExpression::Operation &Op) { return Op.isError(); }); @@ -758,7 +773,7 @@ void DWARFVerifier::verifyDebugLineRows() { << "] row[" << RowIndex << "] decreases in address from previous row:\n"; - DWARFDebugLine::Row::dumpTableHeader(OS); + DWARFDebugLine::Row::dumpTableHeader(OS, 0); if (RowIndex > 0) LineTable->Rows[RowIndex - 1].dump(OS); Row.dump(OS); @@ -776,7 +791,7 @@ void DWARFVerifier::verifyDebugLineRows() { << " (valid values are [" << (isDWARF5 ? "0," : "1,") << LineTable->Prologue.FileNames.size() << (isDWARF5 ? ")" : "]") << "):\n"; - DWARFDebugLine::Row::dumpTableHeader(OS); + DWARFDebugLine::Row::dumpTableHeader(OS, 0); Row.dump(OS); OS << '\n'; } @@ -1290,7 +1305,8 @@ static bool isVariableIndexable(const DWARFDie &Die, DWARFContext &DCtx) { for (const auto &Entry : *Loc) { DataExtractor Data(toStringRef(Entry.Expr), DCtx.isLittleEndian(), U->getAddressByteSize()); - DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize()); + DWARFExpression Expression(Data, U->getAddressByteSize(), + U->getFormParams().Format); bool IsInteresting = any_of(Expression, [](DWARFExpression::Operation &Op) { return !Op.isError() && (Op.getCode() == DW_OP_addr || Op.getCode() == DW_OP_form_tls_address || @@ -1330,9 +1346,7 @@ unsigned DWARFVerifier::verifyNameIndexCompleteness( // "The name index must contain an entry for each debugging information entry // that defines a named subprogram, label, variable, type, or namespace, // subject to ..." - // Instead whitelisting all TAGs representing a "type" or a "subprogram", to - // make sure we catch any missing items, we instead blacklist all TAGs that we - // know shouldn't be indexed. + // Explicitly exclude all TAGs that we know shouldn't be indexed. switch (Die.getTag()) { // Compile units and modules have names but shouldn't be indexed. case DW_TAG_compile_unit: |