DXContainer.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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. #include "llvm/Object/DXContainer.h"
  9. #include "llvm/BinaryFormat/DXContainer.h"
  10. #include "llvm/Object/Error.h"
  11. #include "llvm/Support/FormatVariadic.h"
  12. using namespace llvm;
  13. using namespace llvm::object;
  14. static Error parseFailed(const Twine &Msg) {
  15. return make_error<GenericBinaryError>(Msg.str(), object_error::parse_failed);
  16. }
  17. template <typename T>
  18. static Error readStruct(StringRef Buffer, const char *Src, T &Struct) {
  19. // Don't read before the beginning or past the end of the file
  20. if (Src < Buffer.begin() || Src + sizeof(T) > Buffer.end())
  21. return parseFailed("Reading structure out of file bounds");
  22. memcpy(&Struct, Src, sizeof(T));
  23. // DXContainer is always little endian
  24. if (sys::IsBigEndianHost)
  25. Struct.swapBytes();
  26. return Error::success();
  27. }
  28. template <typename T>
  29. static Error readInteger(StringRef Buffer, const char *Src, T &Val,
  30. Twine Str = "structure") {
  31. static_assert(std::is_integral_v<T>,
  32. "Cannot call readInteger on non-integral type.");
  33. // Don't read before the beginning or past the end of the file
  34. if (Src < Buffer.begin() || Src + sizeof(T) > Buffer.end())
  35. return parseFailed(Twine("Reading ") + Str + " out of file bounds");
  36. // The DXContainer offset table is comprised of uint32_t values but not padded
  37. // to a 64-bit boundary. So Parts may start unaligned if there is an odd
  38. // number of parts and part data itself is not required to be padded.
  39. if (reinterpret_cast<uintptr_t>(Src) % alignof(T) != 0)
  40. memcpy(reinterpret_cast<char *>(&Val), Src, sizeof(T));
  41. else
  42. Val = *reinterpret_cast<const T *>(Src);
  43. // DXContainer is always little endian
  44. if (sys::IsBigEndianHost)
  45. sys::swapByteOrder(Val);
  46. return Error::success();
  47. }
  48. DXContainer::DXContainer(MemoryBufferRef O) : Data(O) {}
  49. Error DXContainer::parseHeader() {
  50. return readStruct(Data.getBuffer(), Data.getBuffer().data(), Header);
  51. }
  52. Error DXContainer::parseDXILHeader(StringRef Part) {
  53. if (DXIL)
  54. return parseFailed("More than one DXIL part is present in the file");
  55. const char *Current = Part.begin();
  56. dxbc::ProgramHeader Header;
  57. if (Error Err = readStruct(Part, Current, Header))
  58. return Err;
  59. Current += offsetof(dxbc::ProgramHeader, Bitcode) + Header.Bitcode.Offset;
  60. DXIL.emplace(std::make_pair(Header, Current));
  61. return Error::success();
  62. }
  63. Error DXContainer::parseShaderFlags(StringRef Part) {
  64. if (ShaderFlags)
  65. return parseFailed("More than one SFI0 part is present in the file");
  66. uint64_t FlagValue = 0;
  67. if (Error Err = readInteger(Part, Part.begin(), FlagValue))
  68. return Err;
  69. ShaderFlags = FlagValue;
  70. return Error::success();
  71. }
  72. Error DXContainer::parseHash(StringRef Part) {
  73. if (Hash)
  74. return parseFailed("More than one HASH part is present in the file");
  75. dxbc::ShaderHash ReadHash;
  76. if (Error Err = readStruct(Part, Part.begin(), ReadHash))
  77. return Err;
  78. Hash = ReadHash;
  79. return Error::success();
  80. }
  81. Error DXContainer::parsePartOffsets() {
  82. uint32_t LastOffset =
  83. sizeof(dxbc::Header) + (Header.PartCount * sizeof(uint32_t));
  84. const char *Current = Data.getBuffer().data() + sizeof(dxbc::Header);
  85. for (uint32_t Part = 0; Part < Header.PartCount; ++Part) {
  86. uint32_t PartOffset;
  87. if (Error Err = readInteger(Data.getBuffer(), Current, PartOffset))
  88. return Err;
  89. if (PartOffset < LastOffset)
  90. return parseFailed(
  91. formatv(
  92. "Part offset for part {0} begins before the previous part ends",
  93. Part)
  94. .str());
  95. Current += sizeof(uint32_t);
  96. if (PartOffset >= Data.getBufferSize())
  97. return parseFailed("Part offset points beyond boundary of the file");
  98. // To prevent overflow when reading the part name, we subtract the part name
  99. // size from the buffer size, rather than adding to the offset. Since the
  100. // file header is larger than the part header we can't reach this code
  101. // unless the buffer is at least as large as a part header, so this
  102. // subtraction can't underflow.
  103. if (PartOffset >= Data.getBufferSize() - sizeof(dxbc::PartHeader::Name))
  104. return parseFailed("File not large enough to read part name");
  105. PartOffsets.push_back(PartOffset);
  106. dxbc::PartType PT =
  107. dxbc::parsePartType(Data.getBuffer().substr(PartOffset, 4));
  108. uint32_t PartDataStart = PartOffset + sizeof(dxbc::PartHeader);
  109. uint32_t PartSize;
  110. if (Error Err = readInteger(Data.getBuffer(),
  111. Data.getBufferStart() + PartOffset + 4,
  112. PartSize, "part size"))
  113. return Err;
  114. StringRef PartData = Data.getBuffer().substr(PartDataStart, PartSize);
  115. LastOffset = PartOffset + PartSize;
  116. switch (PT) {
  117. case dxbc::PartType::DXIL:
  118. if (Error Err = parseDXILHeader(PartData))
  119. return Err;
  120. break;
  121. case dxbc::PartType::SFI0:
  122. if (Error Err = parseShaderFlags(PartData))
  123. return Err;
  124. break;
  125. case dxbc::PartType::HASH:
  126. if (Error Err = parseHash(PartData))
  127. return Err;
  128. break;
  129. case dxbc::PartType::Unknown:
  130. break;
  131. }
  132. }
  133. return Error::success();
  134. }
  135. Expected<DXContainer> DXContainer::create(MemoryBufferRef Object) {
  136. DXContainer Container(Object);
  137. if (Error Err = Container.parseHeader())
  138. return std::move(Err);
  139. if (Error Err = Container.parsePartOffsets())
  140. return std::move(Err);
  141. return Container;
  142. }
  143. void DXContainer::PartIterator::updateIteratorImpl(const uint32_t Offset) {
  144. StringRef Buffer = Container.Data.getBuffer();
  145. const char *Current = Buffer.data() + Offset;
  146. // Offsets are validated during parsing, so all offsets in the container are
  147. // valid and contain enough readable data to read a header.
  148. cantFail(readStruct(Buffer, Current, IteratorState.Part));
  149. IteratorState.Data =
  150. StringRef(Current + sizeof(dxbc::PartHeader), IteratorState.Part.Size);
  151. IteratorState.Offset = Offset;
  152. }