[llvm] 3c36d8d - Introduce DWARFDataExtractor::getInitialLength

Pavel Labath via llvm-commits llvm-commits at lists.llvm.org
Wed Feb 26 08:09:26 PST 2020


Author: Pavel Labath
Date: 2020-02-26T17:07:58+01:00
New Revision: 3c36d8dad57712f6616a197632e754541e71edf4

URL: https://github.com/llvm/llvm-project/commit/3c36d8dad57712f6616a197632e754541e71edf4
DIFF: https://github.com/llvm/llvm-project/commit/3c36d8dad57712f6616a197632e754541e71edf4.diff

LOG: Introduce DWARFDataExtractor::getInitialLength

Summary:
This patch introduces a function to house the code needed to do the
DWARF64 detection dance. The function decodes the initial length field
and returns it as a pair containing the actual length, and the DWARF
encoding.

This patch does _not_ attempt to handle the problem of detecting lengths
which extend past the size of the section, or cases when reads of a
single contribution accidentally escape beyond its specified length, but
I think it's useful in its own right.

Reviewers: dblaikie, jhenderson, ikudrin

Subscribers: hiraditya, probinson, aprantl, JDevlieghere, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D74560

Added: 
    llvm/unittests/DebugInfo/DWARF/DWARFDataExtractorTest.cpp

Modified: 
    llvm/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h
    llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp
    llvm/unittests/DebugInfo/DWARF/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h
index c4ba982ce808..1e5ed09b57b3 100644
--- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h
+++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_DEBUGINFO_DWARFDATAEXTRACTOR_H
 #define LLVM_DEBUGINFO_DWARFDATAEXTRACTOR_H
 
+#include "llvm/BinaryFormat/Dwarf.h"
 #include "llvm/DebugInfo/DWARF/DWARFSection.h"
 #include "llvm/Support/DataExtractor.h"
 
@@ -38,11 +39,27 @@ class DWARFDataExtractor : public DataExtractor {
             StringRef(reinterpret_cast<const char *>(Data.data()), Data.size()),
             IsLittleEndian, AddressSize) {}
 
+  /// Extracts the DWARF "initial length" field, which can either be a 32-bit
+  /// value smaller than 0xfffffff0, or the value 0xffffffff followed by a
+  /// 64-bit length. Returns the actual length, and the DWARF format which is
+  /// encoded in the field. In case of errors, it returns {0, DWARF32} and
+  /// leaves the offset unchanged.
+  std::pair<uint64_t, dwarf::DwarfFormat>
+  getInitialLength(uint64_t *Off, Error *Err = nullptr) const;
+
+  std::pair<uint64_t, dwarf::DwarfFormat> getInitialLength(Cursor &C) const {
+    return getInitialLength(&getOffset(C), &getError(C));
+  }
+
   /// Extracts a value and applies a relocation to the result if
   /// one exists for the given offset.
   uint64_t getRelocatedValue(uint32_t Size, uint64_t *Off,
                              uint64_t *SectionIndex = nullptr,
                              Error *Err = nullptr) const;
+  uint64_t getRelocatedValue(Cursor &C, uint32_t Size,
+                             uint64_t *SectionIndex = nullptr) const {
+    return getRelocatedValue(Size, &getOffset(C), SectionIndex, &getError(C));
+  }
 
   /// Extracts an address-sized value and applies a relocation to the result if
   /// one exists for the given offset.

diff  --git a/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp
index 53e676bc7031..18b86b28ccab 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDataExtractor.cpp
@@ -7,11 +7,42 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
-#include "llvm/BinaryFormat/Dwarf.h"
 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
 
 using namespace llvm;
 
