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
parseFailed(const Twine & Msg)16 static Error parseFailed(const Twine &Msg) {
17 return make_error<GenericBinaryError>(Msg.str(), object_error::parse_failed);
18 }
19
20 template <typename T>
readStruct(StringRef Buffer,const char * Src,T & Struct)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>
readInteger(StringRef Buffer,const char * Src,T & Val)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 assert(reinterpret_cast<uintptr_t>(Src) % alignof(T) == 0 &&
38 "Unaligned read of value from buffer!");
39 // Don't read before the beginning or past the end of the file
40 if (Src < Buffer.begin() || Src + sizeof(T) > Buffer.end())
41 return parseFailed("Reading structure out of file bounds");
42
43 Val = *reinterpret_cast<const T *>(Src);
44 // DXContainer is always little endian
45 if (sys::IsBigEndianHost)
46 sys::swapByteOrder(Val);
47 return Error::success();
48 }
49
DXContainer(MemoryBufferRef O)50 DXContainer::DXContainer(MemoryBufferRef O) : Data(O) {}
51
parseHeader()52 Error DXContainer::parseHeader() {
53 return readStruct(Data.getBuffer(), Data.getBuffer().data(), Header);
54 }
55
parseDXILHeader(uint32_t Offset)56 Error DXContainer::parseDXILHeader(uint32_t Offset) {
57 if (DXIL)
58 return parseFailed("More than one DXIL part is present in the file");
59 const char *Current = Data.getBuffer().data() + Offset;
60 dxbc::ProgramHeader Header;
61 if (Error Err = readStruct(Data.getBuffer(), Current, Header))
62 return Err;
63 Current += offsetof(dxbc::ProgramHeader, Bitcode) + Header.Bitcode.Offset;
64 DXIL.emplace(std::make_pair(Header, Current));
65 return Error::success();
66 }
67
parsePartOffsets()68 Error DXContainer::parsePartOffsets() {
69 const char *Current = Data.getBuffer().data() + sizeof(dxbc::Header);
70 for (uint32_t Part = 0; Part < Header.PartCount; ++Part) {
71 uint32_t PartOffset;
72 if (Error Err = readInteger(Data.getBuffer(), Current, PartOffset))
73 return Err;
74 Current += sizeof(uint32_t);
75 // We need to ensure that each part offset leaves enough space for a part
76 // header. To prevent overflow, we subtract the part header size from the
77 // buffer size, rather than adding to the offset. Since the file header is
78 // larger than the part header we can't reach this code unless the buffer
79 // is larger than the part header, so this can't underflow.
80 if (PartOffset > Data.getBufferSize() - sizeof(dxbc::PartHeader))
81 return parseFailed("Part offset points beyond boundary of the file");
82 PartOffsets.push_back(PartOffset);
83
84 // If this isn't a dxil part stop here...
85 if (Data.getBuffer().substr(PartOffset, 4) != "DXIL")
86 continue;
87 if (Error Err = parseDXILHeader(PartOffset + sizeof(dxbc::PartHeader)))
88 return Err;
89 }
90 return Error::success();
91 }
92
create(MemoryBufferRef Object)93 Expected<DXContainer> DXContainer::create(MemoryBufferRef Object) {
94 DXContainer Container(Object);
95 if (Error Err = Container.parseHeader())
96 return std::move(Err);
97 if (Error Err = Container.parsePartOffsets())
98 return std::move(Err);
99 return Container;
100 }
101
updateIteratorImpl(const uint32_t Offset)102 void DXContainer::PartIterator::updateIteratorImpl(const uint32_t Offset) {
103 StringRef Buffer = Container.Data.getBuffer();
104 const char *Current = Buffer.data() + Offset;
105 // Offsets are validated during parsing, so all offsets in the container are
106 // valid and contain enough readable data to read a header.
107 cantFail(readStruct(Buffer, Current, IteratorState.Part));
108 IteratorState.Data =
109 StringRef(Current + sizeof(dxbc::PartHeader), IteratorState.Part.Size);
110 IteratorState.Offset = Offset;
111 }
112