
To make DWARFDebugAbbrev more amenable to error-handling, I would like to change the return type of DWARFDebugAbbrev::parse from `void` to `Error`. Users of DWARFDebugAbbrev can consume the error if they want to use all the valid DWARF that was parsed (without worrying about the malformed DWARF) or stop when the parse fails if the use case needs to be strict. This also will bring the LLVM DWARFDebugAbbrev interface closer to LLDB's which opens up the opportunity for LLDB adopt the LLVM implementation with minimal changes.
440 lines
15 KiB
C++
440 lines
15 KiB
C++
//===- llvm/unittest/DebugInfo/DWARFDebugAbbrevTest.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/DWARFDebugAbbrev.h"
|
|
#include "llvm/BinaryFormat/Dwarf.h"
|
|
#include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h"
|
|
#include "llvm/Support/DataExtractor.h"
|
|
#include "llvm/Support/LEB128.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Testing/Support/Error.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
using namespace dwarf;
|
|
|
|
enum OrderKind : bool { InOrder, OutOfOrder };
|
|
|
|
void writeValidAbbreviationDeclarations(raw_ostream &OS, uint32_t FirstCode,
|
|
OrderKind Order) {
|
|
encodeULEB128(FirstCode, OS);
|
|
encodeULEB128(DW_TAG_compile_unit, OS);
|
|
OS << static_cast<uint8_t>(DW_CHILDREN_yes);
|
|
encodeULEB128(DW_AT_name, OS);
|
|
encodeULEB128(DW_FORM_strp, OS);
|
|
encodeULEB128(0, OS);
|
|
encodeULEB128(0, OS);
|
|
|
|
uint32_t SecondCode =
|
|
Order == OrderKind::InOrder ? FirstCode + 1 : FirstCode - 1;
|
|
|
|
encodeULEB128(SecondCode, OS);
|
|
encodeULEB128(DW_TAG_subprogram, OS);
|
|
OS << static_cast<uint8_t>(DW_CHILDREN_no);
|
|
encodeULEB128(DW_AT_name, OS);
|
|
encodeULEB128(DW_FORM_strp, OS);
|
|
encodeULEB128(0, OS);
|
|
encodeULEB128(0, OS);
|
|
}
|
|
|
|
void writeMalformedULEB128Value(raw_ostream &OS) {
|
|
OS << static_cast<uint8_t>(0x80);
|
|
}
|
|
|
|
void writeULEB128LargerThan64Bits(raw_ostream &OS) {
|
|
static constexpr llvm::StringRef LargeULEB128 =
|
|
"\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01";
|
|
OS << LargeULEB128;
|
|
}
|
|
|
|
TEST(DWARFDebugAbbrevTest, DWARFAbbrevDeclSetExtractSuccess) {
|
|
SmallString<64> RawData;
|
|
raw_svector_ostream OS(RawData);
|
|
uint32_t FirstCode = 5;
|
|
|
|
writeValidAbbreviationDeclarations(OS, FirstCode, InOrder);
|
|
encodeULEB128(0, OS);
|
|
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
ASSERT_THAT_ERROR(AbbrevSet.extract(Data, &Offset), Succeeded());
|
|
// The Abbreviation Declarations are in order and contiguous, so we want to
|
|
// make sure that FirstAbbrCode was correctly set.
|
|
EXPECT_EQ(AbbrevSet.getFirstAbbrCode(), FirstCode);
|
|
|
|
const DWARFAbbreviationDeclaration *Abbrev5 =
|
|
AbbrevSet.getAbbreviationDeclaration(FirstCode);
|
|
ASSERT_TRUE(Abbrev5);
|
|
EXPECT_EQ(Abbrev5->getTag(), DW_TAG_compile_unit);
|
|
EXPECT_TRUE(Abbrev5->hasChildren());
|
|
EXPECT_EQ(Abbrev5->getNumAttributes(), 1u);
|
|
|
|
const DWARFAbbreviationDeclaration *Abbrev6 =
|
|
AbbrevSet.getAbbreviationDeclaration(FirstCode + 1);
|
|
ASSERT_TRUE(Abbrev6);
|
|
EXPECT_EQ(Abbrev6->getTag(), DW_TAG_subprogram);
|
|
EXPECT_FALSE(Abbrev6->hasChildren());
|
|
EXPECT_EQ(Abbrev6->getNumAttributes(), 1u);
|
|
}
|
|
|
|
TEST(DWARFDebugAbbrevTest, DWARFAbbrevDeclSetExtractSuccessOutOfOrder) {
|
|
SmallString<64> RawData;
|
|
raw_svector_ostream OS(RawData);
|
|
uint32_t FirstCode = 2;
|
|
|
|
writeValidAbbreviationDeclarations(OS, FirstCode, OutOfOrder);
|
|
encodeULEB128(0, OS);
|
|
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
ASSERT_THAT_ERROR(AbbrevSet.extract(Data, &Offset), Succeeded());
|
|
// The declarations are out of order, ensure that FirstAbbrCode is UINT32_MAX.
|
|
EXPECT_EQ(AbbrevSet.getFirstAbbrCode(), UINT32_MAX);
|
|
|
|
const DWARFAbbreviationDeclaration *Abbrev2 =
|
|
AbbrevSet.getAbbreviationDeclaration(FirstCode);
|
|
ASSERT_TRUE(Abbrev2);
|
|
EXPECT_EQ(Abbrev2->getTag(), DW_TAG_compile_unit);
|
|
EXPECT_TRUE(Abbrev2->hasChildren());
|
|
EXPECT_EQ(Abbrev2->getNumAttributes(), 1u);
|
|
|
|
const DWARFAbbreviationDeclaration *Abbrev1 =
|
|
AbbrevSet.getAbbreviationDeclaration(FirstCode - 1);
|
|
ASSERT_TRUE(Abbrev1);
|
|
EXPECT_EQ(Abbrev1->getTag(), DW_TAG_subprogram);
|
|
EXPECT_FALSE(Abbrev1->hasChildren());
|
|
EXPECT_EQ(Abbrev1->getNumAttributes(), 1u);
|
|
}
|
|
|
|
TEST(DWARFDebugAbbrevTest, DWARFAbbreviationDeclSetCodeExtractionError) {
|
|
SmallString<64> RawData;
|
|
|
|
// Check for malformed ULEB128.
|
|
{
|
|
raw_svector_ostream OS(RawData);
|
|
writeMalformedULEB128Value(OS);
|
|
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
ASSERT_THAT_ERROR(
|
|
AbbrevSet.extract(Data, &Offset),
|
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000000: "
|
|
"malformed uleb128, extends past end"));
|
|
EXPECT_EQ(Offset, 0u);
|
|
}
|
|
|
|
RawData.clear();
|
|
// Check for ULEB128 too large to fit into a uin64_t.
|
|
{
|
|
raw_svector_ostream OS(RawData);
|
|
writeULEB128LargerThan64Bits(OS);
|
|
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
ASSERT_THAT_ERROR(
|
|
AbbrevSet.extract(Data, &Offset),
|
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000000: "
|
|
"uleb128 too big for uint64"));
|
|
EXPECT_EQ(Offset, 0u);
|
|
}
|
|
}
|
|
|
|
TEST(DWARFDebugAbbrevTest, DWARFAbbreviationDeclSetTagExtractionError) {
|
|
SmallString<64> RawData;
|
|
const uint32_t Code = 1;
|
|
|
|
// Check for malformed ULEB128.
|
|
{
|
|
raw_svector_ostream OS(RawData);
|
|
encodeULEB128(Code, OS);
|
|
writeMalformedULEB128Value(OS);
|
|
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
ASSERT_THAT_ERROR(
|
|
AbbrevSet.extract(Data, &Offset),
|
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
|
"malformed uleb128, extends past end"));
|
|
// Only the code was extracted correctly.
|
|
EXPECT_EQ(Offset, 1u);
|
|
}
|
|
|
|
RawData.clear();
|
|
// Check for ULEB128 too large to fit into a uint64_t.
|
|
{
|
|
raw_svector_ostream OS(RawData);
|
|
encodeULEB128(Code, OS);
|
|
writeULEB128LargerThan64Bits(OS);
|
|
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
ASSERT_THAT_ERROR(
|
|
AbbrevSet.extract(Data, &Offset),
|
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000001: "
|
|
"uleb128 too big for uint64"));
|
|
// Only the code was extracted correctly.
|
|
EXPECT_EQ(Offset, 1u);
|
|
}
|
|
}
|
|
|
|
TEST(DWARFDebugAbbrevTest, DWARFAbbreviationDeclSetChildExtractionError) {
|
|
SmallString<64> RawData;
|
|
const uint32_t Code = 1;
|
|
const dwarf::Tag Tag = DW_TAG_compile_unit;
|
|
|
|
// We want to make sure that we fail if we reach the end of the stream before
|
|
// reading the 'children' byte.
|
|
raw_svector_ostream OS(RawData);
|
|
encodeULEB128(Code, OS);
|
|
encodeULEB128(Tag, OS);
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
ASSERT_THAT_ERROR(
|
|
AbbrevSet.extract(Data, &Offset),
|
|
FailedWithMessage(
|
|
"unexpected end of data at offset 0x2 while reading [0x2, 0x3)"));
|
|
// The code and the tag were extracted correctly.
|
|
EXPECT_EQ(Offset, 2u);
|
|
}
|
|
|
|
TEST(DWARFDebugAbbrevTest, DWARFAbbreviationDeclSetAttributeExtractionError) {
|
|
SmallString<64> RawData;
|
|
const uint32_t Code = 1;
|
|
const dwarf::Tag Tag = DW_TAG_compile_unit;
|
|
const uint8_t Children = DW_CHILDREN_yes;
|
|
|
|
// Check for malformed ULEB128.
|
|
{
|
|
raw_svector_ostream OS(RawData);
|
|
encodeULEB128(Code, OS);
|
|
encodeULEB128(Tag, OS);
|
|
OS << Children;
|
|
writeMalformedULEB128Value(OS);
|
|
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
ASSERT_THAT_ERROR(
|
|
AbbrevSet.extract(Data, &Offset),
|
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000003: "
|
|
"malformed uleb128, extends past end"));
|
|
// The code, tag, and child byte were extracted correctly.
|
|
EXPECT_EQ(Offset, 3u);
|
|
}
|
|
|
|
RawData.clear();
|
|
// Check for ULEB128 too large to fit into a uint64_t.
|
|
{
|
|
raw_svector_ostream OS(RawData);
|
|
encodeULEB128(Code, OS);
|
|
encodeULEB128(Tag, OS);
|
|
OS << Children;
|
|
writeULEB128LargerThan64Bits(OS);
|
|
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
ASSERT_THAT_ERROR(
|
|
AbbrevSet.extract(Data, &Offset),
|
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000003: "
|
|
"uleb128 too big for uint64"));
|
|
// The code, tag, and child byte were extracted correctly.
|
|
EXPECT_EQ(Offset, 3u);
|
|
}
|
|
}
|
|
|
|
TEST(DWARFDebugAbbrevTest, DWARFAbbreviationDeclSetFormExtractionError) {
|
|
SmallString<64> RawData;
|
|
const uint32_t Code = 1;
|
|
const dwarf::Tag Tag = DW_TAG_compile_unit;
|
|
const uint8_t Children = DW_CHILDREN_yes;
|
|
const dwarf::Attribute Attr = DW_AT_name;
|
|
|
|
// Check for malformed ULEB128.
|
|
{
|
|
raw_svector_ostream OS(RawData);
|
|
encodeULEB128(Code, OS);
|
|
encodeULEB128(Tag, OS);
|
|
OS << Children;
|
|
encodeULEB128(Attr, OS);
|
|
writeMalformedULEB128Value(OS);
|
|
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
ASSERT_THAT_ERROR(
|
|
AbbrevSet.extract(Data, &Offset),
|
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000004: "
|
|
"malformed uleb128, extends past end"));
|
|
// The code, tag, child byte, and first attribute were extracted correctly.
|
|
EXPECT_EQ(Offset, 4u);
|
|
}
|
|
|
|
RawData.clear();
|
|
// Check for ULEB128 too large to fit into a uint64_t.
|
|
{
|
|
raw_svector_ostream OS(RawData);
|
|
encodeULEB128(Code, OS);
|
|
encodeULEB128(Tag, OS);
|
|
OS << Children;
|
|
encodeULEB128(Attr, OS);
|
|
writeULEB128LargerThan64Bits(OS);
|
|
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
ASSERT_THAT_ERROR(
|
|
AbbrevSet.extract(Data, &Offset),
|
|
FailedWithMessage("unable to decode LEB128 at offset 0x00000004: "
|
|
"uleb128 too big for uint64"));
|
|
// The code, tag, child byte, and first attribute were extracted correctly.
|
|
EXPECT_EQ(Offset, 4u);
|
|
}
|
|
}
|
|
|
|
TEST(DWARFDebugAbbrevTest, DWARFAbbrevDeclSetInvalidTag) {
|
|
SmallString<64> RawData;
|
|
raw_svector_ostream OS(RawData);
|
|
uint32_t FirstCode = 1;
|
|
// First, we're going to manually add good data.
|
|
writeValidAbbreviationDeclarations(OS, FirstCode, InOrder);
|
|
|
|
// Afterwards, we're going to write an Abbreviation Decl manually with an
|
|
// invalid tag.
|
|
encodeULEB128(FirstCode + 2, OS);
|
|
encodeULEB128(0, OS); // Invalid Tag
|
|
OS << static_cast<uint8_t>(DW_CHILDREN_no);
|
|
encodeULEB128(DW_AT_name, OS);
|
|
encodeULEB128(DW_FORM_strp, OS);
|
|
encodeULEB128(0, OS);
|
|
encodeULEB128(0, OS);
|
|
|
|
encodeULEB128(0, OS);
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
EXPECT_THAT_ERROR(
|
|
AbbrevSet.extract(Data, &Offset),
|
|
FailedWithMessage("abbreviation declaration requires a non-null tag"));
|
|
}
|
|
|
|
TEST(DWARFDebugAbbrevTest, DWARFAbbrevDeclSetInvalidAttrValidForm) {
|
|
SmallString<64> RawData;
|
|
raw_svector_ostream OS(RawData);
|
|
uint32_t FirstCode = 120;
|
|
// First, we're going to manually add good data.
|
|
writeValidAbbreviationDeclarations(OS, FirstCode, InOrder);
|
|
|
|
// Afterwards, we're going to write an Abbreviation Decl manually with an
|
|
// invalid attribute but valid form.
|
|
encodeULEB128(FirstCode - 5, OS);
|
|
encodeULEB128(DW_TAG_compile_unit, OS);
|
|
OS << static_cast<uint8_t>(DW_CHILDREN_no);
|
|
encodeULEB128(0, OS); // Invalid attribute followed by an invalid form.
|
|
encodeULEB128(DW_FORM_strp, OS);
|
|
encodeULEB128(0, OS);
|
|
encodeULEB128(0, OS);
|
|
|
|
encodeULEB128(0, OS);
|
|
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
EXPECT_THAT_ERROR(
|
|
AbbrevSet.extract(Data, &Offset),
|
|
FailedWithMessage(
|
|
"malformed abbreviation declaration attribute. Either the "
|
|
"attribute or the form is zero while the other is not"));
|
|
}
|
|
|
|
TEST(DWARFDebugAbbrevTest, DWARFAbbrevDeclSetValidAttrInvalidForm) {
|
|
SmallString<64> RawData;
|
|
raw_svector_ostream OS(RawData);
|
|
uint32_t FirstCode = 120;
|
|
// First, we're going to manually add good data.
|
|
writeValidAbbreviationDeclarations(OS, FirstCode, InOrder);
|
|
|
|
// Afterwards, we're going to write an Abbreviation Decl manually with a
|
|
// valid attribute but invalid form.
|
|
encodeULEB128(FirstCode - 5, OS);
|
|
encodeULEB128(DW_TAG_compile_unit, OS);
|
|
OS << static_cast<uint8_t>(DW_CHILDREN_no);
|
|
encodeULEB128(DW_AT_name, OS);
|
|
encodeULEB128(0, OS); // Invalid form after a valid attribute.
|
|
encodeULEB128(0, OS);
|
|
encodeULEB128(0, OS);
|
|
|
|
encodeULEB128(0, OS);
|
|
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
EXPECT_THAT_ERROR(
|
|
AbbrevSet.extract(Data, &Offset),
|
|
FailedWithMessage(
|
|
"malformed abbreviation declaration attribute. Either the "
|
|
"attribute or the form is zero while the other is not"));
|
|
}
|
|
|
|
TEST(DWARFDebugAbbrevTest, DWARFAbbrevDeclSetMissingTerminator) {
|
|
SmallString<64> RawData;
|
|
raw_svector_ostream OS(RawData);
|
|
uint32_t FirstCode = 120;
|
|
// First, we're going to manually add good data.
|
|
writeValidAbbreviationDeclarations(OS, FirstCode, InOrder);
|
|
|
|
// Afterwards, we're going to write an Abbreviation Decl manually without a
|
|
// termintating sequence.
|
|
encodeULEB128(FirstCode + 7, OS);
|
|
encodeULEB128(DW_TAG_compile_unit, OS);
|
|
OS << static_cast<uint8_t>(DW_CHILDREN_no);
|
|
encodeULEB128(DW_AT_name, OS);
|
|
encodeULEB128(DW_FORM_strp, OS);
|
|
|
|
uint64_t Offset = 0;
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFAbbreviationDeclarationSet AbbrevSet;
|
|
EXPECT_THAT_ERROR(
|
|
AbbrevSet.extract(Data, &Offset),
|
|
FailedWithMessage(
|
|
"abbreviation declaration attribute list was not terminated with a "
|
|
"null entry"));
|
|
}
|
|
|
|
TEST(DWARFDebugAbbrevTest, DWARFDebugAbbrevParseError) {
|
|
SmallString<64> RawData;
|
|
raw_svector_ostream OS(RawData);
|
|
const uint32_t FirstCode = 70;
|
|
// First, we're going to manually add good data.
|
|
writeValidAbbreviationDeclarations(OS, FirstCode, InOrder);
|
|
|
|
// Afterwards, we're going to write an Abbreviation Decl manually without a
|
|
// termintating sequence.
|
|
encodeULEB128(FirstCode - 1, OS);
|
|
encodeULEB128(DW_TAG_compile_unit, OS);
|
|
OS << static_cast<uint8_t>(DW_CHILDREN_no);
|
|
encodeULEB128(DW_AT_name, OS);
|
|
encodeULEB128(DW_FORM_strp, OS);
|
|
|
|
// The specific error should percolate up to the DWARFDebugAbbrev::parse().
|
|
DataExtractor Data(RawData, sys::IsLittleEndianHost, sizeof(uint64_t));
|
|
DWARFDebugAbbrev DebugAbbrev(Data);
|
|
EXPECT_THAT_ERROR(
|
|
DebugAbbrev.parse(),
|
|
FailedWithMessage(
|
|
"abbreviation declaration attribute list was not terminated with a "
|
|
"null entry"));
|
|
}
|