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 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 50 DXContainer::DXContainer(MemoryBufferRef O) : Data(O) {} 51 52 Error DXContainer::parseHeader() { 53 return readStruct(Data.getBuffer(), Data.getBuffer().data(), Header); 54 } 55 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 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 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 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