//===- 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(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(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(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(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(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(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(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(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")); }