Recommit "[DWARF] Add an api to get "interpreted" location lists"
authorPavel Labath <pavel@labath.sk>
Fri, 8 Nov 2019 14:24:11 +0000 (15:24 +0100)
committerPavel Labath <pavel@labath.sk>
Wed, 20 Nov 2019 15:24:11 +0000 (16:24 +0100)
This recommits 089c0f581492cd6e2a3d2927be3fbf60ea2d7e62, 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

llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h
llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h
llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h
llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h
llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp
llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp
llvm/lib/ObjectYAML/DWARFEmitter.cpp
llvm/unittests/DebugInfo/DWARF/CMakeLists.txt
llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp [new file with mode: 0644]

index 2ca98cf..8ac3e2a 100644 (file)
@@ -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;
 
index 52a0247..158bd82 100644 (file)
@@ -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.
index 1522658..35aa1a7 100644 (file)
@@ -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
index d0a8533..98d7a7e 100644 (file)
@@ -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.
index 1af51a8..8b84822 100644 (file)
@@ -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(
index a11865e..6cf3027 100644 (file)
@@ -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;
index 2eb7d2f..e5d33e1 100644 (file)
@@ -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();
index 2ae6699..b410fed 100644 (file)
@@ -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);
index 2181e05..4fb79b2 100644 (file)
@@ -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 (file)
index 0000000..5312492
--- /dev/null
@@ -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