1 //===- DXContainer.cpp - DXContainer object file implementation -----------===// 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/BinaryFormat/DXContainer.h" 11 #include "llvm/Object/Error.h" 12 13 using namespace llvm; 14 using namespace llvm::object; 15 16 static Error parseFailed(const Twine &Msg) { 17 return make_error<GenericBinaryError>(Msg.str(), object_error::parse_failed); 18 } 19 20 template <typename T> 21 static Error readStruct(StringRef Buffer, const char *Src, T &Struct) { 22 // Don't read before the beginning or past the end of the file 23 if (Src < Buffer.begin() || Src + sizeof(T) > Buffer.end()) 24 return parseFailed("Reading structure out of file bounds"); 25 26 memcpy(&Struct, Src, sizeof(T)); 27 // DXContainer is always little endian 28 if (sys::IsBigEndianHost) 29 Struct.swapBytes(); 30 return Error::success(); 31 } 32 33 template <typename T> 34 static Error readInteger(StringRef Buffer, const char *Src, T &Val) { 35 static_assert(std::is_integral<T>::value, 36 "Cannot call readInteger on non-integral type."); 37 // Don't read before the beginning or past the end of the file 38 if (Src < Buffer.begin() || Src + sizeof(T) > Buffer.end()) 39 return parseFailed("Reading structure out of file bounds"); 40 41 // The DXContainer offset table is comprised of uint32_t values but not padded 42 // to a 64-bit boundary. So Parts may start unaligned if there is an odd 43 // number of parts and part data itself is not required to be padded. 44 if (reinterpret_cast<uintptr_t>(Src) % alignof(T) != 0) 45 memcpy(reinterpret_cast<char *>(&Val), Src, sizeof(T)); 46 else 47 Val = *reinterpret_cast<const T *>(Src); 48 // DXContainer is always little endian 49 if (sys::IsBigEndianHost) 50 sys::swapByteOrder(Val); 51 return Error::success(); 52 } 53 54 DXContainer::DXContainer(MemoryBufferRef O) : Data(O) {} 55 56 Error DXContainer::parseHeader() { 57 return readStruct(Data.getBuffer(), Data.getBuffer().data(), Header); 58 } 59 60 Error DXContainer::parseDXILHeader(uint32_t Offset) { 61 if (DXIL) 62 return parseFailed("More than one DXIL part is present in the file"); 63 const char *Current = Data.getBuffer().data() + Offset; 64 dxbc::ProgramHeader Header; 65 if (Error Err = readStruct(Data.getBuffer(), Current, Header)) 66 return Err; 67 Current += offsetof(dxbc::ProgramHeader, Bitcode) + Header.Bitcode.Offset; 68 DXIL.emplace(std::make_pair(Header, Current)); 69 return Error::success(); 70 } 71 72 Error DXContainer::parseShaderFlags(uint32_t Offset) { 73 if (ShaderFlags) 74 return parseFailed("More than one SFI0 part is present in the file"); 75 const char *Current = Data.getBuffer().data() + Offset; 76 uint64_t FlagValue = 0; 77 if (Error Err = readInteger(Data.getBuffer(), Current, FlagValue)) 78 return Err; 79 ShaderFlags = FlagValue; 80 return Error::success(); 81 } 82 83 Error DXContainer::parseHash(uint32_t Offset) { 84 if (Hash) 85 return parseFailed("More than one HASH part is present in the file"); 86 const char *Current = Data.getBuffer().data() + Offset; 87 dxbc::ShaderHash ReadHash; 88 if (Error Err = readStruct(Data.getBuffer(), Current, ReadHash)) 89 return Err; 90 Hash = ReadHash; 91 return Error::success(); 92 } 93 94 Error DXContainer::parsePartOffsets() { 95 const char *Current = Data.getBuffer().data() + sizeof(dxbc::Header); 96 for (uint32_t Part = 0; Part < Header.PartCount; ++Part) { 97 uint32_t PartOffset; 98 if (Error Err = readInteger(Data.getBuffer(), Current, PartOffset)) 99 return Err; 100 Current += sizeof(uint32_t); 101 // We need to ensure that each part offset leaves enough space for a part 102 // header. To prevent overflow, we subtract the part header size from the 103 // buffer size, rather than adding to the offset. Since the file header is 104 // larger than the part header we can't reach this code unless the buffer 105 // is larger than the part header, so this can't underflow. 106 if (PartOffset > Data.getBufferSize() - sizeof(dxbc::PartHeader)) 107 return parseFailed("Part offset points beyond boundary of the file"); 108 PartOffsets.push_back(PartOffset); 109 110 dxbc::PartType PT = 111 dxbc::parsePartType(Data.getBuffer().substr(PartOffset, 4)); 112 switch (PT) { 113 case dxbc::PartType::DXIL: 114 if (Error Err = parseDXILHeader(PartOffset + sizeof(dxbc::PartHeader))) 115 return Err; 116 break; 117 case dxbc::PartType::SFI0: 118 if (Error Err = parseShaderFlags(PartOffset + sizeof(dxbc::PartHeader))) 119 return Err; 120 break; 121 case dxbc::PartType::HASH: 122 if (Error Err = parseHash(PartOffset + sizeof(dxbc::PartHeader))) 123 return Err; 124 break; 125 case dxbc::PartType::Unknown: 126 break; 127 } 128 } 129 return Error::success(); 130 } 131 132 Expected<DXContainer> DXContainer::create(MemoryBufferRef Object) { 133 DXContainer Container(Object); 134 if (Error Err = Container.parseHeader()) 135 return std::move(Err); 136 if (Error Err = Container.parsePartOffsets()) 137 return std::move(Err); 138 return Container; 139 } 140 141 void DXContainer::PartIterator::updateIteratorImpl(const uint32_t Offset) { 142 StringRef Buffer = Container.Data.getBuffer(); 143 const char *Current = Buffer.data() + Offset; 144 // Offsets are validated during parsing, so all offsets in the container are 145 // valid and contain enough readable data to read a header. 146 cantFail(readStruct(Buffer, Current, IteratorState.Part)); 147 IteratorState.Data = 148 StringRef(Current + sizeof(dxbc::PartHeader), IteratorState.Part.Size); 149 IteratorState.Offset = Offset; 150 } 151