Skip to content

Commit 089c0f5

Browse files
committed
[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
1 parent 979592a commit 089c0f5

File tree

10 files changed

+197
-1
lines changed

10 files changed

+197
-1
lines changed

llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ class DWARFLocationTable {
6363
const MCRegisterInfo *MRI, DWARFUnit *U,
6464
DIDumpOptions DumpOpts, unsigned Indent) const;
6565

66+
Error visitAbsoluteLocationList(
67+
uint64_t Offset, Optional<object::SectionedAddress> BaseAddr,
68+
std::function<Optional<object::SectionedAddress>(uint32_t)> LookupAddr,
69+
function_ref<bool(Expected<DWARFLocationExpression>)> Callback) const;
70+
6671
protected:
6772
DWARFDataExtractor Data;
6873

llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
1919
#include "llvm/DebugInfo/DWARF/DWARFAttribute.h"
2020
#include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h"
21+
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
2122
#include <cassert>
2223
#include <cstdint>
2324
#include <iterator>
@@ -231,6 +232,9 @@ class DWARFDie {
231232

232233
bool addressRangeContainsAddress(const uint64_t Address) const;
233234

235+
Expected<DWARFLocationExpressionsVector>
236+
getLocations(dwarf::Attribute Attr) const;
237+
234238
/// If a DIE represents a subprogram (or inlined subroutine), returns its
235239
/// mangled name (or short name, if mangled is missing). This name may be
236240
/// fetched from specification or abstract origin for this subprogram.

llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ inline bool operator!=(const DWARFLocationExpression &L,
4141

4242
raw_ostream &operator<<(raw_ostream &OS, const DWARFLocationExpression &Loc);
4343

44+
/// Represents a set of absolute location expressions.
45+
using DWARFLocationExpressionsVector = std::vector<DWARFLocationExpression>;
46+
4447
} // end namespace llvm
4548

4649
#endif // LLVM_DEBUGINFO_DWARF_DWARFLOCATIONEXPRESSION_H

llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,9 @@ class DWARFUnit {
439439
}
440440
Expected<DWARFAddressRangesVector> collectAddressRanges();
441441

442+
Expected<DWARFLocationExpressionsVector>
443+
findLoclistFromOffset(uint64_t Offset);
444+
442445
/// Returns subprogram DIE with address range encompassing the provided
443446
/// address. The pointer is alive as long as parsed compile unit DIEs are not
444447
/// cleared.

llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,21 @@ bool DWARFLocationTable::dumpLocationList(uint64_t *Offset, raw_ostream &OS,
151151
return true;
152152
}
153153

154+
Error DWARFLocationTable::visitAbsoluteLocationList(
155+
uint64_t Offset, Optional<SectionedAddress> BaseAddr,
156+
std::function<Optional<SectionedAddress>(uint32_t)> LookupAddr,
157+
function_ref<bool(Expected<DWARFLocationExpression>)> Callback) const {
158+
DWARFLocationInterpreter Interp(BaseAddr, std::move(LookupAddr));
159+
return visitLocationList(&Offset, [&](const DWARFLocationEntry &E) {
160+
Expected<Optional<DWARFLocationExpression>> Loc = Interp.Interpret(E);
161+
if (!Loc)
162+
return Callback(Loc.takeError());
163+
if (*Loc)
164+
return Callback(**Loc);
165+
return true;
166+
});
167+
}
168+
154169
DWARFDebugLoc::LocationList const *
155170
DWARFDebugLoc::getLocationListAtOffset(uint64_t Offset) const {
156171
auto It = partition_point(

llvm/lib/DebugInfo/DWARF/DWARFDie.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,27 @@ bool DWARFDie::addressRangeContainsAddress(const uint64_t Address) const {
486486
return false;
487487
}
488488

489+
Expected<DWARFLocationExpressionsVector>
490+
DWARFDie::getLocations(dwarf::Attribute Attr) const {
491+
Optional<DWARFFormValue> Location = find(Attr);
492+
if (!Location)
493+
return createStringError(inconvertibleErrorCode(), "No %s",
494+
dwarf::AttributeString(Attr).data());
495+
496+
if (Optional<uint64_t> Off = Location->getAsSectionOffset())
497+
return U->findLoclistFromOffset(*Off);
498+
499+
if (Optional<ArrayRef<uint8_t>> Expr = Location->getAsBlock()) {
500+
return DWARFLocationExpressionsVector{
501+
DWARFLocationExpression{None, to_vector<4>(*Expr)}};
502+
}
503+
504+
return createStringError(
505+
inconvertibleErrorCode(), "Unsupported %s encoding: %s",
506+
dwarf::AttributeString(Attr).data(),
507+
dwarf::FormEncodingString(Location->getForm()).data());
508+
}
509+
489510
const char *DWARFDie::getSubroutineName(DINameKind Kind) const {
490511
if (!isSubroutineDIE())
491512
return nullptr;

llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,30 @@ Expected<DWARFAddressRangesVector> DWARFUnit::collectAddressRanges() {
637637
return *CUDIERangesOrError;
638638
}
639639

640+
Expected<DWARFLocationExpressionsVector>
641+
DWARFUnit::findLoclistFromOffset(uint64_t Offset) {
642+
DWARFLocationExpressionsVector Result;
643+
644+
Error InterpretationError = Error::success();
645+
646+
Error ParseError = getLocationTable().visitAbsoluteLocationList(
647+
Offset, getBaseAddress(),
648+
[this](uint32_t Index) { return getAddrOffsetSectionItem(Index); },
649+
[&](Expected<DWARFLocationExpression> L) {
650+
if (L)
651+
Result.push_back(std::move(*L));
652+
else
653+
InterpretationError =
654+
joinErrors(L.takeError(), std::move(InterpretationError));
655+
return !InterpretationError;
656+
});
657+
658+
if (ParseError || InterpretationError)
659+
return joinErrors(std::move(ParseError), std::move(InterpretationError));
660+
661+
return Result;
662+
}
663+
640664
void DWARFUnit::updateAddressDieMap(DWARFDie Die) {
641665
if (Die.isSubroutineDIE()) {
642666
auto DIERangesOrError = Die.getAddressRanges();

llvm/lib/ObjectYAML/DWARFEmitter.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,10 @@ class DIEFixupVisitor : public DWARFYAML::Visitor {
314314
DIEFixupVisitor(DWARFYAML::Data &DI) : DWARFYAML::Visitor(DI){};
315315

316316
private:
317-
virtual void onStartCompileUnit(DWARFYAML::Unit &CU) { Length = 7; }
317+
virtual void onStartCompileUnit(DWARFYAML::Unit &CU) {
318+
// Size of the unit header, excluding the length field itself.
319+
Length = CU.Version >= 5 ? 8 : 7;
320+
}
318321

319322
virtual void onEndCompileUnit(DWARFYAML::Unit &CU) {
320323
CU.Length.setLength(Length);

llvm/unittests/DebugInfo/DWARF/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ add_llvm_unittest(DebugInfoDWARFTests
1313
DwarfUtils.cpp
1414
DWARFDebugInfoTest.cpp
1515
DWARFDebugLineTest.cpp
16+
DWARFDieTest.cpp
1617
DWARFFormValueTest.cpp
1718
DWARFLocationExpressionTest.cpp
1819
)
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//===- llvm/unittest/DebugInfo/DWARFDieTest.cpp ---------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/BinaryFormat/Dwarf.h"
10+
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
11+
#include "llvm/ObjectYAML/DWARFEmitter.h"
12+
#include "llvm/Testing/Support/Error.h"
13+
#include "gtest/gtest.h"
14+
15+
using namespace llvm;
16+
using namespace llvm::dwarf;
17+
using object::SectionedAddress;
18+
19+
namespace {
20+
21+
TEST(DWARFLocationTable, getLocations) {
22+
const char *yamldata = R"(
23+
debug_abbrev:
24+
- Code: 0x00000001
25+
Tag: DW_TAG_compile_unit
26+
Children: DW_CHILDREN_no
27+
Attributes:
28+
- Attribute: DW_AT_location
29+
Form: DW_FORM_sec_offset
30+
- Attribute: DW_AT_data_member_location
31+
Form: DW_FORM_exprloc
32+
- Attribute: DW_AT_vtable_elem_location
33+
Form: DW_FORM_sec_offset
34+
- Attribute: DW_AT_call_data_location
35+
Form: DW_FORM_sec_offset
36+
debug_info:
37+
- Length:
38+
TotalLength: 0
39+
Version: 5
40+
UnitType: DW_UT_compile
41+
AbbrOffset: 0
42+
AddrSize: 4
43+
Entries:
44+
- AbbrCode: 0x00000001
45+
Values:
46+
- Value: 12
47+
- Value: 0x0000000000000001
48+
BlockData: [ 0x47 ]
49+
- Value: 20
50+
- Value: 25
51+
)";
52+
Expected<StringMap<std::unique_ptr<MemoryBuffer>>> Sections =
53+
DWARFYAML::EmitDebugSections(StringRef(yamldata),
54+
/*IsLittleEndian=*/true);
55+
ASSERT_THAT_EXPECTED(Sections, Succeeded());
56+
std::vector<uint8_t> Loclists{
57+
// Header
58+
0, 0, 0, 0, // Length
59+
5, 0, // Version
60+
4, // Address size
61+
0, // Segment selector size
62+
0, 0, 0, 0, // Offset entry count
63+
// First location list.
64+
DW_LLE_start_length, // First entry
65+
1, 0, 0, 0, // Start offset
66+
2, // Length
67+
0, // Expression length
68+
DW_LLE_end_of_list,
69+
// Second location list.
70+
DW_LLE_startx_length, // First entry
71+
1, // Start index
72+
2, // Length
73+
0, // Expression length
74+
DW_LLE_end_of_list,
75+
// Third location list.
76+
DW_LLE_start_length, // First entry
77+
1, 0, 0, 0, // Start offset
78+
2, // Length
79+
0, // Expression length
80+
// end_of_list intentionally missing
81+
};
82+
Loclists[0] = Loclists.size() - 4;
83+
Sections->try_emplace(
84+
"debug_loclists",
85+
MemoryBuffer::getMemBuffer(toStringRef(Loclists), "debug_loclists",
86+
/*RequiresNullTerminator=*/false));
87+
std::unique_ptr<DWARFContext> Ctx = DWARFContext::create(*Sections, 8);
88+
DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0);
89+
ASSERT_NE(nullptr, CU);
90+
DWARFDie Die = CU->getUnitDIE();
91+
ASSERT_TRUE(Die.isValid());
92+
93+
EXPECT_THAT_EXPECTED(Die.getLocations(DW_AT_location),
94+
HasValue(testing::ElementsAre(DWARFLocationExpression{
95+
DWARFAddressRange{1, 3}, {}})));
96+
97+
EXPECT_THAT_EXPECTED(
98+
Die.getLocations(DW_AT_data_member_location),
99+
HasValue(testing::ElementsAre(DWARFLocationExpression{None, {0x47}})));
100+
101+
EXPECT_THAT_EXPECTED(
102+
Die.getLocations(DW_AT_vtable_elem_location),
103+
Failed<ErrorInfoBase>(testing::Property(
104+
&ErrorInfoBase::message,
105+
"Unable to resolve indirect address 1 for: DW_LLE_startx_length")));
106+
107+
EXPECT_THAT_EXPECTED(Die.getLocations(DW_AT_call_data_location),
108+
Failed<ErrorInfoBase>(testing::Property(
109+
&ErrorInfoBase::message, "unexpected end of data")));
110+
111+
EXPECT_THAT_EXPECTED(
112+
Die.getLocations(DW_AT_call_data_value),
113+
Failed<ErrorInfoBase>(testing::Property(&ErrorInfoBase::message,
114+
"No DW_AT_call_data_value")));
115+
}
116+
117+
} // end anonymous namespace

0 commit comments

Comments
 (0)