[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
This commit is contained in:
Pavel Labath 2019-11-08 15:24:11 +01:00
parent 979592a6f7
commit 089c0f5814
10 changed files with 197 additions and 1 deletions

View File

@ -63,6 +63,11 @@ public:
const MCRegisterInfo *MRI, DWARFUnit *U, const MCRegisterInfo *MRI, DWARFUnit *U,
DIDumpOptions DumpOpts, unsigned Indent) const; 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: protected:
DWARFDataExtractor Data; DWARFDataExtractor Data;

View File

@ -18,6 +18,7 @@
#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h" #include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
#include "llvm/DebugInfo/DWARF/DWARFAttribute.h" #include "llvm/DebugInfo/DWARF/DWARFAttribute.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <iterator> #include <iterator>
@ -231,6 +232,9 @@ public:
bool addressRangeContainsAddress(const uint64_t Address) const; 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 /// If a DIE represents a subprogram (or inlined subroutine), returns its
/// mangled name (or short name, if mangled is missing). This name may be /// mangled name (or short name, if mangled is missing). This name may be
/// fetched from specification or abstract origin for this subprogram. /// fetched from specification or abstract origin for this subprogram.

View File

@ -41,6 +41,9 @@ inline bool operator!=(const DWARFLocationExpression &L,
raw_ostream &operator<<(raw_ostream &OS, const DWARFLocationExpression &Loc); raw_ostream &operator<<(raw_ostream &OS, const DWARFLocationExpression &Loc);
/// Represents a set of absolute location expressions.
using DWARFLocationExpressionsVector = std::vector<DWARFLocationExpression>;
} // end namespace llvm } // end namespace llvm
#endif // LLVM_DEBUGINFO_DWARF_DWARFLOCATIONEXPRESSION_H #endif // LLVM_DEBUGINFO_DWARF_DWARFLOCATIONEXPRESSION_H

View File

@ -439,6 +439,9 @@ public:
} }
Expected<DWARFAddressRangesVector> collectAddressRanges(); Expected<DWARFAddressRangesVector> collectAddressRanges();
Expected<DWARFLocationExpressionsVector>
findLoclistFromOffset(uint64_t Offset);
/// Returns subprogram DIE with address range encompassing the provided /// Returns subprogram DIE with address range encompassing the provided
/// address. The pointer is alive as long as parsed compile unit DIEs are not /// address. The pointer is alive as long as parsed compile unit DIEs are not
/// cleared. /// cleared.

View File

@ -151,6 +151,21 @@ bool DWARFLocationTable::dumpLocationList(uint64_t *Offset, raw_ostream &OS,
return true; 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::LocationList const *
DWARFDebugLoc::getLocationListAtOffset(uint64_t Offset) const { DWARFDebugLoc::getLocationListAtOffset(uint64_t Offset) const {
auto It = partition_point( auto It = partition_point(

View File

@ -486,6 +486,27 @@ bool DWARFDie::addressRangeContainsAddress(const uint64_t Address) const {
return false; 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 { const char *DWARFDie::getSubroutineName(DINameKind Kind) const {
if (!isSubroutineDIE()) if (!isSubroutineDIE())
return nullptr; return nullptr;

View File

@ -637,6 +637,30 @@ Expected<DWARFAddressRangesVector> DWARFUnit::collectAddressRanges() {
return *CUDIERangesOrError; 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) { void DWARFUnit::updateAddressDieMap(DWARFDie Die) {
if (Die.isSubroutineDIE()) { if (Die.isSubroutineDIE()) {
auto DIERangesOrError = Die.getAddressRanges(); auto DIERangesOrError = Die.getAddressRanges();

View File

@ -314,7 +314,10 @@ public:
DIEFixupVisitor(DWARFYAML::Data &DI) : DWARFYAML::Visitor(DI){}; DIEFixupVisitor(DWARFYAML::Data &DI) : DWARFYAML::Visitor(DI){};
private: 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) { virtual void onEndCompileUnit(DWARFYAML::Unit &CU) {
CU.Length.setLength(Length); CU.Length.setLength(Length);

View File

@ -13,6 +13,7 @@ add_llvm_unittest(DebugInfoDWARFTests
DwarfUtils.cpp DwarfUtils.cpp
DWARFDebugInfoTest.cpp DWARFDebugInfoTest.cpp
DWARFDebugLineTest.cpp DWARFDebugLineTest.cpp
DWARFDieTest.cpp
DWARFFormValueTest.cpp DWARFFormValueTest.cpp
DWARFLocationExpressionTest.cpp DWARFLocationExpressionTest.cpp
) )

View File

@ -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