diff --git a/llvm/include/llvm/BinaryFormat/DXContainer.h b/llvm/include/llvm/BinaryFormat/DXContainer.h new file mode 100644 index 0000000000000..f9ec9fe45a0cb --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/DXContainer.h @@ -0,0 +1,91 @@ +//===-- llvm/BinaryFormat/DXContainer.h - The DXBC file format --*- C++/-*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines manifest constants for the DXContainer object file format. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_BINARYFORMAT_DXCONTAINER_H +#define LLVM_BINARYFORMAT_DXCONTAINER_H + +#include "llvm/Support/SwapByteOrder.h" + +#include + +namespace llvm { + +// The DXContainer file format is arranged as a header and "parts". Semantically +// parts are similar to sections in other object file formats. The File format +// structure is roughly: + +// ┌────────────────────────────────┐ +// │ Header │ +// ├────────────────────────────────┤ +// │ Part │ +// ├────────────────────────────────┤ +// │ Part │ +// ├────────────────────────────────┤ +// │ ... │ +// └────────────────────────────────┘ + +namespace dxbc { + +struct Hash { + uint8_t Digest[16]; +}; + +enum class HashFlags : uint32_t { + None = 0, // No flags defined. + IncludesSource = 1, // This flag indicates that the shader hash was computed + // taking into account source information (-Zss) +}; + +struct ShaderHash { + uint32_t Flags; // DxilShaderHashFlags + uint8_t Digest[16]; + + void byteSwap() { sys::swapByteOrder(Flags); } +}; + +struct ContainerVersion { + uint16_t Major; + uint16_t Minor; + + void byteSwap() { + sys::swapByteOrder(Major); + sys::swapByteOrder(Minor); + } +}; + +struct Header { + uint8_t Magic[4]; // "DXBC" + Hash Hash; + ContainerVersion Version; + uint32_t FileSize; + uint32_t PartCount; + + void byteSwap() { + Version.byteSwap(); + sys::swapByteOrder(FileSize); + sys::swapByteOrder(PartCount); + } + // Structure is followed by part offsets: uint32_t PartOffset[PartCount]; + // The offset is to a PartHeader, which is followed by the Part Data. +}; + +/// Use this type to describe the size and type of a DXIL container part. +struct PartHeader { + uint8_t Name[4]; + uint32_t Size; + // Structure is followed directly by part data: uint8_t PartData[PartSize]. +}; + +} // namespace dxbc +} // namespace llvm + +#endif // LLVM_BINARYFORMAT_DXCONTAINER_H diff --git a/llvm/include/llvm/Object/DXContainer.h b/llvm/include/llvm/Object/DXContainer.h new file mode 100644 index 0000000000000..69a5e28539d6c --- /dev/null +++ b/llvm/include/llvm/Object/DXContainer.h @@ -0,0 +1,44 @@ +//===- DXContainer.h - DXContainer file implementation ----------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file declares the DXContainerFile class, which implements the ObjectFile +// interface for DXContainer files. +// +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OBJECT_DXCONTAINER_H +#define LLVM_OBJECT_DXCONTAINER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/DXContainer.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBufferRef.h" + +namespace llvm { +namespace object { +class DXContainer { +private: + DXContainer(MemoryBufferRef O); + + MemoryBufferRef Data; + dxbc::Header Header; + + Error parseHeader(); + +public: + StringRef getData() const { return Data.getBuffer(); } + static Expected create(MemoryBufferRef Object); + + const dxbc::Header &getHeader() const { return Header; } +}; + +} // namespace object +} // namespace llvm + +#endif // LLVM_OBJECT_DXCONTAINERFILE_H diff --git a/llvm/lib/Object/CMakeLists.txt b/llvm/lib/Object/CMakeLists.txt index 082521049439a..ba612e3d95e9b 100644 --- a/llvm/lib/Object/CMakeLists.txt +++ b/llvm/lib/Object/CMakeLists.txt @@ -6,6 +6,7 @@ add_llvm_component_library(LLVMObject COFFModuleDefinition.cpp COFFObjectFile.cpp Decompressor.cpp + DXContainer.cpp ELF.cpp ELFObjectFile.cpp Error.cpp diff --git a/llvm/lib/Object/DXContainer.cpp b/llvm/lib/Object/DXContainer.cpp new file mode 100644 index 0000000000000..05d7bc6264a4b --- /dev/null +++ b/llvm/lib/Object/DXContainer.cpp @@ -0,0 +1,44 @@ +//===- 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" + +using namespace llvm; +using namespace llvm::object; + +static Error parseFailed(const Twine &Msg) { + return make_error(Msg.str(), object_error::parse_failed); +} + +template +static Error readStruct(StringRef Buffer, const char *P, T &Struct) { + // Don't read before the beginning or past the end of the file + if (P < Buffer.begin() || P + sizeof(T) > Buffer.end()) + return parseFailed("Reading structure out of file bounds"); + + memcpy(&Struct, P, sizeof(T)); + // DXContainer is always BigEndian + if (sys::IsBigEndianHost) + Struct.byteSwap(); + return Error::success(); +} + +DXContainer::DXContainer(MemoryBufferRef O) : Data(O) {} + +Error DXContainer::parseHeader() { + return readStruct(Data.getBuffer(), Data.getBuffer().data(), Header); +} + +Expected DXContainer::create(MemoryBufferRef Object) { + DXContainer Container(Object); + if (Error Err = Container.parseHeader()) + return Err; + return Container; +} diff --git a/llvm/unittests/Object/CMakeLists.txt b/llvm/unittests/Object/CMakeLists.txt index ea7df2246b4df..d30125d862eb7 100644 --- a/llvm/unittests/Object/CMakeLists.txt +++ b/llvm/unittests/Object/CMakeLists.txt @@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(ObjectTests ArchiveTest.cpp + DXContainerTest.cpp ELFObjectFileTest.cpp ELFTypesTest.cpp ELFTest.cpp diff --git a/llvm/unittests/Object/DXContainerTest.cpp b/llvm/unittests/Object/DXContainerTest.cpp new file mode 100644 index 0000000000000..846176a089fbc --- /dev/null +++ b/llvm/unittests/Object/DXContainerTest.cpp @@ -0,0 +1,43 @@ +//===- DXContainerTest.cpp - Tests for DXContainerFile --------------------===// +// +// 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/ADT/StringRef.h" +#include "llvm/BinaryFormat/Magic.h" +#include "llvm/Support/MemoryBufferRef.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::object; + +template MemoryBufferRef getMemoryBuffer(uint8_t Data[X]) { + StringRef Obj(reinterpret_cast(&Data[0]), X); + return MemoryBufferRef(Obj, ""); +} + +TEST(DXCFile, ParseHeaderErrors) { + uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43}; + EXPECT_THAT_EXPECTED( + DXContainer::create(getMemoryBuffer<4>(Buffer)), + FailedWithMessage("Reading structure out of file bounds")); +} + +TEST(DXCFile, ParseHeader) { + uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x70, 0x0D, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00}; + DXContainer C = + llvm::cantFail(DXContainer::create(getMemoryBuffer<32>(Buffer))); + EXPECT_TRUE(memcmp(C.getHeader().Magic, "DXBC", 4) == 0); + EXPECT_TRUE(memcmp(C.getHeader().Hash.Digest, + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0); + EXPECT_EQ(C.getHeader().Version.Major, 1u); + EXPECT_EQ(C.getHeader().Version.Minor, 0u); +}