+std::pair<uint64_t, dwarf::DwarfFormat>
+DWARFDataExtractor::getInitialLength(uint64_t *Off, Error *Err) const {
+  ErrorAsOutParameter ErrAsOut(Err);
+  if (Err && *Err)
+    return {0, dwarf::DWARF32};
+
+  Cursor C(*Off);
+  uint64_t Length = getRelocatedValue(C, 4);
+  dwarf::DwarfFormat Format = dwarf::DWARF32;
+  if (Length == dwarf::DW_LENGTH_DWARF64) {
+    Length = getRelocatedValue(C, 8);
+    Format = dwarf::DWARF64;
+  } else if (Length >= dwarf::DW_LENGTH_lo_reserved) {
+    cantFail(C.takeError());
+    if (Err)
+      *Err = createStringError(
+          errc::invalid_argument,
+          "unsupported reserved unit length of value 0x%8.8" PRIx64, Length);
+    return {0, dwarf::DWARF32};
+  }
+
+  if (C) {
+    *Off = C.tell();
+    return {Length, Format};
+  }
+  if (Err)
+    *Err = C.takeError();
+  else
+    consumeError(C.takeError());
+  return {0, dwarf::DWARF32};
+}
+
 uint64_t DWARFDataExtractor::getRelocatedValue(uint32_t Size, uint64_t *Off,
                                                uint64_t *SecNdx,
                                                Error *Err) const {

diff  --git a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt
index 68d7840b6e6d..bb032314aaa3 100644
--- a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt
+++ b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt
@@ -12,6 +12,7 @@ add_llvm_unittest(DebugInfoDWARFTests
   DwarfGenerator.cpp
   DwarfUtils.cpp
   DWARFAcceleratorTableTest.cpp
+  DWARFDataExtractorTest.cpp
   DWARFDebugArangeSetTest.cpp
   DWARFDebugInfoTest.cpp
   DWARFDebugLineTest.cpp

diff  --git a/llvm/unittests/DebugInfo/DWARF/DWARFDataExtractorTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDataExtractorTest.cpp
new file mode 100644
index 000000000000..589ae3ff12b1
--- /dev/null
+++ b/llvm/unittests/DebugInfo/DWARF/DWARFDataExtractorTest.cpp
@@ -0,0 +1,95 @@
+//===- DWARFDataExtractorTest.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/DebugInfo/DWARF/DWARFDataExtractor.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+TEST(DWARFDataExtractorTest, getInitialLength) {
+  auto GetWithError = [](ArrayRef<uint8_t> Bytes)
+      -> Expected<std::tuple<uint64_t, dwarf::DwarfFormat, uint64_t>> {
+    DWARFDataExtractor Data(Bytes, /*IsLittleEndian=*/false, /*AddressSize=*/8);
+    DWARFDataExtractor::Cursor C(0);
+    uint64_t Length;
+    dwarf::DwarfFormat Format;
+    std::tie(Length, Format) = Data.getInitialLength(C);
+    if (C)
+      return std::make_tuple(Length, Format, C.tell());
+
+    EXPECT_EQ(Length, 0u);
+    EXPECT_EQ(Format, dwarf::DWARF32);
+    EXPECT_EQ(C.tell(), 0u);
+    return C.takeError();
+  };
+  auto GetWithoutError = [](ArrayRef<uint8_t> Bytes) {
+    DWARFDataExtractor Data(Bytes, /*IsLittleEndian=*/false, /*AddressSize=*/8);
+    uint64_t Offset = 0;
+    uint64_t Length;
+    dwarf::DwarfFormat Format;
+    std::tie(Length, Format) = Data.getInitialLength(&Offset);
+    return std::make_tuple(Length, Format, Offset);
+  };
+  auto ErrorResult = std::make_tuple(0, dwarf::DWARF32, 0);
+
+  // Empty data.
+  EXPECT_THAT_EXPECTED(GetWithError({}),
+                       FailedWithMessage("unexpected end of data"));
+  EXPECT_EQ(GetWithoutError({}), ErrorResult);
+
+  // Not long enough for the U32 field.
+  EXPECT_THAT_EXPECTED(GetWithError({0x00, 0x01, 0x02}),
+                       FailedWithMessage("unexpected end of data"));
+  EXPECT_EQ(GetWithoutError({0x00, 0x01, 0x02}), ErrorResult);
+
+  EXPECT_THAT_EXPECTED(
+      GetWithError({0x00, 0x01, 0x02, 0x03}),
+      HasValue(std::make_tuple(0x00010203, dwarf::DWARF32, 4)));
+  EXPECT_EQ(GetWithoutError({0x00, 0x01, 0x02, 0x03}),
+            std::make_tuple(0x00010203, dwarf::DWARF32, 4));
+
+  // Zeroes are not an error, but without the Error object it is hard to tell
+  // them apart from a failed read.
+  EXPECT_THAT_EXPECTED(
+      GetWithError({0x00, 0x00, 0x00, 0x00}),
+      HasValue(std::make_tuple(0x00000000, dwarf::DWARF32, 4)));
+  EXPECT_EQ(GetWithoutError({0x00, 0x00, 0x00, 0x00}),
+            std::make_tuple(0x00000000, dwarf::DWARF32, 4));
+
+  // Smallest invalid value.
+  EXPECT_THAT_EXPECTED(
+      GetWithError({0xff, 0xff, 0xff, 0xf0}),
+      FailedWithMessage(
+          "unsupported reserved unit length of value 0xfffffff0"));
+  EXPECT_EQ(GetWithoutError({0xff, 0xff, 0xff, 0xf0}), ErrorResult);
+
+  // DWARF64 marker without the subsequent length field.
+  EXPECT_THAT_EXPECTED(GetWithError({0xff, 0xff, 0xff, 0xff}),
+                       FailedWithMessage("unexpected end of data"));
+  EXPECT_EQ(GetWithoutError({0xff, 0xff, 0xff, 0xff}), ErrorResult);
+
+  // Not enough data for the U64 length.
+  EXPECT_THAT_EXPECTED(
+      GetWithError({0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03}),
+      FailedWithMessage("unexpected end of data"));
+  EXPECT_EQ(GetWithoutError({0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03}),
+            ErrorResult);
+
+  EXPECT_THAT_EXPECTED(
+      GetWithError({0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+                    0x06, 0x07}),
+      HasValue(std::make_tuple(0x0001020304050607, dwarf::DWARF64, 12)));
+  EXPECT_EQ(GetWithoutError({0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03,
+                             0x04, 0x05, 0x06, 0x07}),
+            std::make_tuple(0x0001020304050607, dwarf::DWARF64, 12));
+}
+
+} // namespace


        


More information about the llvm-commits mailing list