| //===- DXContainer.cpp - DXContainer object file implementation -----------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/Object/DXContainer.h" |
| #include "llvm/BinaryFormat/DXContainer.h" |
| #include "llvm/Object/Error.h" |
| #include "llvm/Support/FormatVariadic.h" |
| |
| using namespace llvm; |
| using namespace llvm::object; |
| |
| static Error parseFailed(const Twine &Msg) { |
| return make_error<GenericBinaryError>(Msg.str(), object_error::parse_failed); |
| } |
| |
| template <typename T> |
| static Error readStruct(StringRef Buffer, const char *Src, T &Struct) { |
| // Don't read before the beginning or past the end of the file |
| if (Src < Buffer.begin() || Src + sizeof(T) > Buffer.end()) |
| return parseFailed("Reading structure out of file bounds"); |
| |
| memcpy(&Struct, Src, sizeof(T)); |
| // DXContainer is always little endian |
| if (sys::IsBigEndianHost) |
| Struct.swapBytes(); |
| return Error::success(); |
| } |
| |
| template <typename T> |
| static Error readInteger(StringRef Buffer, const char *Src, T &Val, |
| Twine Str = "structure") { |
| static_assert(std::is_integral_v<T>, |
| "Cannot call readInteger on non-integral type."); |
| // Don't read before the beginning or past the end of the file |
| if (Src < Buffer.begin() || Src + sizeof(T) > Buffer.end()) |
| return parseFailed(Twine("Reading ") + Str + " out of file bounds"); |
| |
| // The DXContainer offset table is comprised of uint32_t values but not padded |
| // to a 64-bit boundary. So Parts may start unaligned if there is an odd |
| // number of parts and part data itself is not required to be padded. |
| if (reinterpret_cast<uintptr_t>(Src) % alignof(T) != 0) |
| memcpy(reinterpret_cast<char *>(&Val), Src, sizeof(T)); |
| else |
| Val = *reinterpret_cast<const T *>(Src); |
| // DXContainer is always little endian |
| if (sys::IsBigEndianHost) |
| sys::swapByteOrder(Val); |
| return Error::success(); |
| } |
| |
| DXContainer::DXContainer(MemoryBufferRef O) : Data(O) {} |
| |
| Error DXContainer::parseHeader() { |
| return readStruct(Data.getBuffer(), Data.getBuffer().data(), Header); |
| } |
| |
| Error DXContainer::parseDXILHeader(StringRef Part) { |
| if (DXIL) |
| return parseFailed("More than one DXIL part is present in the file"); |
| const char *Current = Part.begin(); |
| dxbc::ProgramHeader Header; |
| if (Error Err = readStruct(Part, Current, Header)) |
| return Err; |
| Current += offsetof(dxbc::ProgramHeader, Bitcode) + Header.Bitcode.Offset; |
| DXIL.emplace(std::make_pair(Header, Current)); |
| return Error::success(); |
| } |
| |
| Error DXContainer::parseShaderFlags(StringRef Part) { |
| if (ShaderFlags) |
| return parseFailed("More than one SFI0 part is present in the file"); |
| uint64_t FlagValue = 0; |
| if (Error Err = readInteger(Part, Part.begin(), FlagValue)) |
| return Err; |
| ShaderFlags = FlagValue; |
| return Error::success(); |
| } |
| |
| Error DXContainer::parseHash(StringRef Part) { |
| if (Hash) |
| return parseFailed("More than one HASH part is present in the file"); |
| dxbc::ShaderHash ReadHash; |
| if (Error Err = readStruct(Part, Part.begin(), ReadHash)) |
| return Err; |
| Hash = ReadHash; |
| return Error::success(); |
| } |
| |
| Error DXContainer::parsePartOffsets() { |
| uint32_t LastOffset = |
| sizeof(dxbc::Header) + (Header.PartCount * sizeof(uint32_t)); |
| const char *Current = Data.getBuffer().data() + sizeof(dxbc::Header); |
| for (uint32_t Part = 0; Part < Header.PartCount; ++Part) { |
| uint32_t PartOffset; |
| if (Error Err = readInteger(Data.getBuffer(), Current, PartOffset)) |
| return Err; |
| if (PartOffset < LastOffset) |
| return parseFailed( |
| formatv( |
| "Part offset for part {0} begins before the previous part ends", |
| Part) |
| .str()); |
| Current += sizeof(uint32_t); |
| if (PartOffset >= Data.getBufferSize()) |
| return parseFailed("Part offset points beyond boundary of the file"); |
| // To prevent overflow when reading the part name, we subtract the part name |
| // size from the buffer size, rather than adding to the offset. Since the |
| // file header is larger than the part header we can't reach this code |
| // unless the buffer is at least as large as a part header, so this |
| // subtraction can't underflow. |
| if (PartOffset >= Data.getBufferSize() - sizeof(dxbc::PartHeader::Name)) |
| return parseFailed("File not large enough to read part name"); |
| PartOffsets.push_back(PartOffset); |
| |
| dxbc::PartType PT = |
| dxbc::parsePartType(Data.getBuffer().substr(PartOffset, 4)); |
| uint32_t PartDataStart = PartOffset + sizeof(dxbc::PartHeader); |
| uint32_t PartSize; |
| if (Error Err = readInteger(Data.getBuffer(), |
| Data.getBufferStart() + PartOffset + 4, |
| PartSize, "part size")) |
| return Err; |
| StringRef PartData = Data.getBuffer().substr(PartDataStart, PartSize); |
| LastOffset = PartOffset + PartSize; |
| switch (PT) { |
| case dxbc::PartType::DXIL: |
| if (Error Err = parseDXILHeader(PartData)) |
| return Err; |
| break; |
| case dxbc::PartType::SFI0: |
| if (Error Err = parseShaderFlags(PartData)) |
| return Err; |
| break; |
| case dxbc::PartType::HASH: |
| if (Error Err = parseHash(PartData)) |
| return Err; |
| break; |
| case dxbc::PartType::Unknown: |
| break; |
| } |
| } |
| return Error::success(); |
| } |
| |
| Expected<DXContainer> DXContainer::create(MemoryBufferRef Object) { |
| DXContainer Container(Object); |
| if (Error Err = Container.parseHeader()) |
| return std::move(Err); |
| if (Error Err = Container.parsePartOffsets()) |
| return std::move(Err); |
| return Container; |
| } |
| |
| void DXContainer::PartIterator::updateIteratorImpl(const uint32_t Offset) { |
| StringRef Buffer = Container.Data.getBuffer(); |
| const char *Current = Buffer.data() + Offset; |
| // Offsets are validated during parsing, so all offsets in the container are |
| // valid and contain enough readable data to read a header. |
| cantFail(readStruct(Buffer, Current, IteratorState.Part)); |
| IteratorState.Data = |
| StringRef(Current + sizeof(dxbc::PartHeader), IteratorState.Part.Size); |
| IteratorState.Offset = Offset; |
| } |