diff options
Diffstat (limited to 'unittests/DebugInfo')
| -rw-r--r-- | unittests/DebugInfo/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | unittests/DebugInfo/CodeView/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp | 14 | ||||
| -rw-r--r-- | unittests/DebugInfo/CodeView/TypeHashingTest.cpp | 156 | ||||
| -rw-r--r-- | unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp | 50 | ||||
| -rw-r--r-- | unittests/DebugInfo/DWARF/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp | 992 | ||||
| -rw-r--r-- | unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp | 18 | ||||
| -rw-r--r-- | unittests/DebugInfo/DWARF/DwarfGenerator.cpp | 15 | ||||
| -rw-r--r-- | unittests/DebugInfo/MSF/CMakeLists.txt | 15 | ||||
| -rw-r--r-- | unittests/DebugInfo/MSF/MSFBuilderTest.cpp (renamed from unittests/DebugInfo/PDB/MSFBuilderTest.cpp) | 38 | ||||
| -rw-r--r-- | unittests/DebugInfo/MSF/MSFCommonTest.cpp | 104 | ||||
| -rw-r--r-- | unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp (renamed from unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp) | 72 | ||||
| -rw-r--r-- | unittests/DebugInfo/PDB/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | unittests/DebugInfo/PDB/PDBApiTest.cpp | 9 |
15 files changed, 1359 insertions, 134 deletions
diff --git a/unittests/DebugInfo/CMakeLists.txt b/unittests/DebugInfo/CMakeLists.txt index e38fff58cae6..579fdb202cf4 100644 --- a/unittests/DebugInfo/CMakeLists.txt +++ b/unittests/DebugInfo/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(CodeView) add_subdirectory(DWARF) +add_subdirectory(MSF) add_subdirectory(PDB) diff --git a/unittests/DebugInfo/CodeView/CMakeLists.txt b/unittests/DebugInfo/CodeView/CMakeLists.txt index 6f504d8149b5..d06ccfaba72a 100644 --- a/unittests/DebugInfo/CodeView/CMakeLists.txt +++ b/unittests/DebugInfo/CodeView/CMakeLists.txt @@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS set(DebugInfoCodeViewSources RandomAccessVisitorTest.cpp + TypeHashingTest.cpp TypeIndexDiscoveryTest.cpp ) @@ -11,4 +12,4 @@ add_llvm_unittest(DebugInfoCodeViewTests ${DebugInfoCodeViewSources} ) -target_link_libraries(DebugInfoCodeViewTests LLVMTestingSupport)
\ No newline at end of file +target_link_libraries(DebugInfoCodeViewTests PRIVATE LLVMTestingSupport) diff --git a/unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp b/unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp index 04b7bb0ba936..c84eae32face 100644 --- a/unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp +++ b/unittests/DebugInfo/CodeView/RandomAccessVisitorTest.cpp @@ -7,13 +7,11 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/SmallBitVector.h" +#include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecordMapping.h" -#include "llvm/DebugInfo/CodeView/TypeSerializer.h" -#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" #include "llvm/DebugInfo/PDB/Native/RawTypes.h" #include "llvm/Support/Allocator.h" @@ -95,7 +93,7 @@ public: static void SetUpTestCase() { GlobalState = llvm::make_unique<GlobalTestState>(); - TypeTableBuilder Builder(GlobalState->Allocator); + AppendingTypeTableBuilder Builder(GlobalState->Allocator); uint32_t Offset = 0; for (int I = 0; I < 11; ++I) { @@ -108,7 +106,7 @@ public: Stream << "Array [" << I << "]"; AR.Name = GlobalState->Strings.save(Stream.str()); GlobalState->Records.push_back(AR); - GlobalState->Indices.push_back(Builder.writeKnownType(AR)); + GlobalState->Indices.push_back(Builder.writeLeafType(AR)); CVType Type(TypeLeafKind::LF_ARRAY, Builder.records().back()); GlobalState->TypeVector.push_back(Type); @@ -352,7 +350,7 @@ TEST_F(RandomAccessVisitorTest, InnerChunk) { } TEST_F(RandomAccessVisitorTest, CrossChunkName) { - TypeTableBuilder Builder(GlobalState->Allocator); + AppendingTypeTableBuilder Builder(GlobalState->Allocator); // TypeIndex 0 ClassRecord Class(TypeRecordKind::Class); @@ -363,13 +361,13 @@ TEST_F(RandomAccessVisitorTest, CrossChunkName) { Class.DerivationList = TypeIndex::fromArrayIndex(0); Class.FieldList = TypeIndex::fromArrayIndex(0); Class.VTableShape = TypeIndex::fromArrayIndex(0); - TypeIndex IndexZero = Builder.writeKnownType(Class); + TypeIndex IndexZero = Builder.writeLeafType(Class); // TypeIndex 1 refers to type index 0. ModifierRecord Modifier(TypeRecordKind::Modifier); Modifier.ModifiedType = TypeIndex::fromArrayIndex(0); Modifier.Modifiers = ModifierOptions::Const; - TypeIndex IndexOne = Builder.writeKnownType(Modifier); + TypeIndex IndexOne = Builder.writeLeafType(Modifier); // set up a type stream that refers to the above two serialized records. std::vector<CVType> TypeArray; diff --git a/unittests/DebugInfo/CodeView/TypeHashingTest.cpp b/unittests/DebugInfo/CodeView/TypeHashingTest.cpp new file mode 100644 index 000000000000..5b9dadfb33ff --- /dev/null +++ b/unittests/DebugInfo/CodeView/TypeHashingTest.cpp @@ -0,0 +1,156 @@ +//===- llvm/unittest/DebugInfo/CodeView/TypeHashingTest.cpp ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/CodeView/TypeHashing.h" +#include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::codeview; + +static TypeIndex createPointerRecord(AppendingTypeTableBuilder &Builder, + TypeIndex TI) { + PointerRecord PR(TypeRecordKind::Pointer); + PR.setAttrs(PointerKind::Near32, PointerMode::Pointer, PointerOptions::None, + 4); + PR.ReferentType = TI; + return Builder.writeLeafType(PR); +} + +static TypeIndex createArgListRecord(AppendingTypeTableBuilder &Builder, + TypeIndex Q, TypeIndex R) { + ArgListRecord AR(TypeRecordKind::ArgList); + AR.ArgIndices.push_back(Q); + AR.ArgIndices.push_back(R); + return Builder.writeLeafType(AR); +} + +static TypeIndex createProcedureRecord(AppendingTypeTableBuilder &Builder, + uint32_t ParamCount, TypeIndex Return, + TypeIndex ArgList) { + ProcedureRecord PR(TypeRecordKind::Procedure); + PR.ArgumentList = ArgList; + PR.CallConv = CallingConvention::NearC; + PR.Options = FunctionOptions::None; + PR.ParameterCount = ParamCount; + PR.ReturnType = Return; + return Builder.writeLeafType(PR); +} + +static ArrayRef<uint8_t> hash_of(ArrayRef<GloballyHashedType> Hashes, + TypeIndex TI) { + return Hashes[TI.toArrayIndex()].Hash; +} + +static void verifyHashUniqueness(ArrayRef<GloballyHashedType> Hashes) { + assert(!Hashes.empty()); + + for (size_t I = 0; I < Hashes.size() - 1; ++I) { + for (size_t J = I + 1; J < Hashes.size(); ++J) { + EXPECT_NE(Hashes[I].Hash, Hashes[J].Hash); + } + } +} + +TEST(TypeHashingTest, ContentHash) { + SimpleTypeSerializer Serializer; + + TypeIndex CharStar(SimpleTypeKind::SignedCharacter, + SimpleTypeMode::NearPointer32); + + BumpPtrAllocator Alloc; + AppendingTypeTableBuilder Ordering1(Alloc); + AppendingTypeTableBuilder Ordering2(Alloc); + + TypeIndex CharP(SimpleTypeKind::SignedCharacter, SimpleTypeMode::NearPointer); + TypeIndex IntP(SimpleTypeKind::Int32, SimpleTypeMode::NearPointer); + TypeIndex DoubleP(SimpleTypeKind::Float64, SimpleTypeMode::NearPointer); + + // We're going to the same type sequence with two different orderings, and + // then confirm all records are hashed the same. + + TypeIndex CharPP[2]; + TypeIndex IntPP[2]; + TypeIndex IntPPP[2]; + TypeIndex DoublePP[2]; + TypeIndex Args[2]; + TypeIndex Proc[2]; + + // Ordering 1 + // ---------------------------------------- + // LF_POINTER 0x1000 {char**} + // Referent = char* + // LF_POINTER 0x1001 {int**} + // Referent = int* + // LF_POINTER 0x1002 {int***} + // Referent = 0x1001 + // LF_ARGLIST 0x1003 {(char**, int***)} + // Arg[0] = 0x1000 + // Arg[1] = 0x1002 + // LF_PROCEDURE 0x1004 {int** func(char**, int***)} + // ArgList = 0x1003 + // ReturnType = 0x1001 + std::vector<GloballyHashedType> Ordering1Hashes; + CharPP[0] = createPointerRecord(Ordering1, CharP); + IntPP[0] = createPointerRecord(Ordering1, IntP); + IntPPP[0] = createPointerRecord(Ordering1, IntPP[0]); + Args[0] = createArgListRecord(Ordering1, CharPP[0], IntPPP[0]); + Proc[0] = createProcedureRecord(Ordering1, 2, IntPP[0], Args[0]); + + ASSERT_EQ(0x1000U, CharPP[0].getIndex()); + ASSERT_EQ(0x1001U, IntPP[0].getIndex()); + ASSERT_EQ(0x1002U, IntPPP[0].getIndex()); + ASSERT_EQ(0x1003U, Args[0].getIndex()); + ASSERT_EQ(0x1004U, Proc[0].getIndex()); + + auto Hashes1 = GloballyHashedType::hashTypes(Ordering1.records()); + + // Ordering 2 + // ---------------------------------------- + // LF_POINTER 0x1000 {int**} + // Referent = int* + // LF_POINTER 0x1001 {int***} + // Referent = 0x1000 + // LF_POINTER 0x1002 {char**} + // Referent = char* + // LF_POINTER 0x1003 {double**} + // Referent = double* + // LF_ARGLIST 0x1004 {(char**, int***)} + // Arg[0] = 0x1002 + // Arg[1] = 0x1001 + // LF_PROCEDURE 0x1005 {int** func(char**, int***)} + // ArgList = 0x1004 + // ReturnType = 0x1000 + IntPP[1] = createPointerRecord(Ordering2, IntP); + IntPPP[1] = createPointerRecord(Ordering2, IntPP[1]); + CharPP[1] = createPointerRecord(Ordering2, CharP); + DoublePP[1] = createPointerRecord(Ordering2, DoubleP); + Args[1] = createArgListRecord(Ordering2, CharPP[1], IntPPP[1]); + Proc[1] = createProcedureRecord(Ordering2, 2, IntPP[1], Args[1]); + auto Hashes2 = GloballyHashedType::hashTypes(Ordering2.records()); + + ASSERT_EQ(0x1000U, IntPP[1].getIndex()); + ASSERT_EQ(0x1001U, IntPPP[1].getIndex()); + ASSERT_EQ(0x1002U, CharPP[1].getIndex()); + ASSERT_EQ(0x1003U, DoublePP[1].getIndex()); + ASSERT_EQ(0x1004U, Args[1].getIndex()); + ASSERT_EQ(0x1005U, Proc[1].getIndex()); + + // Sanity check to make sure all same-ordering hashes are different + // from each other. + verifyHashUniqueness(Hashes1); + verifyHashUniqueness(Hashes2); + + EXPECT_EQ(hash_of(Hashes1, IntPP[0]), hash_of(Hashes2, IntPP[1])); + EXPECT_EQ(hash_of(Hashes1, IntPPP[0]), hash_of(Hashes2, IntPPP[1])); + EXPECT_EQ(hash_of(Hashes1, CharPP[0]), hash_of(Hashes2, CharPP[1])); + EXPECT_EQ(hash_of(Hashes1, Args[0]), hash_of(Hashes2, Args[1])); + EXPECT_EQ(hash_of(Hashes1, Proc[0]), hash_of(Hashes2, Proc[1])); +} diff --git a/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp b/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp index fa9e96123184..c51b9e723f04 100644 --- a/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp +++ b/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp @@ -9,7 +9,8 @@ #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" -#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h" +#include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" #include "llvm/DebugInfo/CodeView/SymbolSerializer.h" #include "llvm/Support/Allocator.h" @@ -25,13 +26,13 @@ public: void SetUp() override { Refs.clear(); - TTB = make_unique<TypeTableBuilder>(Storage); - FLRB = make_unique<FieldListRecordBuilder>(*TTB); + TTB = make_unique<AppendingTypeTableBuilder>(Storage); + CRB = make_unique<ContinuationRecordBuilder>(); Symbols.clear(); } void TearDown() override { - FLRB.reset(); + CRB.reset(); TTB.reset(); } @@ -55,10 +56,11 @@ protected: } template <typename... T> void writeFieldList(T &&... MemberRecords) { - FLRB->begin(); + CRB->begin(ContinuationRecordKind::FieldList); writeFieldListImpl(std::forward<T>(MemberRecords)...); - FLRB->end(true); - ASSERT_EQ(1u, TTB->records().size()); + auto Records = CRB->end(TTB->nextTypeIndex()); + ASSERT_EQ(1u, Records.size()); + TTB->insertRecordBytes(Records.front().RecordData); discoverAllTypeIndices(); } @@ -74,8 +76,7 @@ protected: discoverTypeIndicesInSymbols(); } - - std::unique_ptr<TypeTableBuilder> TTB; + std::unique_ptr<AppendingTypeTableBuilder> TTB; private: uint32_t countRefs(uint32_t RecordIndex) const { @@ -131,7 +132,7 @@ private: void discoverTypeIndicesInSymbols() { Refs.resize(Symbols.size()); for (uint32_t I = 0; I < Symbols.size(); ++I) - discoverTypeIndices(Symbols[I], Refs[I]); + discoverTypeIndicesInSymbol(Symbols[I], Refs[I]); } // Helper function to write out a field list record with the given list @@ -140,7 +141,7 @@ private: template <typename RecType, typename... Rest> void writeFieldListImpl(RecType &&Record, Rest &&... Records) { - FLRB->writeMemberType(Record); + CRB->writeMemberType(Record); writeFieldListImpl(std::forward<Rest>(Records)...); } @@ -149,7 +150,7 @@ private: template <typename RecType, typename... Rest> void writeTypeRecordsImpl(RecType &&Record, Rest &&... Records) { - TTB->writeKnownType(Record); + TTB->writeLeafType(Record); writeTypeRecordsImpl(std::forward<Rest>(Records)...); } @@ -164,7 +165,7 @@ private: } std::vector<SmallVector<TiReference, 4>> Refs; - std::unique_ptr<FieldListRecordBuilder> FLRB; + std::unique_ptr<ContinuationRecordBuilder> CRB; std::vector<CVSymbol> Symbols; BumpPtrAllocator Storage; }; @@ -536,9 +537,9 @@ TEST_F(TypeIndexIteratorTest, ManyMembers) { TEST_F(TypeIndexIteratorTest, ProcSym) { ProcSym GS(SymbolRecordKind::GlobalProcSym); - GS.FunctionType = TypeIndex(0x40); + GS.FunctionType = TypeIndex::Float32(); ProcSym LS(SymbolRecordKind::ProcSym); - LS.FunctionType = TypeIndex(0x41); + LS.FunctionType = TypeIndex::Float64(); writeSymbolRecords(GS, LS); checkTypeReferences(0, GS.FunctionType); checkTypeReferences(1, LS.FunctionType); @@ -546,11 +547,20 @@ TEST_F(TypeIndexIteratorTest, ProcSym) { TEST_F(TypeIndexIteratorTest, DataSym) { DataSym DS(SymbolRecordKind::GlobalData); - DS.Type = TypeIndex(0x40); + DS.Type = TypeIndex::Float32(); writeSymbolRecords(DS); checkTypeReferences(0, DS.Type); } +TEST_F(TypeIndexIteratorTest, RegisterSym) { + RegisterSym Reg(SymbolRecordKind::RegisterSym); + Reg.Index = TypeIndex::UInt32(); + Reg.Register = RegisterId::EAX; + Reg.Name = "Target"; + writeSymbolRecords(Reg); + checkTypeReferences(0, Reg.Index); +} + TEST_F(TypeIndexIteratorTest, CallerSym) { CallerSym Callees(SymbolRecordKind::CalleeSym); Callees.Indices.push_back(TypeIndex(1)); @@ -560,7 +570,13 @@ TEST_F(TypeIndexIteratorTest, CallerSym) { Callers.Indices.push_back(TypeIndex(4)); Callers.Indices.push_back(TypeIndex(5)); Callers.Indices.push_back(TypeIndex(6)); - writeSymbolRecords(Callees, Callers); + CallerSym Inlinees(SymbolRecordKind::InlineesSym); + Inlinees.Indices.push_back(TypeIndex(7)); + Inlinees.Indices.push_back(TypeIndex(8)); + Inlinees.Indices.push_back(TypeIndex(9)); + writeSymbolRecords(Callees, Callers, Inlinees); checkTypeReferences(0, TypeIndex(1), TypeIndex(2), TypeIndex(3)); checkTypeReferences(1, TypeIndex(4), TypeIndex(5), TypeIndex(6)); + checkTypeReferences(2, TypeIndex(7), TypeIndex(8), TypeIndex(9)); } + diff --git a/unittests/DebugInfo/DWARF/CMakeLists.txt b/unittests/DebugInfo/DWARF/CMakeLists.txt index 1966472a9467..f490097a21a7 100644 --- a/unittests/DebugInfo/DWARF/CMakeLists.txt +++ b/unittests/DebugInfo/DWARF/CMakeLists.txt @@ -18,4 +18,4 @@ add_llvm_unittest(DebugInfoDWARFTests ${DebugInfoSources} ) -target_link_libraries(DebugInfoDWARFTests LLVMTestingSupport) +target_link_libraries(DebugInfoDWARFTests PRIVATE LLVMTestingSupport) diff --git a/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp b/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp index 6ffb710d2549..cb7bf82d86f6 100644 --- a/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp +++ b/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp @@ -14,27 +14,24 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/BinaryFormat/Dwarf.h" -#include "llvm/Config/llvm-config.h" #include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/Config/llvm-config.h" #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" +#include "llvm/DebugInfo/DWARF/DWARFVerifier.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCStreamer.h" #include "llvm/Object/ObjectFile.h" #include "llvm/ObjectYAML/DWARFEmitter.h" -#include "llvm/ObjectYAML/DWARFYAML.h" #include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/TargetSelect.h" #include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" -#include <climits> -#include <cstdint> -#include <cstring> #include <string> using namespace llvm; @@ -85,6 +82,8 @@ void TestAllForms() { const uint32_t Data4 = 0x6789abcdU; const uint64_t Data8 = 0x0011223344556677ULL; const uint64_t Data8_2 = 0xAABBCCDDEEFF0011ULL; + const uint8_t Data16[16] = {1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16}; const int64_t SData = INT64_MIN; const int64_t ICSData = INT64_MAX; // DW_FORM_implicit_const SData const uint64_t UData[] = {UINT64_MAX - 1, UINT64_MAX - 2, UINT64_MAX - 3, @@ -123,6 +122,11 @@ void TestAllForms() { const auto Attr_DW_FORM_block4 = static_cast<dwarf::Attribute>(Attr++); CUDie.addAttribute(Attr_DW_FORM_block4, DW_FORM_block4, BlockData, BlockSize); + // We handle data16 as a block form. + const auto Attr_DW_FORM_data16 = static_cast<dwarf::Attribute>(Attr++); + if (Version >= 5) + CUDie.addAttribute(Attr_DW_FORM_data16, DW_FORM_data16, Data16, 16); + //---------------------------------------------------------------------- // Test data forms //---------------------------------------------------------------------- @@ -228,10 +232,10 @@ void TestAllForms() { MemoryBufferRef FileBuffer(FileBytes, "dwarf"); auto Obj = object::ObjectFile::createObjectFile(FileBuffer); EXPECT_TRUE((bool)Obj); - DWARFContextInMemory DwarfContext(*Obj.get()); - uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj); + uint32_t NumCUs = DwarfContext->getNumCompileUnits(); EXPECT_EQ(NumCUs, 1u); - DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + DWARFCompileUnit *U = DwarfContext->getCompileUnitAtIndex(0); auto DieDG = U->getUnitDIE(false); EXPECT_TRUE(DieDG.isValid()); @@ -279,6 +283,17 @@ void TestAllForms() { EXPECT_EQ(ExtractedBlockData.size(), BlockSize); EXPECT_TRUE(memcmp(ExtractedBlockData.data(), BlockData, BlockSize) == 0); + // Data16 is handled like a block. + if (Version >= 5) { + FormValue = DieDG.find(Attr_DW_FORM_data16); + EXPECT_TRUE((bool)FormValue); + BlockDataOpt = FormValue->getAsBlock(); + EXPECT_TRUE(BlockDataOpt.hasValue()); + ExtractedBlockData = BlockDataOpt.getValue(); + EXPECT_EQ(ExtractedBlockData.size(), 16u); + EXPECT_TRUE(memcmp(ExtractedBlockData.data(), Data16, 16) == 0); + } + //---------------------------------------------------------------------- // Test data forms //---------------------------------------------------------------------- @@ -458,12 +473,12 @@ template <uint16_t Version, class AddrType> void TestChildren() { MemoryBufferRef FileBuffer(FileBytes, "dwarf"); auto Obj = object::ObjectFile::createObjectFile(FileBuffer); EXPECT_TRUE((bool)Obj); - DWARFContextInMemory DwarfContext(*Obj.get()); + std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj); // Verify the number of compile units is correct. - uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + uint32_t NumCUs = DwarfContext->getNumCompileUnits(); EXPECT_EQ(NumCUs, 1u); - DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + DWARFCompileUnit *U = DwarfContext->getCompileUnitAtIndex(0); // Get the compile unit DIE is valid. auto DieDG = U->getUnitDIE(false); @@ -629,13 +644,13 @@ template <uint16_t Version, class AddrType> void TestReferences() { MemoryBufferRef FileBuffer(FileBytes, "dwarf"); auto Obj = object::ObjectFile::createObjectFile(FileBuffer); EXPECT_TRUE((bool)Obj); - DWARFContextInMemory DwarfContext(*Obj.get()); + std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj); // Verify the number of compile units is correct. - uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + uint32_t NumCUs = DwarfContext->getNumCompileUnits(); EXPECT_EQ(NumCUs, 2u); - DWARFCompileUnit *U1 = DwarfContext.getCompileUnitAtIndex(0); - DWARFCompileUnit *U2 = DwarfContext.getCompileUnitAtIndex(1); + DWARFCompileUnit *U1 = DwarfContext->getCompileUnitAtIndex(0); + DWARFCompileUnit *U2 = DwarfContext->getCompileUnitAtIndex(1); // Get the compile unit DIE is valid. auto Unit1DieDG = U1->getUnitDIE(false); @@ -837,12 +852,12 @@ template <uint16_t Version, class AddrType> void TestAddresses() { MemoryBufferRef FileBuffer(FileBytes, "dwarf"); auto Obj = object::ObjectFile::createObjectFile(FileBuffer); EXPECT_TRUE((bool)Obj); - DWARFContextInMemory DwarfContext(*Obj.get()); + std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj); // Verify the number of compile units is correct. - uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + uint32_t NumCUs = DwarfContext->getNumCompileUnits(); EXPECT_EQ(NumCUs, 1u); - DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + DWARFCompileUnit *U = DwarfContext->getCompileUnitAtIndex(0); // Get the compile unit DIE is valid. auto DieDG = U->getUnitDIE(false); @@ -1012,12 +1027,12 @@ TEST(DWARFDebugInfo, TestRelations) { MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); auto Obj = object::ObjectFile::createObjectFile(FileBuffer); EXPECT_TRUE((bool)Obj); - DWARFContextInMemory DwarfContext(*Obj.get()); + std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj); // Verify the number of compile units is correct. - uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + uint32_t NumCUs = DwarfContext->getNumCompileUnits(); EXPECT_EQ(NumCUs, 1u); - DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + DWARFCompileUnit *U = DwarfContext->getCompileUnitAtIndex(0); // Get the compile unit DIE is valid. auto CUDie = U->getUnitDIE(false); @@ -1127,12 +1142,12 @@ TEST(DWARFDebugInfo, TestChildIterators) { MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); auto Obj = object::ObjectFile::createObjectFile(FileBuffer); EXPECT_TRUE((bool)Obj); - DWARFContextInMemory DwarfContext(*Obj.get()); + std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj); // Verify the number of compile units is correct. - uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + uint32_t NumCUs = DwarfContext->getNumCompileUnits(); EXPECT_EQ(NumCUs, 1u); - DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + DWARFCompileUnit *U = DwarfContext->getCompileUnitAtIndex(0); // Get the compile unit DIE is valid. auto CUDie = U->getUnitDIE(false); @@ -1188,12 +1203,13 @@ TEST(DWARFDebugInfo, TestEmptyChildren) { auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); - DWARFContextInMemory DwarfContext(*ErrOrSections, 8); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); // Verify the number of compile units is correct. - uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + uint32_t NumCUs = DwarfContext->getNumCompileUnits(); EXPECT_EQ(NumCUs, 1u); - DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + DWARFCompileUnit *U = DwarfContext->getCompileUnitAtIndex(0); // Get the compile unit DIE is valid. auto CUDie = U->getUnitDIE(false); @@ -1235,12 +1251,12 @@ TEST(DWARFDebugInfo, TestAttributeIterators) { MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); auto Obj = object::ObjectFile::createObjectFile(FileBuffer); EXPECT_TRUE((bool)Obj); - DWARFContextInMemory DwarfContext(*Obj.get()); + std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj); // Verify the number of compile units is correct. - uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + uint32_t NumCUs = DwarfContext->getNumCompileUnits(); EXPECT_EQ(NumCUs, 1u); - DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + DWARFCompileUnit *U = DwarfContext->getCompileUnitAtIndex(0); // Get the compile unit DIE is valid. auto CUDie = U->getUnitDIE(false); @@ -1285,12 +1301,16 @@ TEST(DWARFDebugInfo, TestFindRecurse) { auto CUDie = CU.getUnitDIE(); auto FuncSpecDie = CUDie.addChild(DW_TAG_subprogram); auto FuncAbsDie = CUDie.addChild(DW_TAG_subprogram); + // Put the linkage name in a second abstract origin DIE to ensure we + // recurse through more than just one DIE when looking for attributes. + auto FuncAbsDie2 = CUDie.addChild(DW_TAG_subprogram); auto FuncDie = CUDie.addChild(DW_TAG_subprogram); auto VarAbsDie = CUDie.addChild(DW_TAG_variable); auto VarDie = CUDie.addChild(DW_TAG_variable); FuncSpecDie.addAttribute(DW_AT_name, DW_FORM_strp, SpecDieName); - FuncAbsDie.addAttribute(DW_AT_linkage_name, DW_FORM_strp, SpecLinkageName); + FuncAbsDie2.addAttribute(DW_AT_linkage_name, DW_FORM_strp, SpecLinkageName); FuncAbsDie.addAttribute(DW_AT_specification, DW_FORM_ref4, FuncSpecDie); + FuncAbsDie.addAttribute(DW_AT_abstract_origin, DW_FORM_ref4, FuncAbsDie2); FuncDie.addAttribute(DW_AT_abstract_origin, DW_FORM_ref4, FuncAbsDie); VarAbsDie.addAttribute(DW_AT_name, DW_FORM_strp, AbsDieName); VarDie.addAttribute(DW_AT_abstract_origin, DW_FORM_ref4, VarAbsDie); @@ -1299,12 +1319,12 @@ TEST(DWARFDebugInfo, TestFindRecurse) { MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); auto Obj = object::ObjectFile::createObjectFile(FileBuffer); EXPECT_TRUE((bool)Obj); - DWARFContextInMemory DwarfContext(*Obj.get()); + std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj); // Verify the number of compile units is correct. - uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + uint32_t NumCUs = DwarfContext->getNumCompileUnits(); EXPECT_EQ(NumCUs, 1u); - DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + DWARFCompileUnit *U = DwarfContext->getCompileUnitAtIndex(0); // Get the compile unit DIE is valid. auto CUDie = U->getUnitDIE(false); @@ -1312,7 +1332,8 @@ TEST(DWARFDebugInfo, TestFindRecurse) { auto FuncSpecDie = CUDie.getFirstChild(); auto FuncAbsDie = FuncSpecDie.getSibling(); - auto FuncDie = FuncAbsDie.getSibling(); + auto FuncAbsDie2 = FuncAbsDie.getSibling(); + auto FuncDie = FuncAbsDie2.getSibling(); auto VarAbsDie = FuncDie.getSibling(); auto VarDie = VarAbsDie.getSibling(); @@ -1505,12 +1526,12 @@ TEST(DWARFDebugInfo, TestFindAttrs) { MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); auto Obj = object::ObjectFile::createObjectFile(FileBuffer); EXPECT_TRUE((bool)Obj); - DWARFContextInMemory DwarfContext(*Obj.get()); + std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj); // Verify the number of compile units is correct. - uint32_t NumCUs = DwarfContext.getNumCompileUnits(); + uint32_t NumCUs = DwarfContext->getNumCompileUnits(); EXPECT_EQ(NumCUs, 1u); - DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + DWARFCompileUnit *U = DwarfContext->getCompileUnitAtIndex(0); // Get the compile unit DIE is valid. auto CUDie = U->getUnitDIE(false); @@ -1568,8 +1589,8 @@ TEST(DWARFDebugInfo, TestImplicitConstAbbrevs) { MemoryBufferRef FileBuffer(DG->generate(), "dwarf"); auto Obj = object::ObjectFile::createObjectFile(FileBuffer); EXPECT_TRUE((bool)Obj); - DWARFContextInMemory DwarfContext(*Obj.get()); - DWARFCompileUnit *U = DwarfContext.getCompileUnitAtIndex(0); + std::unique_ptr<DWARFContext> DwarfContext = DWARFContext::create(**Obj); + DWARFCompileUnit *U = DwarfContext->getCompileUnitAtIndex(0); EXPECT_TRUE((bool)U); const auto *Abbrevs = U->getAbbreviations(); @@ -1657,16 +1678,29 @@ TEST(DWARFDebugInfo, TestImplicitConstAbbrevs) { EXPECT_EQ(DIEs.find(Val2)->second, AbbrevPtrVal2); } +void VerifyWarning(DWARFContext &DwarfContext, StringRef Error) { + SmallString<1024> Str; + raw_svector_ostream Strm(Str); + EXPECT_TRUE(DwarfContext.verify(Strm)); + EXPECT_TRUE(Str.str().contains(Error)); +} + void VerifyError(DWARFContext &DwarfContext, StringRef Error) { SmallString<1024> Str; raw_svector_ostream Strm(Str); - EXPECT_FALSE(DwarfContext.verify(Strm, DIDT_All)); + EXPECT_FALSE(DwarfContext.verify(Strm)); EXPECT_TRUE(Str.str().contains(Error)); } +void VerifySuccess(DWARFContext &DwarfContext) { + SmallString<1024> Str; + raw_svector_ostream Strm(Str); + EXPECT_TRUE(DwarfContext.verify(Strm)); +} + TEST(DWARFDebugInfo, TestDwarfVerifyInvalidCURef) { // Create a single compile unit with a single function that has a DW_AT_type - // that is CU relative. The CU offset is not valid becuase it is larger than + // that is CU relative. The CU offset is not valid because it is larger than // the compile unit itself. const char *yamldata = R"( @@ -1708,10 +1742,11 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidCURef) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); - DWARFContextInMemory DwarfContext(*ErrOrSections, 8); - VerifyError(DwarfContext, "error: DW_FORM_ref4 CU offset 0x00001234 is " - "invalid (must be less than CU size of " - "0x0000001a):"); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyError(*DwarfContext, "error: DW_FORM_ref4 CU offset 0x00001234 is " + "invalid (must be less than CU size of " + "0x0000001a):"); } TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddr) { @@ -1756,8 +1791,9 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddr) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); - DWARFContextInMemory DwarfContext(*ErrOrSections, 8); - VerifyError(DwarfContext, + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyError(*DwarfContext, "error: DW_FORM_ref_addr offset beyond .debug_info bounds:"); } @@ -1792,8 +1828,9 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRanges) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); - DWARFContextInMemory DwarfContext(*ErrOrSections, 8); - VerifyError(DwarfContext, + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyError(*DwarfContext, "error: DW_AT_ranges offset is beyond .debug_ranges bounds:"); } @@ -1828,9 +1865,10 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStmtList) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); - DWARFContextInMemory DwarfContext(*ErrOrSections, 8); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); VerifyError( - DwarfContext, + *DwarfContext, "error: DW_AT_stmt_list offset is beyond .debug_line bounds: 0x00001000"); } @@ -1860,8 +1898,9 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidStrp) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); - DWARFContextInMemory DwarfContext(*ErrOrSections, 8); - VerifyError(DwarfContext, + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyError(*DwarfContext, "error: DW_FORM_strp offset beyond .debug_str bounds:"); } @@ -1907,9 +1946,10 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidRefAddrBetween) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata)); ASSERT_TRUE((bool)ErrOrSections); - DWARFContextInMemory DwarfContext(*ErrOrSections, 8); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); VerifyError( - DwarfContext, + *DwarfContext, "error: invalid DIE reference 0x00000011. Offset is in between DIEs:"); } @@ -1977,9 +2017,10 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidLineSequence) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); ASSERT_TRUE((bool)ErrOrSections); - DWARFContextInMemory DwarfContext(*ErrOrSections, 8); - VerifyError(DwarfContext, "error: .debug_line[0x00000000] row[1] decreases " - "in address from previous row:"); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyError(*DwarfContext, "error: .debug_line[0x00000000] row[1] decreases " + "in address from previous row:"); } TEST(DWARFDebugInfo, TestDwarfVerifyInvalidLineFileIndex) { @@ -2048,9 +2089,160 @@ TEST(DWARFDebugInfo, TestDwarfVerifyInvalidLineFileIndex) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); ASSERT_TRUE((bool)ErrOrSections); - DWARFContextInMemory DwarfContext(*ErrOrSections, 8); - VerifyError(DwarfContext, "error: .debug_line[0x00000000][1] has invalid " - "file index 5 (valid values are [1,1]):"); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyError(*DwarfContext, "error: .debug_line[0x00000000][1] has invalid " + "file index 5 (valid values are [1,1]):"); +} + +TEST(DWARFDebugInfo, TestDwarfVerifyInvalidLineTablePorlogueDirIndex) { + // Create a single compile unit whose line table has a prologue with an + // invalid dir index. + StringRef yamldata = R"( + debug_str: + - '' + - /tmp/main.c + debug_abbrev: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + debug_info: + - Length: + TotalLength: 16 + Version: 4 + AbbrOffset: 0 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x0000000000000001 + - Value: 0x0000000000000000 + debug_line: + - Length: + TotalLength: 61 + Version: 2 + PrologueLength: 34 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + IncludeDirs: + - /tmp + Files: + - Name: main.c + DirIdx: 2 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 9 + SubOpcode: DW_LNE_set_address + Data: 4096 + - Opcode: DW_LNS_advance_line + SData: 9 + Data: 4096 + - Opcode: DW_LNS_copy + Data: 4096 + - Opcode: DW_LNS_advance_pc + Data: 16 + - Opcode: DW_LNS_set_file + Data: 1 + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + Data: 1 + )"; + auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); + ASSERT_TRUE((bool)ErrOrSections); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyError(*DwarfContext, + "error: .debug_line[0x00000000].prologue." + "file_names[1].dir_idx contains an invalid index: 2"); +} + +TEST(DWARFDebugInfo, TestDwarfVerifyDuplicateFileWarning) { + // Create a single compile unit whose line table has a prologue with an + // invalid dir index. + StringRef yamldata = R"( + debug_str: + - '' + - /tmp/main.c + debug_abbrev: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + debug_info: + - Length: + TotalLength: 16 + Version: 4 + AbbrOffset: 0 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x0000000000000001 + - Value: 0x0000000000000000 + debug_line: + - Length: + TotalLength: 71 + Version: 2 + PrologueLength: 44 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + IncludeDirs: + - /tmp + Files: + - Name: main.c + DirIdx: 1 + ModTime: 0 + Length: 0 + - Name: main.c + DirIdx: 1 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 9 + SubOpcode: DW_LNE_set_address + Data: 4096 + - Opcode: DW_LNS_advance_line + SData: 9 + Data: 4096 + - Opcode: DW_LNS_copy + Data: 4096 + - Opcode: DW_LNS_advance_pc + Data: 16 + - Opcode: DW_LNS_set_file + Data: 1 + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + Data: 2 + )"; + auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); + ASSERT_TRUE((bool)ErrOrSections); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyWarning(*DwarfContext, + "warning: .debug_line[0x00000000].prologue.file_names[2] is " + "a duplicate of file_names[1]"); } TEST(DWARFDebugInfo, TestDwarfVerifyCUDontShareLineTable) { @@ -2128,10 +2320,12 @@ TEST(DWARFDebugInfo, TestDwarfVerifyCUDontShareLineTable) { )"; auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); ASSERT_TRUE((bool)ErrOrSections); - DWARFContextInMemory DwarfContext(*ErrOrSections, 8); - VerifyError(DwarfContext, "error: two compile unit DIEs, 0x0000000b and " - "0x0000001f, have the same DW_AT_stmt_list section " - "offset:"); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyError(*DwarfContext, + "error: two compile unit DIEs, 0x0000000b and " + "0x0000001f, have the same DW_AT_stmt_list section " + "offset:"); } TEST(DWARFDebugInfo, TestErrorReportingPolicy) { @@ -2158,24 +2352,664 @@ TEST(DWARFDebugInfo, TestErrorReportingPolicy) { EXPECT_TRUE((bool)Obj); // Case 1: error handler handles all errors. That allows - // DWARFContextInMemory - // to parse whole file and find both two errors we know about. + // DWARFContext to parse whole file and find both two errors we know about. int Errors = 0; - DWARFContextInMemory Ctx1(*Obj.get(), nullptr, [&](Error E) { - ++Errors; - consumeError(std::move(E)); - return ErrorPolicy::Continue; - }); + std::unique_ptr<DWARFContext> Ctx1 = + DWARFContext::create(**Obj, nullptr, [&](Error E) { + ++Errors; + consumeError(std::move(E)); + return ErrorPolicy::Continue; + }); EXPECT_TRUE(Errors == 2); // Case 2: error handler stops parsing of object after first error. Errors = 0; - DWARFContextInMemory Ctx2(*Obj.get(), nullptr, [&](Error E) { - ++Errors; - consumeError(std::move(E)); - return ErrorPolicy::Halt; - }); + std::unique_ptr<DWARFContext> Ctx2 = + DWARFContext::create(**Obj, nullptr, [&](Error E) { + ++Errors; + consumeError(std::move(E)); + return ErrorPolicy::Halt; + }); EXPECT_TRUE(Errors == 1); } +TEST(DWARFDebugInfo, TestDwarfVerifyCURangesIncomplete) { + // Create a single compile unit with a single function. The compile + // unit has a DW_AT_ranges attribute that doesn't fully contain the + // address range of the function. The verification should fail due to + // the CU ranges not containing all of the address ranges of all of the + // functions. + StringRef yamldata = R"( + debug_str: + - '' + - /tmp/main.c + debug_abbrev: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0x00000002 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + debug_info: + - Length: + TotalLength: 46 + Version: 4 + AbbrOffset: 0 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x0000000000001000 + - Value: 0x0000000000001500 + - Value: 0x0000000000000001 + - AbbrCode: 0x00000002 + Values: + - Value: 0x0000000000001000 + - Value: 0x0000000000002000 + - AbbrCode: 0x00000000 + Values: + )"; + auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); + ASSERT_TRUE((bool)ErrOrSections); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyError(*DwarfContext, "error: DIE address ranges are not " + "contained in its parent's ranges:"); +} + +TEST(DWARFDebugInfo, TestDwarfVerifyLexicalBlockRanges) { + // Create a single compile unit with a single function that has a lexical + // block whose address range is not contained in the function address range. + StringRef yamldata = R"( + debug_str: + - '' + - /tmp/main.c + - main + debug_abbrev: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0x00000002 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + - Code: 0x00000003 + Tag: DW_TAG_lexical_block + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + debug_info: + - Length: + TotalLength: 52 + Version: 4 + AbbrOffset: 0 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x0000000000000001 + - AbbrCode: 0x00000002 + Values: + - Value: 0x000000000000000D + - Value: 0x0000000000001000 + - Value: 0x0000000000002000 + - AbbrCode: 0x00000003 + Values: + - Value: 0x0000000000001000 + - Value: 0x0000000000002001 + - AbbrCode: 0x00000000 + Values: + - AbbrCode: 0x00000000 + Values: + )"; + auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); + ASSERT_TRUE((bool)ErrOrSections); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyError(*DwarfContext, "error: DIE address ranges are not " + "contained in its parent's ranges:"); +} + +TEST(DWARFDebugInfo, TestDwarfVerifyOverlappingFunctionRanges) { + // Create a single compile unit with a two functions that have overlapping + // address ranges. + StringRef yamldata = R"( + debug_str: + - '' + - /tmp/main.c + - main + - foo + debug_abbrev: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0x00000002 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + debug_info: + - Length: + TotalLength: 55 + Version: 4 + AbbrOffset: 0 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x0000000000000001 + - AbbrCode: 0x00000002 + Values: + - Value: 0x000000000000000D + - Value: 0x0000000000001000 + - Value: 0x0000000000002000 + - AbbrCode: 0x00000002 + Values: + - Value: 0x0000000000000012 + - Value: 0x0000000000001FFF + - Value: 0x0000000000002000 + - AbbrCode: 0x00000000 + Values: + )"; + auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); + ASSERT_TRUE((bool)ErrOrSections); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyError(*DwarfContext, "error: DIEs have overlapping address ranges:"); +} + +TEST(DWARFDebugInfo, TestDwarfVerifyOverlappingLexicalBlockRanges) { + // Create a single compile unit with a one function that has two lexical + // blocks with overlapping address ranges. + StringRef yamldata = R"( + debug_str: + - '' + - /tmp/main.c + - main + debug_abbrev: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0x00000002 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + - Code: 0x00000003 + Tag: DW_TAG_lexical_block + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + debug_info: + - Length: + TotalLength: 85 + Version: 4 + AbbrOffset: 0 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x0000000000001000 + - Value: 0x0000000000002000 + - Value: 0x0000000000000001 + - AbbrCode: 0x00000002 + Values: + - Value: 0x000000000000000D + - Value: 0x0000000000001000 + - Value: 0x0000000000002000 + - AbbrCode: 0x00000003 + Values: + - Value: 0x0000000000001100 + - Value: 0x0000000000001300 + - AbbrCode: 0x00000003 + Values: + - Value: 0x00000000000012FF + - Value: 0x0000000000001300 + - AbbrCode: 0x00000000 + Values: + - AbbrCode: 0x00000000 + Values: + )"; + auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); + ASSERT_TRUE((bool)ErrOrSections); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyError(*DwarfContext, "error: DIEs have overlapping address ranges:"); +} + +TEST(DWARFDebugInfo, TestDwarfVerifyInvalidDIERange) { + // Create a single compile unit with a single function that has an invalid + // address range where the high PC is smaller than the low PC. + StringRef yamldata = R"( + debug_str: + - '' + - /tmp/main.c + - main + debug_abbrev: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0x00000002 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + debug_info: + - Length: + TotalLength: 34 + Version: 4 + AbbrOffset: 0 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x0000000000000001 + - AbbrCode: 0x00000002 + Values: + - Value: 0x000000000000000D + - Value: 0x0000000000001000 + - Value: 0x0000000000000900 + - AbbrCode: 0x00000000 + Values: + )"; + auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); + ASSERT_TRUE((bool)ErrOrSections); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifyError(*DwarfContext, "error: Invalid address range"); +} + +TEST(DWARFDebugInfo, TestDwarfVerifyElidedDoesntFail) { + // Create a single compile unit with two functions: one that has a valid range + // and one whose low and high PC are the same. When the low and high PC are + // the same, this indicates the function was dead code stripped. We want to + // ensure that verification succeeds. + StringRef yamldata = R"( + debug_str: + - '' + - /tmp/main.c + - main + - elided + debug_abbrev: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0x00000002 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + debug_info: + - Length: + TotalLength: 71 + Version: 4 + AbbrOffset: 0 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x0000000000001000 + - Value: 0x0000000000002000 + - Value: 0x0000000000000001 + - AbbrCode: 0x00000002 + Values: + - Value: 0x000000000000000D + - Value: 0x0000000000001000 + - Value: 0x0000000000002000 + - AbbrCode: 0x00000002 + Values: + - Value: 0x0000000000000012 + - Value: 0x0000000000002000 + - Value: 0x0000000000002000 + - AbbrCode: 0x00000000 + Values: + )"; + auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); + ASSERT_TRUE((bool)ErrOrSections); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifySuccess(*DwarfContext); +} + +TEST(DWARFDebugInfo, TestDwarfVerifyNestedFunctions) { + // Create a single compile unit with a nested function which is not contained + // in its parent. Although LLVM doesn't generate this, it is valid accoridng + // to the DWARF standard. + StringRef yamldata = R"( + debug_str: + - '' + - /tmp/main.c + - main + - nested + debug_abbrev: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0x00000002 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + debug_info: + - Length: + TotalLength: 73 + Version: 4 + AbbrOffset: 0 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x0000000000001000 + - Value: 0x0000000000002000 + - Value: 0x0000000000000001 + - AbbrCode: 0x00000002 + Values: + - Value: 0x000000000000000D + - Value: 0x0000000000001000 + - Value: 0x0000000000001500 + - AbbrCode: 0x00000002 + Values: + - Value: 0x0000000000000012 + - Value: 0x0000000000001500 + - Value: 0x0000000000002000 + - AbbrCode: 0x00000000 + Values: + - AbbrCode: 0x00000000 + Values: + - AbbrCode: 0x00000000 + Values: + )"; + auto ErrOrSections = DWARFYAML::EmitDebugSections(yamldata); + ASSERT_TRUE((bool)ErrOrSections); + std::unique_ptr<DWARFContext> DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + VerifySuccess(*DwarfContext); +} + +TEST(DWARFDebugInfo, TestDwarfRangesContains) { + DWARFAddressRange R(0x10, 0x20); + + //---------------------------------------------------------------------- + // Test ranges that start before R... + //---------------------------------------------------------------------- + // Other range ends before start of R + ASSERT_FALSE(R.contains({0x0f, 0x10})); + // Other range end address is start of a R + ASSERT_FALSE(R.contains({0x0f, 0x11})); + // Other range end address is at and of R + ASSERT_FALSE(R.contains({0x0f, 0x20})); + // Other range end address is past end of R + ASSERT_FALSE(R.contains({0x0f, 0x40})); + + //---------------------------------------------------------------------- + // Test ranges that start at R's start address + //---------------------------------------------------------------------- + // Ensure empty ranges matches + ASSERT_TRUE(R.contains({0x10, 0x10})); + // 1 byte of Range + ASSERT_TRUE(R.contains({0x10, 0x11})); + // same as Range + ASSERT_TRUE(R.contains({0x10, 0x20})); + // 1 byte past Range + ASSERT_FALSE(R.contains({0x10, 0x21})); + + //---------------------------------------------------------------------- + // Test ranges that start inside Range + //---------------------------------------------------------------------- + // empty in range + ASSERT_TRUE(R.contains({0x11, 0x11})); + // all in Range + ASSERT_TRUE(R.contains({0x11, 0x1f})); + // ends at end of Range + ASSERT_TRUE(R.contains({0x11, 0x20})); + // ends past Range + ASSERT_FALSE(R.contains({0x11, 0x21})); + + //---------------------------------------------------------------------- + // Test ranges that start at last bytes of Range + //---------------------------------------------------------------------- + // ends at end of Range + ASSERT_TRUE(R.contains({0x1f, 0x20})); + // ends past Range + ASSERT_FALSE(R.contains({0x1f, 0x21})); + + //---------------------------------------------------------------------- + // Test ranges that start after Range + //---------------------------------------------------------------------- + // empty considered in Range + ASSERT_TRUE(R.contains({0x20, 0x20})); + // valid past Range + ASSERT_FALSE(R.contains({0x20, 0x21})); +} + +TEST(DWARFDebugInfo, TestDWARFDieRangeInfoContains) { + DWARFVerifier::DieRangeInfo Ranges({{0x10, 0x20}, {0x30, 0x40}}); + + ASSERT_FALSE(Ranges.contains({{{0x0f, 0x10}}})); + ASSERT_FALSE(Ranges.contains({{{0x20, 0x30}}})); + ASSERT_FALSE(Ranges.contains({{{0x40, 0x41}}})); + ASSERT_TRUE(Ranges.contains({{{0x10, 0x20}}})); + ASSERT_TRUE(Ranges.contains({{{0x11, 0x12}}})); + ASSERT_TRUE(Ranges.contains({{{0x1f, 0x20}}})); + ASSERT_TRUE(Ranges.contains({{{0x30, 0x40}}})); + ASSERT_TRUE(Ranges.contains({{{0x31, 0x32}}})); + ASSERT_TRUE(Ranges.contains({{{0x3f, 0x40}}})); + ASSERT_TRUE(Ranges.contains({{{0x10, 0x20}, {0x30, 0x40}}})); + ASSERT_TRUE(Ranges.contains({{{0x11, 0x12}, {0x31, 0x32}}})); + ASSERT_TRUE(Ranges.contains( + {{{0x11, 0x12}, {0x12, 0x13}, {0x31, 0x32}, {0x32, 0x33}}})); + ASSERT_FALSE(Ranges.contains({{{0x11, 0x12}, + {0x12, 0x13}, + {0x20, 0x21}, + {0x31, 0x32}, + {0x32, 0x33}}})); + ASSERT_FALSE(Ranges.contains( + {{{0x11, 0x12}, {0x12, 0x13}, {0x31, 0x32}, {0x32, 0x41}}})); +} + +namespace { + +void AssertRangesIntersect(const DWARFAddressRange &LHS, + const DWARFAddressRange &RHS) { + ASSERT_TRUE(LHS.intersects(RHS)); + ASSERT_TRUE(RHS.intersects(LHS)); +} +void AssertRangesDontIntersect(const DWARFAddressRange &LHS, + const DWARFAddressRange &RHS) { + ASSERT_FALSE(LHS.intersects(RHS)); + ASSERT_FALSE(RHS.intersects(LHS)); +} + +void AssertRangesIntersect(const DWARFVerifier::DieRangeInfo &LHS, + const DWARFAddressRangesVector &Ranges) { + DWARFVerifier::DieRangeInfo RHS(Ranges); + ASSERT_TRUE(LHS.intersects(RHS)); + ASSERT_TRUE(RHS.intersects(LHS)); +} + +void AssertRangesDontIntersect(const DWARFVerifier::DieRangeInfo &LHS, + const DWARFAddressRangesVector &Ranges) { + DWARFVerifier::DieRangeInfo RHS(Ranges); + ASSERT_FALSE(LHS.intersects(RHS)); + ASSERT_FALSE(RHS.intersects(LHS)); +} + +} // namespace +TEST(DWARFDebugInfo, TestDwarfRangesIntersect) { + DWARFAddressRange R(0x10, 0x20); + + //---------------------------------------------------------------------- + // Test ranges that start before R... + //---------------------------------------------------------------------- + // Other range ends before start of R + AssertRangesDontIntersect(R, {0x00, 0x10}); + // Other range end address is start of a R + AssertRangesIntersect(R, {0x00, 0x11}); + // Other range end address is in R + AssertRangesIntersect(R, {0x00, 0x15}); + // Other range end address is at and of R + AssertRangesIntersect(R, {0x00, 0x20}); + // Other range end address is past end of R + AssertRangesIntersect(R, {0x00, 0x40}); + + //---------------------------------------------------------------------- + // Test ranges that start at R's start address + //---------------------------------------------------------------------- + // Ensure empty ranges doesn't match + AssertRangesDontIntersect(R, {0x10, 0x10}); + // 1 byte of Range + AssertRangesIntersect(R, {0x10, 0x11}); + // same as Range + AssertRangesIntersect(R, {0x10, 0x20}); + // 1 byte past Range + AssertRangesIntersect(R, {0x10, 0x21}); + + //---------------------------------------------------------------------- + // Test ranges that start inside Range + //---------------------------------------------------------------------- + // empty in range + AssertRangesDontIntersect(R, {0x11, 0x11}); + // all in Range + AssertRangesIntersect(R, {0x11, 0x1f}); + // ends at end of Range + AssertRangesIntersect(R, {0x11, 0x20}); + // ends past Range + AssertRangesIntersect(R, {0x11, 0x21}); + + //---------------------------------------------------------------------- + // Test ranges that start at last bytes of Range + //---------------------------------------------------------------------- + // ends at end of Range + AssertRangesIntersect(R, {0x1f, 0x20}); + // ends past Range + AssertRangesIntersect(R, {0x1f, 0x21}); + + //---------------------------------------------------------------------- + // Test ranges that start after Range + //---------------------------------------------------------------------- + // empty just past in Range + AssertRangesDontIntersect(R, {0x20, 0x20}); + // valid past Range + AssertRangesDontIntersect(R, {0x20, 0x21}); +} + +TEST(DWARFDebugInfo, TestDWARFDieRangeInfoIntersects) { + + DWARFVerifier::DieRangeInfo Ranges({{0x10, 0x20}, {0x30, 0x40}}); + + // Test empty range + AssertRangesDontIntersect(Ranges, {}); + // Test range that appears before all ranges in Ranges + AssertRangesDontIntersect(Ranges, {{0x00, 0x10}}); + // Test range that appears between ranges in Ranges + AssertRangesDontIntersect(Ranges, {{0x20, 0x30}}); + // Test range that appears after ranges in Ranges + AssertRangesDontIntersect(Ranges, {{0x40, 0x50}}); + + // Test range that start before first range + AssertRangesIntersect(Ranges, {{0x00, 0x11}}); + // Test range that start at first range + AssertRangesIntersect(Ranges, {{0x10, 0x11}}); + // Test range that start in first range + AssertRangesIntersect(Ranges, {{0x11, 0x12}}); + // Test range that start at end of first range + AssertRangesIntersect(Ranges, {{0x1f, 0x20}}); + // Test range that starts at end of first range + AssertRangesDontIntersect(Ranges, {{0x20, 0x21}}); + // Test range that starts at end of first range + AssertRangesIntersect(Ranges, {{0x20, 0x31}}); + + // Test range that start before second range and ends before second + AssertRangesDontIntersect(Ranges, {{0x2f, 0x30}}); + // Test range that start before second range and ends in second + AssertRangesIntersect(Ranges, {{0x2f, 0x31}}); + // Test range that start at second range + AssertRangesIntersect(Ranges, {{0x30, 0x31}}); + // Test range that start in second range + AssertRangesIntersect(Ranges, {{0x31, 0x32}}); + // Test range that start at end of second range + AssertRangesIntersect(Ranges, {{0x3f, 0x40}}); + // Test range that starts at end of second range + AssertRangesDontIntersect(Ranges, {{0x40, 0x41}}); +} + } // end anonymous namespace diff --git a/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp b/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp index f283ac0961cc..c552623a7866 100644 --- a/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp +++ b/unittests/DebugInfo/DWARF/DWARFFormValueTest.cpp @@ -99,7 +99,7 @@ DWARFFormValue createDataXFormValue(dwarf::Form Form, RawTypeT Value) { DWARFFormValue Result(Form); DWARFDataExtractor Data(StringRef(Raw, sizeof(RawTypeT)), sys::IsLittleEndianHost, sizeof(void *)); - Result.extractValue(Data, &Offset, nullptr); + Result.extractValue(Data, &Offset, {0, 0, dwarf::DwarfFormat::DWARF32}); return Result; } @@ -110,7 +110,7 @@ DWARFFormValue createULEBFormValue(uint64_t Value) { uint32_t Offset = 0; DWARFFormValue Result(DW_FORM_udata); DWARFDataExtractor Data(OS.str(), sys::IsLittleEndianHost, sizeof(void *)); - Result.extractValue(Data, &Offset, nullptr); + Result.extractValue(Data, &Offset, {0, 0, dwarf::DwarfFormat::DWARF32}); return Result; } @@ -121,7 +121,7 @@ DWARFFormValue createSLEBFormValue(int64_t Value) { uint32_t Offset = 0; DWARFFormValue Result(DW_FORM_sdata); DWARFDataExtractor Data(OS.str(), sys::IsLittleEndianHost, sizeof(void *)); - Result.extractValue(Data, &Offset, nullptr); + Result.extractValue(Data, &Offset, {0, 0, dwarf::DwarfFormat::DWARF32}); return Result; } @@ -160,6 +160,18 @@ TEST(DWARFFormValue, SignedConstantForms) { EXPECT_EQ(LEBMax.getAsSignedConstant().getValue(), LLONG_MAX); EXPECT_EQ(LEB1.getAsSignedConstant().getValue(), -42); EXPECT_EQ(LEB2.getAsSignedConstant().getValue(), 42); + + // Data16 is a little tricky. + char Cksum[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + DWARFFormValue Data16(DW_FORM_data16); + DWARFDataExtractor DE16(StringRef(Cksum, 16), sys::IsLittleEndianHost, + sizeof(void *)); + uint32_t Offset = 0; + Data16.extractValue(DE16, &Offset, {0, 0, dwarf::DwarfFormat::DWARF32}); + SmallString<32> Str; + raw_svector_ostream Res(Str); + Data16.dump(Res, DIDumpOptions()); + EXPECT_EQ(memcmp(Str.data(), "000102030405060708090a0b0c0d0e0f", 32), 0); } } // end anonymous namespace diff --git a/unittests/DebugInfo/DWARF/DwarfGenerator.cpp b/unittests/DebugInfo/DWARF/DwarfGenerator.cpp index c32cfa1de9ae..3aa52a0d5b8f 100644 --- a/unittests/DebugInfo/DWARF/DwarfGenerator.cpp +++ b/unittests/DebugInfo/DWARF/DwarfGenerator.cpp @@ -13,10 +13,8 @@ #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/DIE.h" -#include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" -#include "llvm/IR/LegacyPassManagers.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCCodeEmitter.h" @@ -27,9 +25,8 @@ #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCTargetOptionsCommandFlags.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.def" #include "llvm/PassAnalysisSupport.h" -#include "llvm/Support/LEB128.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" @@ -153,10 +150,10 @@ llvm::Error dwarfgen::Generator::init(Triple TheTriple, uint16_t V) { MOFI.reset(new MCObjectFileInfo); MC.reset(new MCContext(MAI.get(), MRI.get(), MOFI.get())); - MOFI->InitMCObjectFileInfo(TheTriple, /*PIC*/ false, CodeModel::Default, *MC); + MOFI->InitMCObjectFileInfo(TheTriple, /*PIC*/ false, *MC); - MCTargetOptions Options; - MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, "", Options); + MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); + MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, "", MCOptions); if (!MAB) return make_error<StringError>("no asm backend for target " + TripleName, inconvertibleErrorCode()); @@ -179,9 +176,9 @@ llvm::Error dwarfgen::Generator::init(Triple TheTriple, uint16_t V) { Stream = make_unique<raw_svector_ostream>(FileBytes); - MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); MS = TheTarget->createMCObjectStreamer( - TheTriple, *MC, *MAB, *Stream, MCE, *MSTI, MCOptions.MCRelaxAll, + TheTriple, *MC, std::unique_ptr<MCAsmBackend>(MAB), *Stream, + std::unique_ptr<MCCodeEmitter>(MCE), *MSTI, MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, /*DWARFMustBeAtTheEnd*/ false); if (!MS) diff --git a/unittests/DebugInfo/MSF/CMakeLists.txt b/unittests/DebugInfo/MSF/CMakeLists.txt new file mode 100644 index 000000000000..20f3b2ab3dcd --- /dev/null +++ b/unittests/DebugInfo/MSF/CMakeLists.txt @@ -0,0 +1,15 @@ +set(LLVM_LINK_COMPONENTS + DebugInfoMSF + ) + +set(DebugInfoMSFSources + MappedBlockStreamTest.cpp + MSFBuilderTest.cpp + MSFCommonTest.cpp + ) + +add_llvm_unittest(DebugInfoMSFTests + ${DebugInfoMSFSources} + ) + +target_link_libraries(DebugInfoMSFTests PRIVATE LLVMTestingSupport) diff --git a/unittests/DebugInfo/PDB/MSFBuilderTest.cpp b/unittests/DebugInfo/MSF/MSFBuilderTest.cpp index 23a15d14f756..a91ac8d443fe 100644 --- a/unittests/DebugInfo/PDB/MSFBuilderTest.cpp +++ b/unittests/DebugInfo/MSF/MSFBuilderTest.cpp @@ -11,10 +11,13 @@ #include "llvm/DebugInfo/MSF/MSFCommon.h" #include "llvm/Testing/Support/Error.h" +#include "gmock/gmock-matchers.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" using namespace llvm; using namespace llvm::msf; +using namespace testing; namespace { class MSFBuilderTest : public testing::Test { @@ -38,7 +41,7 @@ protected: BumpPtrAllocator Allocator; }; -} +} // namespace TEST_F(MSFBuilderTest, ValidateSuperBlockAccept) { // Test that a known good super block passes validation. @@ -359,3 +362,36 @@ TEST_F(MSFBuilderTest, DirectoryBlockHintOverestimated) { EXPECT_EQ(1U, L.DirectoryBlocks.size()); EXPECT_EQ(B + 1, L.DirectoryBlocks[0]); } + +TEST_F(MSFBuilderTest, StreamDoesntUseFpmBlocks) { + Expected<MSFBuilder> ExpectedMsf = MSFBuilder::create(Allocator, 4096); + ASSERT_THAT_EXPECTED(ExpectedMsf, Succeeded()); + auto &Msf = *ExpectedMsf; + + // A block is 4096 bytes, and every 4096 blocks we have 2 reserved FPM blocks. + // By creating add a stream that spans 4096*4096*3 bytes, we ensure that we + // cross over a couple of reserved FPM blocks, and that none of them are + // allocated to the stream. + constexpr uint32_t StreamSize = 4096 * 4096 * 3; + Expected<uint32_t> SN = Msf.addStream(StreamSize); + ASSERT_THAT_EXPECTED(SN, Succeeded()); + + auto ExpectedLayout = Msf.build(); + ASSERT_THAT_EXPECTED(ExpectedLayout, Succeeded()); + MSFLayout &L = *ExpectedLayout; + auto BlocksRef = L.StreamMap[*SN]; + std::vector<uint32_t> Blocks(BlocksRef.begin(), BlocksRef.end()); + EXPECT_EQ(StreamSize, L.StreamSizes[*SN]); + + for (uint32_t I = 0; I <= 3; ++I) { + // Pages from the regular FPM are allocated, while pages from the alt fpm + // are free. + EXPECT_FALSE(L.FreePageMap.test(1 + I * 4096)); + EXPECT_TRUE(L.FreePageMap.test(2 + I * 4096)); + } + + for (uint32_t I = 1; I <= 3; ++I) { + EXPECT_THAT(Blocks, Not(Contains(1 + I * 4096))); + EXPECT_THAT(Blocks, Not(Contains(2 + I * 4096))); + } +} diff --git a/unittests/DebugInfo/MSF/MSFCommonTest.cpp b/unittests/DebugInfo/MSF/MSFCommonTest.cpp new file mode 100644 index 000000000000..144f5b113fb5 --- /dev/null +++ b/unittests/DebugInfo/MSF/MSFCommonTest.cpp @@ -0,0 +1,104 @@ +//===- MSFBuilderTest.cpp Tests manipulation of MSF stream metadata ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::msf; + +TEST(MSFCommonTest, BytesToBlocks) { + EXPECT_EQ(0ULL, bytesToBlocks(0, 4096)); + EXPECT_EQ(1ULL, bytesToBlocks(12, 4096)); + EXPECT_EQ(1ULL, bytesToBlocks(4096, 4096)); + EXPECT_EQ(2ULL, bytesToBlocks(4097, 4096)); + EXPECT_EQ(2ULL, bytesToBlocks(600, 512)); +} + +TEST(MSFCommonTest, FpmIntervals) { + SuperBlock SB; + SB.FreeBlockMapBlock = 1; + SB.BlockSize = 4096; + + MSFLayout L; + L.SB = &SB; + + SB.NumBlocks = 12; + EXPECT_EQ(1u, getNumFpmIntervals(L, false)); + SB.NumBlocks = SB.BlockSize; + EXPECT_EQ(1u, getNumFpmIntervals(L, false)); + SB.NumBlocks = SB.BlockSize + 1; + EXPECT_EQ(1u, getNumFpmIntervals(L, false)); + SB.NumBlocks = SB.BlockSize * 8; + EXPECT_EQ(1u, getNumFpmIntervals(L, false)); + SB.NumBlocks = SB.BlockSize * 8 + 1; + EXPECT_EQ(2u, getNumFpmIntervals(L, false)); + + SB.NumBlocks = 12; + EXPECT_EQ(1u, getNumFpmIntervals(L, true)); + SB.NumBlocks = SB.BlockSize; + EXPECT_EQ(1u, getNumFpmIntervals(L, true)); + SB.NumBlocks = SB.BlockSize + 1; + EXPECT_EQ(2u, getNumFpmIntervals(L, true)); + SB.NumBlocks = SB.BlockSize * 8; + EXPECT_EQ(8u, getNumFpmIntervals(L, true)); + SB.NumBlocks = SB.BlockSize * 8 + 1; + EXPECT_EQ(9u, getNumFpmIntervals(L, true)); +} + +TEST(MSFCommonTest, FpmStreamLayout) { + SuperBlock SB; + MSFLayout L; + L.SB = &SB; + SB.FreeBlockMapBlock = 1; + + // Each FPM block has 4096 bytes for a maximum of 4096*8 allocation states. + SB.BlockSize = 4096; + + // 1. When we're not including unused FPM data, the length of the FPM stream + // should be only long enough to contain 1 bit for each block. + + // 1a. When the PDB has <= 4096*8 blocks, there should only be one FPM block. + SB.NumBlocks = 8000; + MSFStreamLayout SL = getFpmStreamLayout(L, false, false); + EXPECT_EQ(1000u, SL.Length); + EXPECT_EQ(1u, SL.Blocks.size()); + EXPECT_EQ(SB.FreeBlockMapBlock, SL.Blocks.front()); + + SL = getFpmStreamLayout(L, false, true); + EXPECT_EQ(1000u, SL.Length); + EXPECT_EQ(1u, SL.Blocks.size()); + EXPECT_EQ(3u - SB.FreeBlockMapBlock, SL.Blocks.front()); + + // 1b. When the PDB has > 4096*8 blocks, there should be multiple FPM blocks. + SB.NumBlocks = SB.BlockSize * 8 + 1; + SL = getFpmStreamLayout(L, false, false); + EXPECT_EQ(SB.BlockSize + 1, SL.Length); + EXPECT_EQ(2u, SL.Blocks.size()); + EXPECT_EQ(SB.FreeBlockMapBlock, SL.Blocks[0]); + EXPECT_EQ(SB.FreeBlockMapBlock + SB.BlockSize, SL.Blocks[1]); + + SL = getFpmStreamLayout(L, false, true); + EXPECT_EQ(SB.BlockSize + 1, SL.Length); + EXPECT_EQ(2u, SL.Blocks.size()); + EXPECT_EQ(3u - SB.FreeBlockMapBlock, SL.Blocks[0]); + EXPECT_EQ(3u - SB.FreeBlockMapBlock + SB.BlockSize, SL.Blocks[1]); + + // 2. When we are including unused FPM data, there should be one FPM block + // at every BlockSize interval in the file, even if entire FPM blocks are + // unused. + SB.NumBlocks = SB.BlockSize * 8 + 1; + SL = getFpmStreamLayout(L, true, false); + EXPECT_EQ(SB.BlockSize * 9, SL.Length); + EXPECT_EQ(9u, SL.Blocks.size()); + for (int I = 0; I < 9; ++I) + EXPECT_EQ(I * SB.BlockSize + SB.FreeBlockMapBlock, SL.Blocks[I]); +} diff --git a/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp b/unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp index a9a1ee4d65b9..639536e94be9 100644 --- a/unittests/DebugInfo/PDB/MappedBlockStreamTest.cpp +++ b/unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp @@ -1,4 +1,4 @@ -//===- llvm/unittest/DebugInfo/PDB/MappedBlockStreamTest.cpp --------------===// +//===- llvm/unittest/DebugInfo/MSF/MappedBlockStreamTest.cpp --------------===// // // The LLVM Compiler Infrastructure // @@ -8,18 +8,15 @@ //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/MSF/MappedBlockStream.h" -#include "llvm/DebugInfo/MSF/IMSFFile.h" -#include "llvm/DebugInfo/MSF/MSFError.h" -#include "llvm/DebugInfo/MSF/MSFStreamLayout.h" #include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/BinaryStreamRef.h" #include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Testing/Support/Error.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" -#include <unordered_map> using namespace llvm; using namespace llvm::msf; @@ -42,7 +39,7 @@ public: Error readBytes(uint32_t Offset, uint32_t Size, ArrayRef<uint8_t> &Buffer) override { - if (auto EC = checkOffset(Offset, Size)) + if (auto EC = checkOffsetForRead(Offset, Size)) return EC; Buffer = Data.slice(Offset, Size); return Error::success(); @@ -50,7 +47,7 @@ public: Error readLongestContiguousChunk(uint32_t Offset, ArrayRef<uint8_t> &Buffer) override { - if (auto EC = checkOffset(Offset, 1)) + if (auto EC = checkOffsetForRead(Offset, 1)) return EC; Buffer = Data.drop_front(Offset); return Error::success(); @@ -59,7 +56,7 @@ public: uint32_t getLength() override { return Data.size(); } Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> SrcData) override { - if (auto EC = checkOffset(Offset, SrcData.size())) + if (auto EC = checkOffsetForWrite(Offset, SrcData.size())) return EC; ::memcpy(&Data[Offset], SrcData.data(), SrcData.size()); return Error::success(); @@ -83,7 +80,6 @@ TEST(MappedBlockStreamTest, NumBlocks) { F.Allocator); EXPECT_EQ(F.block_size(), S->getBlockSize()); EXPECT_EQ(F.layout().Blocks.size(), S->getNumBlocks()); - } // Tests that a read which is entirely contained within a single block works @@ -255,8 +251,6 @@ TEST(MappedBlockStreamTest, WriteBeyondEndOfStream) { DiscontiguousStream F(BlocksAry, Data); auto S = WritableMappedBlockStream::createStream(F.block_size(), F.layout(), F, F.Allocator); - ArrayRef<uint8_t> Buffer; - EXPECT_THAT_ERROR(S->writeBytes(0, ArrayRef<uint8_t>(LargeBuffer)), Failed()); EXPECT_THAT_ERROR(S->writeBytes(0, ArrayRef<uint8_t>(SmallBuffer)), Succeeded()); @@ -335,7 +329,7 @@ TEST(MappedBlockStreamTest, TestWriteThenRead) { uint8_t byteArray1[] = {'0', '0'}; ArrayRef<uint8_t> byteArrayRef0(byteArray0); ArrayRef<uint8_t> byteArrayRef1(byteArray1); - ArrayRef<uint8_t> byteArray[] = { byteArrayRef0, byteArrayRef1 }; + ArrayRef<uint8_t> byteArray[] = {byteArrayRef0, byteArrayRef1}; uint32_t intArr0[] = {890723408, 29082234}; uint32_t intArr1[] = {890723408, 29082234}; ArrayRef<uint32_t> intArray[] = {intArr0, intArr1}; @@ -496,5 +490,59 @@ TEST(MappedBlockStreamTest, DataLivesAfterStreamDestruction) { EXPECT_EQ(Str[0], Str[1]); } +} // namespace + +MATCHER_P3(BlockIsFilledWith, Layout, BlockIndex, Byte, "succeeded") { + uint64_t Offset = msf::blockToOffset(BlockIndex, Layout.SB->BlockSize); + ArrayRef<uint8_t> BufferRef = makeArrayRef(arg); + BufferRef = BufferRef.slice(Offset, Layout.SB->BlockSize); + return llvm::all_of(BufferRef, [this](uint8_t B) { return B == Byte; }); +} + +namespace { +TEST(MappedBlockStreamTest, CreateFpmStream) { + BumpPtrAllocator Allocator; + SuperBlock SB; + MSFLayout L; + L.SB = &SB; + + SB.FreeBlockMapBlock = 1; + SB.BlockSize = 4096; + + constexpr uint32_t NumFileBlocks = 4096 * 4; + + std::vector<uint8_t> MsfBuffer(NumFileBlocks * SB.BlockSize); + MutableBinaryByteStream MsfStream(MsfBuffer, llvm::support::little); + + SB.NumBlocks = NumFileBlocks; + auto FpmStream = + WritableMappedBlockStream::createFpmStream(L, MsfStream, Allocator); + // 4096 * 4 / 8 = 2048 bytes of FPM data is needed to describe 4096 * 4 + // blocks. This translates to 1 FPM block. + EXPECT_EQ(2048u, FpmStream->getLength()); + EXPECT_EQ(1u, FpmStream->getStreamLayout().Blocks.size()); + EXPECT_EQ(1u, FpmStream->getStreamLayout().Blocks[0]); + // All blocks from FPM1 should be 1 initialized, and all blocks from FPM2 + // should be 0 initialized (since we requested the main FPM, not the alt FPM) + for (int I = 0; I < 4; ++I) { + EXPECT_THAT(MsfBuffer, BlockIsFilledWith(L, 1 + I * SB.BlockSize, 0xFF)); + EXPECT_THAT(MsfBuffer, BlockIsFilledWith(L, 2 + I * SB.BlockSize, 0)); + } + + ::memset(MsfBuffer.data(), 0, MsfBuffer.size()); + FpmStream = + WritableMappedBlockStream::createFpmStream(L, MsfStream, Allocator, true); + // 4096 * 4 / 8 = 2048 bytes of FPM data is needed to describe 4096 * 4 + // blocks. This translates to 1 FPM block. + EXPECT_EQ(2048u, FpmStream->getLength()); + EXPECT_EQ(1u, FpmStream->getStreamLayout().Blocks.size()); + EXPECT_EQ(2u, FpmStream->getStreamLayout().Blocks[0]); + // All blocks from FPM2 should be 1 initialized, and all blocks from FPM1 + // should be 0 initialized (since we requested the alt FPM, not the main FPM) + for (int I = 0; I < 4; ++I) { + EXPECT_THAT(MsfBuffer, BlockIsFilledWith(L, 1 + I * SB.BlockSize, 0)); + EXPECT_THAT(MsfBuffer, BlockIsFilledWith(L, 2 + I * SB.BlockSize, 0xFF)); + } +} } // end anonymous namespace diff --git a/unittests/DebugInfo/PDB/CMakeLists.txt b/unittests/DebugInfo/PDB/CMakeLists.txt index 583b065f464c..b19ee2cf43a5 100644 --- a/unittests/DebugInfo/PDB/CMakeLists.txt +++ b/unittests/DebugInfo/PDB/CMakeLists.txt @@ -6,9 +6,7 @@ set(LLVM_LINK_COMPONENTS set(DebugInfoPDBSources HashTableTest.cpp - MappedBlockStreamTest.cpp StringTableBuilderTest.cpp - MSFBuilderTest.cpp PDBApiTest.cpp ) @@ -16,4 +14,4 @@ add_llvm_unittest(DebugInfoPDBTests ${DebugInfoPDBSources} ) -target_link_libraries(DebugInfoPDBTests LLVMTestingSupport) +target_link_libraries(DebugInfoPDBTests PRIVATE LLVMTestingSupport) diff --git a/unittests/DebugInfo/PDB/PDBApiTest.cpp b/unittests/DebugInfo/PDB/PDBApiTest.cpp index 257a8879e439..e998acf009ec 100644 --- a/unittests/DebugInfo/PDB/PDBApiTest.cpp +++ b/unittests/DebugInfo/PDB/PDBApiTest.cpp @@ -14,6 +14,7 @@ #include "llvm/DebugInfo/PDB/IPDBRawSymbol.h" #include "llvm/DebugInfo/PDB/IPDBSession.h" #include "llvm/DebugInfo/PDB/IPDBSourceFile.h" +#include "llvm/DebugInfo/PDB/IPDBTable.h" #include "llvm/DebugInfo/PDB/PDBSymbol.h" #include "llvm/DebugInfo/PDB/PDBSymbolAnnotation.h" @@ -118,6 +119,10 @@ class MockSession : public IPDBSession { std::unique_ptr<IPDBEnumDataStreams> getDebugStreams() const override { return nullptr; } + + std::unique_ptr<IPDBEnumTables> getEnumTables() const override { + return nullptr; + } }; class MockRawSymbol : public IPDBRawSymbol { @@ -152,6 +157,10 @@ public: PDB_SymType getSymTag() const override { return Type; } + std::string getUndecoratedNameEx(PDB_UndnameFlags Flags) const override { + return {}; + } + MOCK_SYMBOL_ACCESSOR(getAccess) MOCK_SYMBOL_ACCESSOR(getAddressOffset) MOCK_SYMBOL_ACCESSOR(getAddressSection) |
