1 //===- DXContainerTest.cpp - Tests for DXContainerFile --------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/Object/DXContainer.h" 10 #include "llvm/ADT/StringRef.h" 11 #include "llvm/BinaryFormat/Magic.h" 12 #include "llvm/Support/MemoryBufferRef.h" 13 #include "llvm/Testing/Support/Error.h" 14 #include "gtest/gtest.h" 15 16 using namespace llvm; 17 using namespace llvm::object; 18 19 template <std::size_t X> MemoryBufferRef getMemoryBuffer(uint8_t Data[X]) { 20 StringRef Obj(reinterpret_cast<char *>(&Data[0]), X); 21 return MemoryBufferRef(Obj, ""); 22 } 23 24 TEST(DXCFile, IdentifyMagic) { 25 { 26 StringRef Buffer("DXBC"); 27 EXPECT_EQ(identify_magic(Buffer), file_magic::dxcontainer_object); 28 } 29 { 30 StringRef Buffer("DXBCBlahBlahBlah"); 31 EXPECT_EQ(identify_magic(Buffer), file_magic::dxcontainer_object); 32 } 33 } 34 35 TEST(DXCFile, ParseHeaderErrors) { 36 uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43}; 37 EXPECT_THAT_EXPECTED( 38 DXContainer::create(getMemoryBuffer<4>(Buffer)), 39 FailedWithMessage("Reading structure out of file bounds")); 40 } 41 42 TEST(DXCFile, EmptyFile) { 43 EXPECT_THAT_EXPECTED( 44 DXContainer::create(MemoryBufferRef(StringRef("", 0), "")), 45 FailedWithMessage("Reading structure out of file bounds")); 46 } 47 48 TEST(DXCFile, ParseHeader) { 49 uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 50 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 51 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 52 0x70, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 53 DXContainer C = 54 llvm::cantFail(DXContainer::create(getMemoryBuffer<32>(Buffer))); 55 EXPECT_TRUE(memcmp(C.getHeader().Magic, "DXBC", 4) == 0); 56 EXPECT_TRUE(memcmp(C.getHeader().FileHash.Digest, 57 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0); 58 EXPECT_EQ(C.getHeader().Version.Major, 1u); 59 EXPECT_EQ(C.getHeader().Version.Minor, 0u); 60 } 61 62 TEST(DXCFile, ParsePartMissingOffsets) { 63 uint8_t Buffer[] = { 64 0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 65 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 66 0x00, 0x00, 0x70, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 67 }; 68 EXPECT_THAT_EXPECTED( 69 DXContainer::create(getMemoryBuffer<32>(Buffer)), 70 FailedWithMessage("Reading structure out of file bounds")); 71 } 72 73 TEST(DXCFile, ParsePartInvalidOffsets) { 74 // This test covers a case where the part offset is beyond the buffer size. 75 uint8_t Buffer[] = { 76 0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 77 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 78 0x70, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 79 }; 80 EXPECT_THAT_EXPECTED( 81 DXContainer::create(getMemoryBuffer<36>(Buffer)), 82 FailedWithMessage("Part offset points beyond boundary of the file")); 83 } 84 85 TEST(DXCFile, ParsePartTooSmallBuffer) { 86 // This test covers a case where there is insufficent space to read a full 87 // part name, but the offset for the part is inside the buffer. 88 uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 89 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 90 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 91 0x26, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 92 0x24, 0x00, 0x00, 0x00, 0x46, 0x4B}; 93 EXPECT_THAT_EXPECTED( 94 DXContainer::create(getMemoryBuffer<38>(Buffer)), 95 FailedWithMessage("File not large enough to read part name")); 96 } 97 98 TEST(DXCFile, ParsePartNoSize) { 99 // This test covers a case where the part's header is readable, but the size 100 // the part extends beyond the boundaries of the file. 101 uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 103 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x0D, 0x00, 104 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 105 0x46, 0x4B, 0x45, 0x30, 0x00, 0x00}; 106 EXPECT_THAT_EXPECTED( 107 DXContainer::create(getMemoryBuffer<42>(Buffer)), 108 FailedWithMessage("Reading part size out of file bounds")); 109 } 110 111 TEST(DXCFile, ParseOverlappingParts) { 112 // This test covers a case where a part's offset is inside the size range 113 // covered by the previous part. 114 uint8_t Buffer[] = { 115 0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 116 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 117 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 118 0x2C, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x30, 0x08, 0x00, 0x00, 0x00, 119 0x46, 0x4B, 0x45, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 120 }; 121 EXPECT_THAT_EXPECTED( 122 DXContainer::create(getMemoryBuffer<60>(Buffer)), 123 FailedWithMessage( 124 "Part offset for part 1 begins before the previous part ends")); 125 } 126 127 TEST(DXCFile, ParseEmptyParts) { 128 uint8_t Buffer[] = { 129 0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 131 0x70, 0x0D, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 132 0x44, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 133 0x5C, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 134 0x46, 0x4B, 0x45, 0x30, 0x00, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x31, 135 0x00, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x32, 0x00, 0x00, 0x00, 0x00, 136 0x46, 0x4B, 0x45, 0x33, 0x00, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x34, 137 0x00, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x35, 0x00, 0x00, 0x00, 0x00, 138 0x46, 0x4B, 0x45, 0x36, 0x00, 0x00, 0x00, 0x00, 139 }; 140 DXContainer C = 141 llvm::cantFail(DXContainer::create(getMemoryBuffer<116>(Buffer))); 142 EXPECT_EQ(C.getHeader().PartCount, 7u); 143 144 // All the part sizes are 0, which makes a nice test of the range based for 145 int ElementsVisited = 0; 146 for (auto Part : C) { 147 EXPECT_EQ(Part.Part.Size, 0u); 148 EXPECT_EQ(Part.Data.size(), 0u); 149 ++ElementsVisited; 150 } 151 EXPECT_EQ(ElementsVisited, 7); 152 153 { 154 // These are all intended to be fake part names so that the parser doesn't 155 // try to parse the part data. 156 auto It = C.begin(); 157 EXPECT_TRUE(memcmp(It->Part.Name, "FKE0", 4) == 0); 158 ++It; 159 EXPECT_TRUE(memcmp(It->Part.Name, "FKE1", 4) == 0); 160 ++It; 161 EXPECT_TRUE(memcmp(It->Part.Name, "FKE2", 4) == 0); 162 ++It; 163 EXPECT_TRUE(memcmp(It->Part.Name, "FKE3", 4) == 0); 164 ++It; 165 EXPECT_TRUE(memcmp(It->Part.Name, "FKE4", 4) == 0); 166 ++It; 167 EXPECT_TRUE(memcmp(It->Part.Name, "FKE5", 4) == 0); 168 ++It; 169 EXPECT_TRUE(memcmp(It->Part.Name, "FKE6", 4) == 0); 170 ++It; // Don't increment past the end 171 EXPECT_TRUE(memcmp(It->Part.Name, "FKE6", 4) == 0); 172 } 173 } 174