diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDataExtractor.h --- 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 @@ StringRef(reinterpret_cast(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 + getInitialLength(uint64_t *Off, Error *Err = nullptr) const; + + std::pair 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 --- 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 +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 --- a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt @@ -12,6 +12,7 @@ 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 --- /dev/null +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDataExtractorTest.cpp @@ -0,0 +1,94 @@ +//===- 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 Bytes) + -> Expected> { + 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 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 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