Skip to content

Commit

Permalink
Recommit "[DWARF] Add an api to get "interpreted" location lists"
Browse files Browse the repository at this point in the history
This recommits 089c0f5, which was
reverted due to failing tests on big endian machines. It includes a fix
which I believe (I don't have BE machine) should fix this issue. The fix
consists of correcting the invocation DWARFYAML::EmitDebugSections,
which was missing one (default) function arguments, and so didn't
actually force the little-endian mode.

The original commit message follows.

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
  • Loading branch information
labath committed Nov 20, 2019
1 parent b80033e commit a03435e
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 1 deletion.
5 changes: 5 additions & 0 deletions llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h
Expand Up @@ -63,6 +63,11 @@ class DWARFLocationTable {
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;

Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h
Expand Up @@ -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>
Expand Down Expand Up @@ -231,6 +232,9 @@ class DWARFDie {

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.
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h
Expand Up @@ -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
3 changes: 3 additions & 0 deletions llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h
Expand Up @@ -439,6 +439,9 @@ class DWARFUnit {
}
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.
Expand Down
15 changes: 15 additions & 0 deletions llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp
Expand Up @@ -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(
Expand Down
21 changes: 21 additions & 0 deletions llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
Expand Up @@ -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;
Expand Down
24 changes: 24 additions & 0 deletions llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
Expand Up @@ -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();
Expand Down
5 changes: 4 additions & 1 deletion llvm/lib/ObjectYAML/DWARFEmitter.cpp
Expand Up @@ -314,7 +314,10 @@ class DIEFixupVisitor : public DWARFYAML::Visitor {
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);
Expand Down
1 change: 1 addition & 0 deletions llvm/unittests/DebugInfo/DWARF/CMakeLists.txt
Expand Up @@ -13,6 +13,7 @@ add_llvm_unittest(DebugInfoDWARFTests
DwarfUtils.cpp
DWARFDebugInfoTest.cpp
DWARFDebugLineTest.cpp
DWARFDieTest.cpp
DWARFFormValueTest.cpp
DWARFLocationExpressionTest.cpp
)
Expand Down
118 changes: 118 additions & 0 deletions llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp
@@ -0,0 +1,118 @@
//===- 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(DWARFDie, 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), /*ApplyFixups=*/true,
/*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, 4, /*isLittleEndian=*/true);
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

0 comments on commit a03435e

Please sign in to comment.