181ad6265SDimitry Andric //===- DXContainer.cpp - DXContainer object file implementation -----------===// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric 981ad6265SDimitry Andric #include "llvm/Object/DXContainer.h" 1081ad6265SDimitry Andric #include "llvm/BinaryFormat/DXContainer.h" 1181ad6265SDimitry Andric #include "llvm/Object/Error.h" 12bdd1243dSDimitry Andric #include "llvm/Support/FormatVariadic.h" 1381ad6265SDimitry Andric 1481ad6265SDimitry Andric using namespace llvm; 1581ad6265SDimitry Andric using namespace llvm::object; 1681ad6265SDimitry Andric 1781ad6265SDimitry Andric static Error parseFailed(const Twine &Msg) { 1881ad6265SDimitry Andric return make_error<GenericBinaryError>(Msg.str(), object_error::parse_failed); 1981ad6265SDimitry Andric } 2081ad6265SDimitry Andric 2181ad6265SDimitry Andric template <typename T> 2281ad6265SDimitry Andric static Error readStruct(StringRef Buffer, const char *Src, T &Struct) { 2381ad6265SDimitry Andric // Don't read before the beginning or past the end of the file 2481ad6265SDimitry Andric if (Src < Buffer.begin() || Src + sizeof(T) > Buffer.end()) 2581ad6265SDimitry Andric return parseFailed("Reading structure out of file bounds"); 2681ad6265SDimitry Andric 2781ad6265SDimitry Andric memcpy(&Struct, Src, sizeof(T)); 2881ad6265SDimitry Andric // DXContainer is always little endian 2981ad6265SDimitry Andric if (sys::IsBigEndianHost) 3081ad6265SDimitry Andric Struct.swapBytes(); 3181ad6265SDimitry Andric return Error::success(); 3281ad6265SDimitry Andric } 3381ad6265SDimitry Andric 3481ad6265SDimitry Andric template <typename T> 35bdd1243dSDimitry Andric static Error readInteger(StringRef Buffer, const char *Src, T &Val, 36bdd1243dSDimitry Andric Twine Str = "structure") { 37bdd1243dSDimitry Andric static_assert(std::is_integral_v<T>, 3881ad6265SDimitry Andric "Cannot call readInteger on non-integral type."); 3981ad6265SDimitry Andric // Don't read before the beginning or past the end of the file 4081ad6265SDimitry Andric if (Src < Buffer.begin() || Src + sizeof(T) > Buffer.end()) 41bdd1243dSDimitry Andric return parseFailed(Twine("Reading ") + Str + " out of file bounds"); 4281ad6265SDimitry Andric 43bdd1243dSDimitry Andric // The DXContainer offset table is comprised of uint32_t values but not padded 44bdd1243dSDimitry Andric // to a 64-bit boundary. So Parts may start unaligned if there is an odd 45bdd1243dSDimitry Andric // number of parts and part data itself is not required to be padded. 46bdd1243dSDimitry Andric if (reinterpret_cast<uintptr_t>(Src) % alignof(T) != 0) 47bdd1243dSDimitry Andric memcpy(reinterpret_cast<char *>(&Val), Src, sizeof(T)); 48bdd1243dSDimitry Andric else 4981ad6265SDimitry Andric Val = *reinterpret_cast<const T *>(Src); 5081ad6265SDimitry Andric // DXContainer is always little endian 5181ad6265SDimitry Andric if (sys::IsBigEndianHost) 5281ad6265SDimitry Andric sys::swapByteOrder(Val); 5381ad6265SDimitry Andric return Error::success(); 5481ad6265SDimitry Andric } 5581ad6265SDimitry Andric 5681ad6265SDimitry Andric DXContainer::DXContainer(MemoryBufferRef O) : Data(O) {} 5781ad6265SDimitry Andric 5881ad6265SDimitry Andric Error DXContainer::parseHeader() { 5981ad6265SDimitry Andric return readStruct(Data.getBuffer(), Data.getBuffer().data(), Header); 6081ad6265SDimitry Andric } 6181ad6265SDimitry Andric 62bdd1243dSDimitry Andric Error DXContainer::parseDXILHeader(StringRef Part) { 6381ad6265SDimitry Andric if (DXIL) 6481ad6265SDimitry Andric return parseFailed("More than one DXIL part is present in the file"); 65bdd1243dSDimitry Andric const char *Current = Part.begin(); 6681ad6265SDimitry Andric dxbc::ProgramHeader Header; 67bdd1243dSDimitry Andric if (Error Err = readStruct(Part, Current, Header)) 6881ad6265SDimitry Andric return Err; 6981ad6265SDimitry Andric Current += offsetof(dxbc::ProgramHeader, Bitcode) + Header.Bitcode.Offset; 7081ad6265SDimitry Andric DXIL.emplace(std::make_pair(Header, Current)); 7181ad6265SDimitry Andric return Error::success(); 7281ad6265SDimitry Andric } 7381ad6265SDimitry Andric 74bdd1243dSDimitry Andric Error DXContainer::parseShaderFlags(StringRef Part) { 75bdd1243dSDimitry Andric if (ShaderFlags) 76bdd1243dSDimitry Andric return parseFailed("More than one SFI0 part is present in the file"); 77bdd1243dSDimitry Andric uint64_t FlagValue = 0; 78bdd1243dSDimitry Andric if (Error Err = readInteger(Part, Part.begin(), FlagValue)) 79bdd1243dSDimitry Andric return Err; 80bdd1243dSDimitry Andric ShaderFlags = FlagValue; 81bdd1243dSDimitry Andric return Error::success(); 82bdd1243dSDimitry Andric } 83bdd1243dSDimitry Andric 84bdd1243dSDimitry Andric Error DXContainer::parseHash(StringRef Part) { 85bdd1243dSDimitry Andric if (Hash) 86bdd1243dSDimitry Andric return parseFailed("More than one HASH part is present in the file"); 87bdd1243dSDimitry Andric dxbc::ShaderHash ReadHash; 88bdd1243dSDimitry Andric if (Error Err = readStruct(Part, Part.begin(), ReadHash)) 89bdd1243dSDimitry Andric return Err; 90bdd1243dSDimitry Andric Hash = ReadHash; 91bdd1243dSDimitry Andric return Error::success(); 92bdd1243dSDimitry Andric } 93bdd1243dSDimitry Andric 94*06c3fb27SDimitry Andric Error DXContainer::parsePSVInfo(StringRef Part) { 95*06c3fb27SDimitry Andric if (PSVInfo) 96*06c3fb27SDimitry Andric return parseFailed("More than one PSV0 part is present in the file"); 97*06c3fb27SDimitry Andric PSVInfo = DirectX::PSVRuntimeInfo(Part); 98*06c3fb27SDimitry Andric // Parsing the PSVRuntime info occurs late because we need to read data from 99*06c3fb27SDimitry Andric // other parts first. 100*06c3fb27SDimitry Andric return Error::success(); 101*06c3fb27SDimitry Andric } 102*06c3fb27SDimitry Andric 10381ad6265SDimitry Andric Error DXContainer::parsePartOffsets() { 104bdd1243dSDimitry Andric uint32_t LastOffset = 105bdd1243dSDimitry Andric sizeof(dxbc::Header) + (Header.PartCount * sizeof(uint32_t)); 10681ad6265SDimitry Andric const char *Current = Data.getBuffer().data() + sizeof(dxbc::Header); 10781ad6265SDimitry Andric for (uint32_t Part = 0; Part < Header.PartCount; ++Part) { 10881ad6265SDimitry Andric uint32_t PartOffset; 10981ad6265SDimitry Andric if (Error Err = readInteger(Data.getBuffer(), Current, PartOffset)) 11081ad6265SDimitry Andric return Err; 111bdd1243dSDimitry Andric if (PartOffset < LastOffset) 112bdd1243dSDimitry Andric return parseFailed( 113bdd1243dSDimitry Andric formatv( 114bdd1243dSDimitry Andric "Part offset for part {0} begins before the previous part ends", 115bdd1243dSDimitry Andric Part) 116bdd1243dSDimitry Andric .str()); 11781ad6265SDimitry Andric Current += sizeof(uint32_t); 118bdd1243dSDimitry Andric if (PartOffset >= Data.getBufferSize()) 11981ad6265SDimitry Andric return parseFailed("Part offset points beyond boundary of the file"); 120bdd1243dSDimitry Andric // To prevent overflow when reading the part name, we subtract the part name 121bdd1243dSDimitry Andric // size from the buffer size, rather than adding to the offset. Since the 122bdd1243dSDimitry Andric // file header is larger than the part header we can't reach this code 123bdd1243dSDimitry Andric // unless the buffer is at least as large as a part header, so this 124bdd1243dSDimitry Andric // subtraction can't underflow. 125bdd1243dSDimitry Andric if (PartOffset >= Data.getBufferSize() - sizeof(dxbc::PartHeader::Name)) 126bdd1243dSDimitry Andric return parseFailed("File not large enough to read part name"); 12781ad6265SDimitry Andric PartOffsets.push_back(PartOffset); 12881ad6265SDimitry Andric 129bdd1243dSDimitry Andric dxbc::PartType PT = 130bdd1243dSDimitry Andric dxbc::parsePartType(Data.getBuffer().substr(PartOffset, 4)); 131bdd1243dSDimitry Andric uint32_t PartDataStart = PartOffset + sizeof(dxbc::PartHeader); 132bdd1243dSDimitry Andric uint32_t PartSize; 133bdd1243dSDimitry Andric if (Error Err = readInteger(Data.getBuffer(), 134bdd1243dSDimitry Andric Data.getBufferStart() + PartOffset + 4, 135bdd1243dSDimitry Andric PartSize, "part size")) 13681ad6265SDimitry Andric return Err; 137bdd1243dSDimitry Andric StringRef PartData = Data.getBuffer().substr(PartDataStart, PartSize); 138bdd1243dSDimitry Andric LastOffset = PartOffset + PartSize; 139bdd1243dSDimitry Andric switch (PT) { 140bdd1243dSDimitry Andric case dxbc::PartType::DXIL: 141bdd1243dSDimitry Andric if (Error Err = parseDXILHeader(PartData)) 142bdd1243dSDimitry Andric return Err; 143bdd1243dSDimitry Andric break; 144bdd1243dSDimitry Andric case dxbc::PartType::SFI0: 145bdd1243dSDimitry Andric if (Error Err = parseShaderFlags(PartData)) 146bdd1243dSDimitry Andric return Err; 147bdd1243dSDimitry Andric break; 148bdd1243dSDimitry Andric case dxbc::PartType::HASH: 149bdd1243dSDimitry Andric if (Error Err = parseHash(PartData)) 150bdd1243dSDimitry Andric return Err; 151bdd1243dSDimitry Andric break; 152*06c3fb27SDimitry Andric case dxbc::PartType::PSV0: 153*06c3fb27SDimitry Andric if (Error Err = parsePSVInfo(PartData)) 154*06c3fb27SDimitry Andric return Err; 155*06c3fb27SDimitry Andric break; 156bdd1243dSDimitry Andric case dxbc::PartType::Unknown: 157bdd1243dSDimitry Andric break; 158bdd1243dSDimitry Andric } 15981ad6265SDimitry Andric } 160*06c3fb27SDimitry Andric 161*06c3fb27SDimitry Andric // Fully parsing the PSVInfo requires knowing the shader kind which we read 162*06c3fb27SDimitry Andric // out of the program header in the DXIL part. 163*06c3fb27SDimitry Andric if (PSVInfo) { 164*06c3fb27SDimitry Andric if (!DXIL) 165*06c3fb27SDimitry Andric return parseFailed("Cannot fully parse pipeline state validation " 166*06c3fb27SDimitry Andric "information without DXIL part."); 167*06c3fb27SDimitry Andric if (Error Err = PSVInfo->parse(DXIL->first.ShaderKind)) 168*06c3fb27SDimitry Andric return Err; 169*06c3fb27SDimitry Andric } 17081ad6265SDimitry Andric return Error::success(); 17181ad6265SDimitry Andric } 17281ad6265SDimitry Andric 17381ad6265SDimitry Andric Expected<DXContainer> DXContainer::create(MemoryBufferRef Object) { 17481ad6265SDimitry Andric DXContainer Container(Object); 17581ad6265SDimitry Andric if (Error Err = Container.parseHeader()) 17681ad6265SDimitry Andric return std::move(Err); 17781ad6265SDimitry Andric if (Error Err = Container.parsePartOffsets()) 17881ad6265SDimitry Andric return std::move(Err); 17981ad6265SDimitry Andric return Container; 18081ad6265SDimitry Andric } 18181ad6265SDimitry Andric 18281ad6265SDimitry Andric void DXContainer::PartIterator::updateIteratorImpl(const uint32_t Offset) { 18381ad6265SDimitry Andric StringRef Buffer = Container.Data.getBuffer(); 18481ad6265SDimitry Andric const char *Current = Buffer.data() + Offset; 18581ad6265SDimitry Andric // Offsets are validated during parsing, so all offsets in the container are 18681ad6265SDimitry Andric // valid and contain enough readable data to read a header. 18781ad6265SDimitry Andric cantFail(readStruct(Buffer, Current, IteratorState.Part)); 18881ad6265SDimitry Andric IteratorState.Data = 18981ad6265SDimitry Andric StringRef(Current + sizeof(dxbc::PartHeader), IteratorState.Part.Size); 19081ad6265SDimitry Andric IteratorState.Offset = Offset; 19181ad6265SDimitry Andric } 192*06c3fb27SDimitry Andric 193*06c3fb27SDimitry Andric Error DirectX::PSVRuntimeInfo::parse(uint16_t ShaderKind) { 194*06c3fb27SDimitry Andric Triple::EnvironmentType ShaderStage = dxbc::getShaderStage(ShaderKind); 195*06c3fb27SDimitry Andric 196*06c3fb27SDimitry Andric const char *Current = Data.begin(); 197*06c3fb27SDimitry Andric if (Error Err = readInteger(Data, Current, Size)) 198*06c3fb27SDimitry Andric return Err; 199*06c3fb27SDimitry Andric Current += sizeof(uint32_t); 200*06c3fb27SDimitry Andric 201*06c3fb27SDimitry Andric StringRef PSVInfoData = Data.substr(sizeof(uint32_t), Size); 202*06c3fb27SDimitry Andric 203*06c3fb27SDimitry Andric if (PSVInfoData.size() < Size) 204*06c3fb27SDimitry Andric return parseFailed( 205*06c3fb27SDimitry Andric "Pipeline state data extends beyond the bounds of the part"); 206*06c3fb27SDimitry Andric 207*06c3fb27SDimitry Andric using namespace dxbc::PSV; 208*06c3fb27SDimitry Andric 209*06c3fb27SDimitry Andric const uint32_t PSVVersion = getVersion(); 210*06c3fb27SDimitry Andric 211*06c3fb27SDimitry Andric // Detect the PSVVersion by looking at the size field. 212*06c3fb27SDimitry Andric if (PSVVersion == 2) { 213*06c3fb27SDimitry Andric v2::RuntimeInfo Info; 214*06c3fb27SDimitry Andric if (Error Err = readStruct(PSVInfoData, Current, Info)) 215*06c3fb27SDimitry Andric return Err; 216*06c3fb27SDimitry Andric if (sys::IsBigEndianHost) 217*06c3fb27SDimitry Andric Info.swapBytes(ShaderStage); 218*06c3fb27SDimitry Andric BasicInfo = Info; 219*06c3fb27SDimitry Andric } else if (PSVVersion == 1) { 220*06c3fb27SDimitry Andric v1::RuntimeInfo Info; 221*06c3fb27SDimitry Andric if (Error Err = readStruct(PSVInfoData, Current, Info)) 222*06c3fb27SDimitry Andric return Err; 223*06c3fb27SDimitry Andric if (sys::IsBigEndianHost) 224*06c3fb27SDimitry Andric Info.swapBytes(ShaderStage); 225*06c3fb27SDimitry Andric BasicInfo = Info; 226*06c3fb27SDimitry Andric } else { 227*06c3fb27SDimitry Andric v0::RuntimeInfo Info; 228*06c3fb27SDimitry Andric if (Error Err = readStruct(PSVInfoData, Current, Info)) 229*06c3fb27SDimitry Andric return Err; 230*06c3fb27SDimitry Andric if (sys::IsBigEndianHost) 231*06c3fb27SDimitry Andric Info.swapBytes(ShaderStage); 232*06c3fb27SDimitry Andric BasicInfo = Info; 233*06c3fb27SDimitry Andric } 234*06c3fb27SDimitry Andric Current += Size; 235*06c3fb27SDimitry Andric 236*06c3fb27SDimitry Andric uint32_t ResourceCount = 0; 237*06c3fb27SDimitry Andric if (Error Err = readInteger(Data, Current, ResourceCount)) 238*06c3fb27SDimitry Andric return Err; 239*06c3fb27SDimitry Andric Current += sizeof(uint32_t); 240*06c3fb27SDimitry Andric 241*06c3fb27SDimitry Andric if (ResourceCount > 0) { 242*06c3fb27SDimitry Andric if (Error Err = readInteger(Data, Current, Resources.Stride)) 243*06c3fb27SDimitry Andric return Err; 244*06c3fb27SDimitry Andric Current += sizeof(uint32_t); 245*06c3fb27SDimitry Andric 246*06c3fb27SDimitry Andric size_t BindingDataSize = Resources.Stride * ResourceCount; 247*06c3fb27SDimitry Andric Resources.Data = Data.substr(Current - Data.begin(), BindingDataSize); 248*06c3fb27SDimitry Andric 249*06c3fb27SDimitry Andric if (Resources.Data.size() < BindingDataSize) 250*06c3fb27SDimitry Andric return parseFailed( 251*06c3fb27SDimitry Andric "Resource binding data extends beyond the bounds of the part"); 252*06c3fb27SDimitry Andric 253*06c3fb27SDimitry Andric Current += BindingDataSize; 254*06c3fb27SDimitry Andric } 255*06c3fb27SDimitry Andric 256*06c3fb27SDimitry Andric return Error::success(); 257*06c3fb27SDimitry Andric } 258