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 @@ -63,6 +63,11 @@ const MCRegisterInfo *MRI, DWARFUnit *U, DIDumpOptions DumpOpts, unsigned Indent) const; + Error visitAbsoluteLocationList( + uint64_t Offset, Optional BaseAddr, + std::function(uint32_t)> LookupAddr, + function_ref)> Callback) const; + protected: DWARFDataExtractor Data; diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h @@ -18,6 +18,7 @@ #include "llvm/DebugInfo/DWARF/DWARFAddressRange.h" #include "llvm/DebugInfo/DWARF/DWARFAttribute.h" #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include #include #include @@ -231,6 +232,9 @@ bool addressRangeContainsAddress(const uint64_t Address) const; + Expected + getLocations(dwarf::Attribute Attr) const; + /// If a DIE represents a subprogram (or inlined subroutine), returns its /// mangled name (or short name, if mangled is missing). This name may be /// fetched from specification or abstract origin for this subprogram. diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h @@ -41,6 +41,9 @@ raw_ostream &operator<<(raw_ostream &OS, const DWARFLocationExpression &Loc); +/// Represents a set of absolute location expressions. +using DWARFLocationExpressionsVector = std::vector; + } // end namespace llvm #endif // LLVM_DEBUGINFO_DWARF_DWARFLOCATIONEXPRESSION_H diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h @@ -439,6 +439,9 @@ } Expected collectAddressRanges(); + Expected + findLoclistFromOffset(uint64_t Offset); + /// Returns subprogram DIE with address range encompassing the provided /// address. The pointer is alive as long as parsed compile unit DIEs are not /// cleared. 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 @@ -151,6 +151,21 @@ return true; } +Error DWARFLocationTable::visitAbsoluteLocationList( + uint64_t Offset, Optional BaseAddr, + std::function(uint32_t)> LookupAddr, + function_ref)> Callback) const { + DWARFLocationInterpreter Interp(BaseAddr, std::move(LookupAddr)); + return visitLocationList(&Offset, [&](const DWARFLocationEntry &E) { + Expected> Loc = Interp.Interpret(E); + if (!Loc) + return Callback(Loc.takeError()); + if (*Loc) + return Callback(**Loc); + return true; + }); +} + DWARFDebugLoc::LocationList const * DWARFDebugLoc::getLocationListAtOffset(uint64_t Offset) const { auto It = partition_point( diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -486,6 +486,27 @@ return false; } +Expected +DWARFDie::getLocations(dwarf::Attribute Attr) const { + Optional Location = find(Attr); + if (!Location) + return createStringError(inconvertibleErrorCode(), "No %s", + dwarf::AttributeString(Attr).data()); + + if (Optional Off = Location->getAsSectionOffset()) + return U->findLoclistFromOffset(*Off); + + if (Optional> Expr = Location->getAsBlock()) { + return DWARFLocationExpressionsVector{ + DWARFLocationExpression{None, to_vector<4>(*Expr)}}; + } + + return createStringError( + inconvertibleErrorCode(), "Unsupported %s encoding: %s", + dwarf::AttributeString(Attr).data(), + dwarf::FormEncodingString(Location->getForm()).data()); +} + const char *DWARFDie::getSubroutineName(DINameKind Kind) const { if (!isSubroutineDIE()) return nullptr; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -637,6 +637,30 @@ return *CUDIERangesOrError; } +Expected +DWARFUnit::findLoclistFromOffset(uint64_t Offset) { + DWARFLocationExpressionsVector Result; + + Error InterpretationError = Error::success(); + + Error ParseError = getLocationTable().visitAbsoluteLocationList( + Offset, getBaseAddress(), + [this](uint32_t Index) { return getAddrOffsetSectionItem(Index); }, + [&](Expected L) { + if (L) + Result.push_back(std::move(*L)); + else + InterpretationError = + joinErrors(L.takeError(), std::move(InterpretationError)); + return !InterpretationError; + }); + + if (ParseError || InterpretationError) + return joinErrors(std::move(ParseError), std::move(InterpretationError)); + + return Result; +} + void DWARFUnit::updateAddressDieMap(DWARFDie Die) { if (Die.isSubroutineDIE()) { auto DIERangesOrError = Die.getAddressRanges(); diff --git a/llvm/lib/ObjectYAML/DWARFEmitter.cpp b/llvm/lib/ObjectYAML/DWARFEmitter.cpp --- a/llvm/lib/ObjectYAML/DWARFEmitter.cpp +++ b/llvm/lib/ObjectYAML/DWARFEmitter.cpp @@ -314,7 +314,10 @@ DIEFixupVisitor(DWARFYAML::Data &DI) : DWARFYAML::Visitor(DI){}; private: - virtual void onStartCompileUnit(DWARFYAML::Unit &CU) { Length = 7; } + virtual void onStartCompileUnit(DWARFYAML::Unit &CU) { + // Size of the unit header, excluding the length field itself. + Length = CU.Version >= 5 ? 8 : 7; + } virtual void onEndCompileUnit(DWARFYAML::Unit &CU) { CU.Length.setLength(Length); diff --git a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt --- a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt @@ -13,6 +13,7 @@ DwarfUtils.cpp DWARFDebugInfoTest.cpp DWARFDebugLineTest.cpp + DWARFDieTest.cpp DWARFFormValueTest.cpp DWARFLocationExpressionTest.cpp ) diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp @@ -0,0 +1,117 @@ +//===- llvm/unittest/DebugInfo/DWARFDieTest.cpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/ObjectYAML/DWARFEmitter.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::dwarf; +using object::SectionedAddress; + +namespace { + +TEST(DWARFLocationTable, getLocations) { + const char *yamldata = R"( + debug_abbrev: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_location + Form: DW_FORM_sec_offset + - Attribute: DW_AT_data_member_location + Form: DW_FORM_exprloc + - Attribute: DW_AT_vtable_elem_location + Form: DW_FORM_sec_offset + - Attribute: DW_AT_call_data_location + Form: DW_FORM_sec_offset + debug_info: + - Length: + TotalLength: 0 + Version: 5 + UnitType: DW_UT_compile + AbbrOffset: 0 + AddrSize: 4 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 12 + - Value: 0x0000000000000001 + BlockData: [ 0x47 ] + - Value: 20 + - Value: 25 + )"; + Expected>> Sections = + DWARFYAML::EmitDebugSections(StringRef(yamldata), + /*IsLittleEndian=*/true); + ASSERT_THAT_EXPECTED(Sections, Succeeded()); + std::vector Loclists{ + // Header + 0, 0, 0, 0, // Length + 5, 0, // Version + 4, // Address size + 0, // Segment selector size + 0, 0, 0, 0, // Offset entry count + // First location list. + DW_LLE_start_length, // First entry + 1, 0, 0, 0, // Start offset + 2, // Length + 0, // Expression length + DW_LLE_end_of_list, + // Second location list. + DW_LLE_startx_length, // First entry + 1, // Start index + 2, // Length + 0, // Expression length + DW_LLE_end_of_list, + // Third location list. + DW_LLE_start_length, // First entry + 1, 0, 0, 0, // Start offset + 2, // Length + 0, // Expression length + // end_of_list intentionally missing + }; + Loclists[0] = Loclists.size() - 4; + Sections->try_emplace( + "debug_loclists", + MemoryBuffer::getMemBuffer(toStringRef(Loclists), "debug_loclists", + /*RequiresNullTerminator=*/false)); + std::unique_ptr Ctx = DWARFContext::create(*Sections, 8); + DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0); + ASSERT_NE(nullptr, CU); + DWARFDie Die = CU->getUnitDIE(); + ASSERT_TRUE(Die.isValid()); + + EXPECT_THAT_EXPECTED(Die.getLocations(DW_AT_location), + HasValue(testing::ElementsAre(DWARFLocationExpression{ + DWARFAddressRange{1, 3}, {}}))); + + EXPECT_THAT_EXPECTED( + Die.getLocations(DW_AT_data_member_location), + HasValue(testing::ElementsAre(DWARFLocationExpression{None, {0x47}}))); + + EXPECT_THAT_EXPECTED( + Die.getLocations(DW_AT_vtable_elem_location), + Failed(testing::Property( + &ErrorInfoBase::message, + "Unable to resolve indirect address 1 for: DW_LLE_startx_length"))); + + EXPECT_THAT_EXPECTED(Die.getLocations(DW_AT_call_data_location), + Failed(testing::Property( + &ErrorInfoBase::message, "unexpected end of data"))); + + EXPECT_THAT_EXPECTED( + Die.getLocations(DW_AT_call_data_value), + Failed(testing::Property(&ErrorInfoBase::message, + "No DW_AT_call_data_value"))); +} + +} // end anonymous namespace