1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
//===- 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;
}
|