diff options
author | Pavel Labath <pavel@labath.sk> | 2019-11-08 15:24:11 +0100 |
---|---|---|
committer | Pavel Labath <pavel@labath.sk> | 2019-11-20 13:25:18 +0100 |
commit | 089c0f581492cd6e2a3d2927be3fbf60ea2d7e62 (patch) | |
tree | c99b6523359271ad380b5b039af76805b7dab1dc /llvm | |
parent | 979592a6f735a7111ba2c3a19e5ef3e8d336e01a (diff) | |
download | bcm5719-llvm-089c0f581492cd6e2a3d2927be3fbf60ea2d7e62.tar.gz bcm5719-llvm-089c0f581492cd6e2a3d2927be3fbf60ea2d7e62.zip |
[DWARF] Add an api to get "interpreted" location lists
Summary:
This patch adds DWARFDie::getLocations, which returns the location
expressions for a given attribute (typically DW_AT_location). It handles
both "inline" locations and references to the external location list
sections (currently only of the DW_FORM_sec_offset type). It is
implemented on top of DWARFUnit::findLoclistFromOffset, which is also
added in this patch. I tried to make their signatures similar to the
equivalent range list functionality.
The actual location list interpretation logic is in
DWARFLocationTable::visitAbsoluteLocationList. This part is not
equivalent to the range list code, but this deviation is motivated by a
desire to reuse the same location list parsing code within lldb.
The functionality is tested via a c++ unit test of the DWARFDie API.
Reviewers: dblaikie, JDevlieghere, SouraVX
Subscribers: mgorny, hiraditya, cmtice, probinson, llvm-commits, aprantl
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D70394
Diffstat (limited to 'llvm')
-rw-r--r-- | llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h | 5 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h | 4 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h | 3 | ||||
-rw-r--r-- | llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h | 3 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp | 15 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/DWARF/DWARFDie.cpp | 21 | ||||
-rw-r--r-- | llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp | 24 | ||||
-rw-r--r-- | llvm/lib/ObjectYAML/DWARFEmitter.cpp | 5 | ||||
-rw-r--r-- | llvm/unittests/DebugInfo/DWARF/CMakeLists.txt | 1 | ||||
-rw-r--r-- | llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp | 117 |
10 files changed, 197 insertions, 1 deletions
diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h index 2ca98cfcfff..8ac3e2add88 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h @@ -63,6 +63,11 @@ public: const MCRegisterInfo *MRI, DWARFUnit *U, DIDumpOptions DumpOpts, unsigned Indent) const; + Error visitAbsoluteLocationList( + uint64_t Offset, Optional<object::SectionedAddress> BaseAddr, + std::function<Optional<object::SectionedAddress>(uint32_t)> LookupAddr, + function_ref<bool(Expected<DWARFLocationExpression>)> Callback) const; + protected: DWARFDataExtractor Data; diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h index 52a0247b3e4..158bd82edee 100644 --- 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 <cassert> #include <cstdint> #include <iterator> @@ -231,6 +232,9 @@ public: bool addressRangeContainsAddress(const uint64_t Address) const; + Expected<DWARFLocationExpressionsVector> + 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 index 1522658ead7..35aa1a78e12 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h @@ -41,6 +41,9 @@ inline bool operator!=(const DWARFLocationExpression &L, raw_ostream &operator<<(raw_ostream &OS, const DWARFLocationExpression &Loc); +/// Represents a set of absolute location expressions. +using DWARFLocationExpressionsVector = std::vector<DWARFLocationExpression>; + } // 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 index d0a8533d1e3..98d7a7ee3ca 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h @@ -439,6 +439,9 @@ public: } Expected<DWARFAddressRangesVector> collectAddressRanges(); + Expected<DWARFLocationExpressionsVector> + 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 index 1af51a85d18..8b84822914d 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -151,6 +151,21 @@ bool DWARFLocationTable::dumpLocationList(uint64_t *Offset, raw_ostream &OS, return true; } +Error DWARFLocationTable::visitAbsoluteLocationList( + uint64_t Offset, Optional<SectionedAddress> BaseAddr, + std::function<Optional<SectionedAddress>(uint32_t)> LookupAddr, + function_ref<bool(Expected<DWARFLocationExpression>)> Callback) const { + DWARFLocationInterpreter Interp(BaseAddr, std::move(LookupAddr)); + return visitLocationList(&Offset, [&](const DWARFLocationEntry &E) { + Expected<Optional<DWARFLocationExpression>> Loc = Interp.Interpret(E); + if (!Loc) + return Callback(Loc.takeError()); + if (*Loc) + return Callback(**Loc); + return true; + }); +} + 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 index a11865e048c..6cf30270539 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -486,6 +486,27 @@ bool DWARFDie::addressRangeContainsAddress(const uint64_t Address) const { return false; } +Expected<DWARFLocationExpressionsVector> +DWARFDie::getLocations(dwarf::Attribute Attr) const { + Optional<DWARFFormValue> Location = find(Attr); + if (!Location) + return createStringError(inconvertibleErrorCode(), "No %s", + dwarf::AttributeString(Attr).data()); + + if (Optional<uint64_t> Off = Location->getAsSectionOffset()) + return U->findLoclistFromOffset(*Off); + + if (Optional<ArrayRef<uint8_t>> Expr = Location->getAsBlock()) { + return DWARFLocationExpressionsVector{ + DWARFLocationExpression{None, to_vector<4>(*Expr)}}; + } + + return createStringError( + inconvertibleErrorCode(), "Unsupported %s encoding: %s", + dwarf::AttributeString(Attr).data(), + dwarf::FormEncodingString(Location->getForm()).data()); +} + const char *DWARFDie::getSubroutineName(DINameKind Kind) const { if (!isSubroutineDIE()) return nullptr; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp index 2eb7d2f945a..e5d33e13644 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -637,6 +637,30 @@ Expected<DWARFAddressRangesVector> DWARFUnit::collectAddressRanges() { return *CUDIERangesOrError; } +Expected<DWARFLocationExpressionsVector> +DWARFUnit::findLoclistFromOffset(uint64_t Offset) { + DWARFLocationExpressionsVector Result; + + Error InterpretationError = Error::success(); + + Error ParseError = getLocationTable().visitAbsoluteLocationList( + Offset, getBaseAddress(), + [this](uint32_t Index) { return getAddrOffsetSectionItem(Index); }, + [&](Expected<DWARFLocationExpression> L) { + if (L) + Result.push_back(std::move(*L)); + else + InterpretationError = + joinErrors(L.takeError(), std::move(InterpretationError)); + return !InterpretationError; + }); + + if (ParseError || InterpretationError) + return joinErrors(std::move(ParseError), std::move(InterpretationError)); + + return Result; +} + void DWARFUnit::updateAddressDieMap(DWARFDie Die) { if (Die.isSubroutineDIE()) { auto DIERangesOrError = Die.getAddressRanges(); diff --git a/llvm/lib/ObjectYAML/DWARFEmitter.cpp b/llvm/lib/ObjectYAML/DWARFEmitter.cpp index 2ae66997cf5..b410fed16f0 100644 --- a/llvm/lib/ObjectYAML/DWARFEmitter.cpp +++ b/llvm/lib/ObjectYAML/DWARFEmitter.cpp @@ -314,7 +314,10 @@ public: 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 index 2181e0543d0..4fb79b2a96e 100644 --- a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt @@ -13,6 +13,7 @@ add_llvm_unittest(DebugInfoDWARFTests 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 index 00000000000..05e8e05b927 --- /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<StringMap<std::unique_ptr<MemoryBuffer>>> Sections = + DWARFYAML::EmitDebugSections(StringRef(yamldata), + /*IsLittleEndian=*/true); + ASSERT_THAT_EXPECTED(Sections, Succeeded()); + std::vector<uint8_t> 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<DWARFContext> 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<ErrorInfoBase>(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<ErrorInfoBase>(testing::Property( + &ErrorInfoBase::message, "unexpected end of data"))); + + EXPECT_THAT_EXPECTED( + Die.getLocations(DW_AT_call_data_value), + Failed<ErrorInfoBase>(testing::Property(&ErrorInfoBase::message, + "No DW_AT_call_data_value"))); +} + +} // end anonymous namespace |