blob: 871dbf1a10f438c2f17502272ca32dee471b6f95 [file] [log] [blame]
//===- 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"));
}