diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h @@ -21,25 +21,31 @@ class MCRegisterInfo; class raw_ostream; +/// A single location within a location list. Entries are stored in the DWARF5 +/// form even if they originally come from a DWARF<=4 location list. +struct DWARFLocationEntry { + /// The entry kind (DW_LLE_***). + uint8_t Kind; + + /// The first value of the location entry (if applicable). + uint64_t Value0; + + /// The second value of the location entry (if applicable). + uint64_t Value1; + + /// The location expression itself (if applicable). + SmallVector Loc; +}; + class DWARFDebugLoc { public: - /// A single location within a location list. - struct Entry { - /// The beginning address of the instruction range. - uint64_t Begin; - /// The ending address of the instruction range. - uint64_t End; - /// The location of the variable within the specified range. - SmallVector Loc; - }; - /// A list of locations that contain one variable. struct LocationList { /// The beginning offset where this location list is stored in the debug_loc /// section. uint64_t Offset; /// All the locations in which the variable is stored. - SmallVector Entries; + SmallVector Entries; /// Dump this list on OS. void dump(raw_ostream &OS, uint64_t BaseAddress, bool IsLittleEndian, unsigned AddressSize, const MCRegisterInfo *MRI, DWARFUnit *U, @@ -70,34 +76,41 @@ /// Return the location list at the given offset or nullptr. LocationList const *getLocationListAtOffset(uint64_t Offset) const; + /// Call the user-provided callback for each entry (including the end-of-list + /// entry) in the location list starting at \p Offset. The callback can return + /// false to terminate the iteration early. Returns an error if it was unable + /// to parse the entire location list correctly. Upon successful termination + /// \p Offset will be updated point past the end of the list. + static Error + visitLocationList(const DWARFDataExtractor &Data, uint64_t *Offset, + uint16_t Version, + function_ref F); + Expected parseOneLocationList(const DWARFDataExtractor &Data, uint64_t *Offset); }; class DWARFDebugLoclists { + + static void dumpEntry(const DWARFLocationEntry &Entry, raw_ostream &OS, + uint64_t &BaseAddr, bool IsLittleEndian, + unsigned AddressSize, const MCRegisterInfo *MRI, + DWARFUnit *U, DIDumpOptions DumpOpts, unsigned Indent, + size_t MaxEncodingStringLength); + public: // Unconstructible. DWARFDebugLoclists() = delete; - struct Entry { - uint8_t Kind; - uint64_t Offset; - uint64_t Value0; - uint64_t Value1; - SmallVector Loc; - void dump(raw_ostream &OS, uint64_t &BaseAddr, bool IsLittleEndian, - unsigned AddressSize, const MCRegisterInfo *MRI, DWARFUnit *U, - DIDumpOptions DumpOpts, unsigned Indent, size_t MaxEncodingStringLength) const; - }; - /// Call the user-provided callback for each entry (including the end-of-list /// entry) in the location list starting at \p Offset. The callback can return /// false to terminate the iteration early. Returns an error if it was unable /// to parse the entire location list correctly. Upon successful termination /// \p Offset will be updated point past the end of the list. - static Error visitLocationList(const DWARFDataExtractor &Data, - uint64_t *Offset, uint16_t Version, - llvm::function_ref F); + static Error + visitLocationList(const DWARFDataExtractor &Data, uint64_t *Offset, + uint16_t Version, + function_ref F); /// Dump the location list at the given \p Offset. The function returns true /// iff it has successfully reched the end of the list. This means that one diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -41,13 +41,29 @@ const MCRegisterInfo *MRI, DWARFUnit *U, DIDumpOptions DumpOpts, unsigned Indent) const { - for (const Entry &E : Entries) { + for (const DWARFLocationEntry &E : Entries) { + uint64_t Value0, Value1; + switch (E.Kind) { + case dwarf::DW_LLE_base_address: + Value0 = AddressSize == 4 ? -1U : -1ULL; + Value1 = E.Value0; + break; + case dwarf::DW_LLE_offset_pair: + Value0 = E.Value0; + Value1 = E.Value1; + break; + case dwarf::DW_LLE_end_of_list: + assert(&E == &Entries.back()); + return; + default: + llvm_unreachable("Not possible in DWARF4!"); + } OS << '\n'; OS.indent(Indent); OS << format("[0x%*.*" PRIx64 ", ", AddressSize * 2, AddressSize * 2, - BaseAddress + E.Begin); + BaseAddress + Value0); OS << format(" 0x%*.*" PRIx64 ")", AddressSize * 2, AddressSize * 2, - BaseAddress + E.End); + BaseAddress + Value1); OS << ": "; dumpExpression(OS, E.Loc, IsLittleEndian, AddressSize, MRI, U); @@ -84,44 +100,58 @@ } } -Expected -DWARFDebugLoc::parseOneLocationList(const DWARFDataExtractor &Data, - uint64_t *Offset) { - LocationList LL; - LL.Offset = *Offset; - AddressSize = Data.getAddressSize(); +Error DWARFDebugLoc::visitLocationList( + const DWARFDataExtractor &Data, uint64_t *Offset, uint16_t Version, + function_ref F) { + assert(Version <= 4 && "Location format changed in DWARF5"); DataExtractor::Cursor C(*Offset); + bool Continue = true; + while (Continue) { + uint64_t Value0 = Data.getRelocatedAddress(C); + uint64_t Value1 = Data.getRelocatedAddress(C); - // 2.6.2 Location Lists - // A location list entry consists of: - while (true) { - Entry E; - - // 1. A beginning address offset. ... - E.Begin = Data.getRelocatedAddress(C); - - // 2. An ending address offset. ... - E.End = Data.getRelocatedAddress(C); - - if (Error Err = C.takeError()) - return std::move(Err); + DWARFLocationEntry E; // The end of any given location list is marked by an end of list entry, // which consists of a 0 for the beginning address offset and a 0 for the - // ending address offset. - if (E.Begin == 0 && E.End == 0) { - *Offset = C.tell(); - return LL; - } - - if (E.Begin != (AddressSize == 4 ? -1U : -1ULL)) { + // ending address offset. A beginning offset of 0xff...f marks the base + // address selection entry. + if (Value0 == 0 && Value1 == 0) { + E.Kind = dwarf::DW_LLE_end_of_list; + } else if (Value0 == (Data.getAddressSize() == 4 ? -1U : -1ULL)) { + E.Kind = dwarf::DW_LLE_base_address; + E.Value0 = Value1; + } else { + E.Kind = dwarf::DW_LLE_offset_pair; + E.Value0 = Value0; + E.Value1 = Value1; unsigned Bytes = Data.getU16(C); // A single location description describing the location of the object... Data.getU8(C, E.Loc, Bytes); } - LL.Entries.push_back(std::move(E)); + if (!C) + return C.takeError(); + Continue = F(E) && E.Kind != dwarf::DW_LLE_end_of_list; } + *Offset = C.tell(); + return Error::success(); +} + +Expected +DWARFDebugLoc::parseOneLocationList(const DWARFDataExtractor &Data, + uint64_t *Offset) { + LocationList LL; + LL.Offset = *Offset; + + Error E = + visitLocationList(Data, Offset, 4, [&](const DWARFLocationEntry &E) { + LL.Entries.push_back(E); + return true; + }); + if (E) + return std::move(E); + return std::move(LL); } void DWARFDebugLoc::parse(const DWARFDataExtractor &data) { @@ -141,13 +171,12 @@ Error DWARFDebugLoclists::visitLocationList( const DWARFDataExtractor &Data, uint64_t *Offset, uint16_t Version, - llvm::function_ref F) { + function_ref F) { DataExtractor::Cursor C(*Offset); bool Continue = true; while (Continue) { - Entry E; - E.Offset = C.tell(); + DWARFLocationEntry E; E.Kind = Data.getU8(C); switch (E.Kind) { case dwarf::DW_LLE_end_of_list: @@ -215,11 +244,12 @@ } OS << format("0x%8.8" PRIx64 ": ", *Offset); - Error E = visitLocationList(Data, Offset, Version, [&](const Entry &E) { - E.dump(OS, BaseAddr, Data.isLittleEndian(), Data.getAddressSize(), MRI, U, - DumpOpts, Indent, MaxEncodingStringLength); - return true; - }); + Error E = visitLocationList( + Data, Offset, Version, [&](const DWARFLocationEntry &E) { + dumpEntry(E, OS, BaseAddr, Data.isLittleEndian(), Data.getAddressSize(), + MRI, U, DumpOpts, Indent, MaxEncodingStringLength); + return true; + }); if (E) { OS << "\n"; OS.indent(Indent); @@ -229,31 +259,32 @@ return true; } -void DWARFDebugLoclists::Entry::dump(raw_ostream &OS, uint64_t &BaseAddr, - bool IsLittleEndian, unsigned AddressSize, - const MCRegisterInfo *MRI, DWARFUnit *U, - DIDumpOptions DumpOpts, unsigned Indent, - size_t MaxEncodingStringLength) const { +void DWARFDebugLoclists::dumpEntry(const DWARFLocationEntry &Entry, + raw_ostream &OS, uint64_t &BaseAddr, + bool IsLittleEndian, unsigned AddressSize, + const MCRegisterInfo *MRI, DWARFUnit *U, + DIDumpOptions DumpOpts, unsigned Indent, + size_t MaxEncodingStringLength) { if (DumpOpts.Verbose) { OS << "\n"; OS.indent(Indent); - auto EncodingString = dwarf::LocListEncodingString(Kind); + auto EncodingString = dwarf::LocListEncodingString(Entry.Kind); // Unsupported encodings should have been reported during parsing. assert(!EncodingString.empty() && "Unknown loclist entry encoding"); OS << format("%s%*c", EncodingString.data(), MaxEncodingStringLength - EncodingString.size() + 1, '('); - switch (Kind) { + switch (Entry.Kind) { case dwarf::DW_LLE_startx_length: case dwarf::DW_LLE_start_length: case dwarf::DW_LLE_offset_pair: OS << format("0x%*.*" PRIx64 ", 0x%*.*" PRIx64, AddressSize * 2, - AddressSize * 2, Value0, AddressSize * 2, AddressSize * 2, - Value1); + AddressSize * 2, Entry.Value0, AddressSize * 2, + AddressSize * 2, Entry.Value1); break; case dwarf::DW_LLE_base_addressx: case dwarf::DW_LLE_base_address: OS << format("0x%*.*" PRIx64, AddressSize * 2, AddressSize * 2, - Value0); + Entry.Value0); break; case dwarf::DW_LLE_end_of_list: break; @@ -266,20 +297,21 @@ if (DumpOpts.Verbose) OS << format("%*s", MaxEncodingStringLength, (const char *)"=> "); }; - switch (Kind) { + switch (Entry.Kind) { case dwarf::DW_LLE_startx_length: PrintPrefix(); - OS << "Addr idx " << Value0 << " (w/ length " << Value1 << "): "; + OS << "Addr idx " << Entry.Value0 << " (w/ length " << Entry.Value1 + << "): "; break; case dwarf::DW_LLE_start_length: PrintPrefix(); - DWARFAddressRange(Value0, Value0 + Value1) + DWARFAddressRange(Entry.Value0, Entry.Value0 + Entry.Value1) .dump(OS, AddressSize, DumpOpts); OS << ": "; break; case dwarf::DW_LLE_offset_pair: PrintPrefix(); - DWARFAddressRange(BaseAddr + Value0, BaseAddr + Value1) + DWARFAddressRange(BaseAddr + Entry.Value0, BaseAddr + Entry.Value1) .dump(OS, AddressSize, DumpOpts); OS << ": "; break; @@ -292,7 +324,7 @@ return; break; case dwarf::DW_LLE_base_address: - BaseAddr = Value0; + BaseAddr = Entry.Value0; if (!DumpOpts.Verbose) return; break; @@ -300,7 +332,7 @@ llvm_unreachable("unreachable locations list kind"); } - dumpExpression(OS, Loc, IsLittleEndian, AddressSize, MRI, U); + dumpExpression(OS, Entry.Loc, IsLittleEndian, AddressSize, MRI, U); } void DWARFDebugLoclists::dumpRange(const DWARFDataExtractor &Data, diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -1302,7 +1302,7 @@ if (const DWARFDebugLoc *DebugLoc = DCtx.getDebugLoc()) { if (const DWARFDebugLoc::LocationList *LocList = DebugLoc->getLocationListAtOffset(*Offset)) { - if (any_of(LocList->Entries, [&](const DWARFDebugLoc::Entry &E) { + if (any_of(LocList->Entries, [&](const DWARFLocationEntry &E) { return ContainsInterestingOperators(E.Loc); })) return true; diff --git a/llvm/tools/llvm-dwarfdump/Statistics.cpp b/llvm/tools/llvm-dwarfdump/Statistics.cpp --- a/llvm/tools/llvm-dwarfdump/Statistics.cpp +++ b/llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -249,15 +249,22 @@ // Get PC coverage. if (auto DebugLocOffset = FormValue->getAsSectionOffset()) { auto *DebugLoc = Die.getDwarfUnit()->getContext().getDebugLoc(); + // TODO: This code does not handle DWARF5 nor DWARF4 base address + // selection entries. This should use a higher-level API which abstracts + // these away. if (auto List = DebugLoc->getLocationListAtOffset(*DebugLocOffset)) { - for (auto Entry : List->Entries) { - uint64_t BytesEntryCovered = Entry.End - Entry.Begin; + ArrayRef Entries = List->Entries; + // Ignore end-of-list entries + Entries = Entries.drop_back(); + + for (auto Entry : Entries) { + uint64_t BytesEntryCovered = Entry.Value1 - Entry.Value0; BytesCovered += BytesEntryCovered; if (IsEntryValue(Entry.Loc)) BytesEntryValuesCovered += BytesEntryCovered; } - if (List->Entries.size()) { - uint64_t FirstDef = List->Entries[0].Begin; + if (Entries.size()) { + uint64_t FirstDef = Entries[0].Value0; uint64_t UnitOfs = UnitLowPC; // Ranges sometimes start before the lexical scope. if (UnitOfs + FirstDef >= ScopeLowPC)