123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- ///////////////////////////////////////////////////////////////////////////////
- // //
- // DxilPDB.cpp //
- // Copyright (C) Microsoft Corporation. All rights reserved. //
- // This file is distributed under the University of Illinois Open Source //
- // License. See LICENSE.TXT for details. //
- // //
- // Helpers to wrap debug information in a PDB container. //
- // //
- ///////////////////////////////////////////////////////////////////////////////
- //
- // This file contains code that helps creating our special PDB format. PDB
- // format contains streams at fixed locations. Outside of those fixed
- // locations, unless they are listed in the stream hash table, there is be no
- // way to know what the stream is. As far as normal PDB's are concerned, they
- // dont' really exist.
- //
- // For our purposes, we always put our data in one stream at a fixed index
- // defined below. The data is an ordinary DXIL container format, with parts
- // that are relevant for debugging.
- //
- #include "llvm/Support/raw_ostream.h"
- #include "llvm/ADT/ArrayRef.h"
- #include "llvm/ADT/SmallString.h"
- #include "llvm/Support/Endian.h"
- #include "dxc/DXIL/DxilPDB.h"
- #include "dxc/Support/WinIncludes.h"
- #include "dxc/Support/Global.h"
- #include "dxc/Support/FileIOHelper.h"
- #include "dxc/DxilContainer/DxilContainer.h"
- #include "dxc/dxcapi.h"
- #include "dxc/Support/dxcapi.impl.h"
- using namespace llvm;
- // MSF header
- static const char kMsfMagic[] = {'M', 'i', 'c', 'r', 'o', 's', 'o', 'f',
- 't', ' ', 'C', '/', 'C', '+', '+', ' ',
- 'M', 'S', 'F', ' ', '7', '.', '0', '0',
- '\r', '\n', '\x1a', 'D', 'S', '\0', '\0', '\0'};
- static const uint32_t kPdbStreamIndex = 1; // This is the fixed stream index where the PDB stream header is
- static const uint32_t kDataStreamIndex = 5; // This is the fixed stream index where we will store our custom data.
- static const uint32_t kMsfBlockSize = 512;
- // The superblock is overlaid at the beginning of the file (offset 0).
- // It starts with a magic header and is followed by information which
- // describes the layout of the file system.
- struct MSF_SuperBlock {
- char MagicBytes[sizeof(kMsfMagic)];
- // The file system is split into a variable number of fixed size elements.
- // These elements are referred to as blocks. The size of a block may vary
- // from system to system.
- support::ulittle32_t BlockSize;
- // The index of the free block map.
- support::ulittle32_t FreeBlockMapBlock;
- // This contains the number of blocks resident in the file system. In
- // practice, NumBlocks * BlockSize is equivalent to the size of the MSF
- // file.
- support::ulittle32_t NumBlocks;
- // This contains the number of bytes which make up the directory.
- support::ulittle32_t NumDirectoryBytes;
- // This field's purpose is not yet known.
- support::ulittle32_t Unknown1;
- // This contains the block # of the block map.
- support::ulittle32_t BlockMapAddr;
- };
- static_assert(sizeof(MSF_SuperBlock) <= kMsfBlockSize, "MSF Block too small.");
- // Calculate how many blocks are needed
- static uint32_t CalculateNumBlocks(uint32_t BlockSize, uint32_t Size) {
- return (Size / BlockSize) +
- ((Size % BlockSize) ? 1 : 0);
- }
- static HRESULT ReadAllBytes(IStream *pStream, void *pDst, size_t uSize) {
- ULONG uBytesRead = 0;
- IFR(pStream->Read(pDst, uSize, &uBytesRead));
- if (uBytesRead != uSize)
- return E_FAIL;
- return S_OK;
- }
- struct MSFWriter {
- struct Stream {
- ArrayRef<char> Data;
- unsigned NumBlocks = 0;
- };
- struct StreamLayout {
- MSF_SuperBlock SB;
- };
- int m_NumBlocks = 0;
- SmallVector<Stream, 8> m_Streams;
- static uint32_t GetNumBlocks(uint32_t Size) {
- return CalculateNumBlocks(kMsfBlockSize, Size);
- }
- uint32_t AddStream(ArrayRef<char> Data) {
- uint32_t ID = m_Streams.size();
- Stream S;
- S.Data = Data;
- S.NumBlocks = GetNumBlocks(Data.size());
- m_NumBlocks += S.NumBlocks;
- m_Streams.push_back(S);
- return ID;
- }
- uint32_t AddEmptyStream() {
- return AddStream({});
- }
- uint32_t CalculateDirectorySize() {
- uint32_t DirectorySizeInBytes = 0;
- DirectorySizeInBytes += sizeof(uint32_t);
- DirectorySizeInBytes += m_Streams.size() * 4;
- for (unsigned i = 0; i < m_Streams.size(); i++) {
- DirectorySizeInBytes += m_Streams[i].NumBlocks * 4;
- }
- return DirectorySizeInBytes;
- }
- MSF_SuperBlock CalculateSuperblock() {
- MSF_SuperBlock SB = {};
- memcpy(SB.MagicBytes, kMsfMagic, sizeof(kMsfMagic));
- SB.BlockSize = kMsfBlockSize;
- SB.NumDirectoryBytes = CalculateDirectorySize();
- SB.NumBlocks = 3 + m_NumBlocks + GetNumBlocks(SB.NumDirectoryBytes);
- SB.FreeBlockMapBlock = 1;
- SB.BlockMapAddr = 3;
- return SB;
- }
- struct BlockWriter {
- uint32_t BlocksWritten = 0;
- raw_ostream &OS;
- BlockWriter(raw_ostream &OS) : OS(OS) {}
- void WriteZeroPads(uint32_t Count) {
- for (unsigned i = 0; i < Count; i++)
- OS.write(0);
- }
- void WriteEmptyBlock() {
- BlocksWritten++;
- WriteZeroPads(kMsfBlockSize);
- }
- void WriteBlocks(uint32_t NumBlocks, const void *Data, uint32_t Size) {
- assert(NumBlocks >= GetNumBlocks(Size) && "Cannot fit data into the requested number of blocks!");
- uint32_t TotalSize = NumBlocks * kMsfBlockSize;
- OS.write(static_cast<char*>(const_cast<void *>(Data)), Size);
- WriteZeroPads(TotalSize - Size);
- BlocksWritten += NumBlocks;
- }
- void WriteUint32(uint32_t Value) {
- support::ulittle32_t ValueLE;
- ValueLE = Value;
- OS.write((char *)&ValueLE, sizeof(ValueLE));
- }
- };
- void WriteBlocks(raw_ostream &OS, ArrayRef<char> Data, uint32_t NumBlocks) {
- assert(NumBlocks >= GetNumBlocks(Data.size()) && "Cannot fit data into the requested number of blocks!");
- uint32_t TotalSize = NumBlocks * kMsfBlockSize;
- OS.write(Data.data(), Data.size());
- WriteZeroPadding(OS, TotalSize - Data.size());
- }
- void WriteZeroPadding(raw_ostream &OS, int Count) {
- for (int i = 0; i < Count; i++)
- OS.write(0);
- }
- static support::ulittle32_t MakeUint32LE(uint32_t Value) {
- support::ulittle32_t ValueLE;
- ValueLE = Value;
- return ValueLE;
- }
- void WriteToStream(raw_ostream &OS) {
- MSF_SuperBlock SB = CalculateSuperblock();
- const uint32_t NumDirectoryBlocks = GetNumBlocks(SB.NumDirectoryBytes);
- const uint32_t StreamDirectoryAddr = SB.BlockMapAddr;
- const uint32_t BlockAddrSize = NumDirectoryBlocks * sizeof(support::ulittle32_t);
- const uint32_t NumBlockAddrBlocks = GetNumBlocks(BlockAddrSize);
- const uint32_t StreamDirectoryStart = StreamDirectoryAddr + NumBlockAddrBlocks;
- const uint32_t StreamStart = StreamDirectoryStart + NumDirectoryBlocks;
- BlockWriter Writer(OS);
- Writer.WriteBlocks(1, &SB, sizeof(SB)); // Super Block
- Writer.WriteEmptyBlock(); // FPM 1
- Writer.WriteEmptyBlock(); // FPM 2
- // BlockAddr
- // This block contains a list of uint32's that point to the blocks that
- // make up the stream directory.
- {
- SmallVector<support::ulittle32_t, 4> BlockAddr;
- uint32_t Start = StreamDirectoryStart;
- for (unsigned i = 0; i < NumDirectoryBlocks; i++) {
- support::ulittle32_t V;
- V = Start++;
- BlockAddr.push_back(V);
- }
- assert(BlockAddrSize == sizeof(BlockAddr[0])*BlockAddr.size());
- Writer.WriteBlocks(NumBlockAddrBlocks, BlockAddr.data(), BlockAddrSize);
- }
- // Stream Directory. Describes where all the streams are
- // Looks like this:
- //
- {
- SmallVector<support::ulittle32_t, 32> StreamDirectoryData;
- StreamDirectoryData.push_back(MakeUint32LE(m_Streams.size()));
- for (unsigned i = 0; i < m_Streams.size(); i++) {
- StreamDirectoryData.push_back(MakeUint32LE(m_Streams[i].Data.size()));
- }
- uint32_t Start = StreamStart;
- for (unsigned i = 0; i < m_Streams.size(); i++) {
- auto &Stream = m_Streams[i];
- for (unsigned j = 0; j < Stream.NumBlocks; j++) {
- StreamDirectoryData.push_back(MakeUint32LE(Start++));
- }
- }
- Writer.WriteBlocks(NumDirectoryBlocks, StreamDirectoryData.data(), StreamDirectoryData.size()*sizeof(StreamDirectoryData[0]));
- }
- // Write the streams.
- {
- for (unsigned i = 0; i < m_Streams.size(); i++) {
- auto &Stream = m_Streams[i];
- Writer.WriteBlocks(Stream.NumBlocks, Stream.Data.data(), Stream.Data.size());
- }
- }
- }
- };
- enum class PdbStreamVersion : uint32_t {
- VC2 = 19941610,
- VC4 = 19950623,
- VC41 = 19950814,
- VC50 = 19960307,
- VC98 = 19970604,
- VC70Dep = 19990604,
- VC70 = 20000404,
- VC80 = 20030901,
- VC110 = 20091201,
- VC140 = 20140508,
- };
- struct PdbStreamHeader {
- support::ulittle32_t Version;
- support::ulittle32_t Signature;
- support::ulittle32_t Age;
- uint8_t UniqueId[16];
- };
- static_assert(sizeof(PdbStreamHeader) == 28, "PDB Header incorrect.");
- static
- SmallVector<char, 0> WritePdbStream(ArrayRef<BYTE> Hash) {
- PdbStreamHeader Header = {};
- Header.Version = (uint32_t)PdbStreamVersion::VC70;
- Header.Age = 1;
- Header.Signature = 0;
- DXASSERT_NOMSG(Hash.size() == sizeof(Header.UniqueId));
- memcpy(Header.UniqueId, Hash.data(), std::min(Hash.size(), sizeof(Header.UniqueId)));
- SmallVector<char, 0> Result;
- raw_svector_ostream OS(Result);
- auto WriteU32 = [&](uint32_t val) {
- support::ulittle32_t valLE;
- valLE = val;
- OS.write((char *)&valLE, sizeof(valLE));
- };
- OS.write((char *)&Header, 28);
- WriteU32(0); // String buffer size
- WriteU32(0); // Size
- WriteU32(1); // Capacity // Capacity is required to be 1.
- WriteU32(0); // Present count
- WriteU32(0); // Deleted count
- WriteU32(0); // Key
- WriteU32(0); // Value
- OS.flush();
- return Result;
- }
- HRESULT hlsl::pdb::WriteDxilPDB(IMalloc *pMalloc, IDxcBlob *pContainer, ArrayRef<BYTE> HashData, IDxcBlob **ppOutBlob) {
- return hlsl::pdb::WriteDxilPDB(pMalloc,
- llvm::ArrayRef<BYTE>((const BYTE *)pContainer->GetBufferPointer(), pContainer->GetBufferSize()),
- HashData, ppOutBlob);
- }
- HRESULT hlsl::pdb::WriteDxilPDB(IMalloc *pMalloc, llvm::ArrayRef<BYTE> ContainerData, llvm::ArrayRef<BYTE> HashData, IDxcBlob **ppOutBlob) {
- if (!hlsl::IsValidDxilContainer((const hlsl::DxilContainerHeader *)ContainerData.data(), ContainerData.size()))
- return E_FAIL;
- SmallVector<char, 0> PdbStream = WritePdbStream(HashData);
- MSFWriter Writer;
- Writer.AddEmptyStream(); // Old Directory
- Writer.AddStream(PdbStream); // PDB Header
- // Fixed streams
- Writer.AddEmptyStream(); // TPI
- Writer.AddEmptyStream(); // DBI
- Writer.AddEmptyStream(); // IPI
-
- Writer.AddStream(llvm::ArrayRef<char>((const char *)ContainerData.data(), ContainerData.size() )); // Actual data block
-
- CComPtr<hlsl::AbstractMemoryStream> pStream;
- IFR(hlsl::CreateMemoryStream(pMalloc, &pStream));
- raw_stream_ostream OS(pStream);
- Writer.WriteToStream(OS);
- OS.flush();
- IFR(pStream.QueryInterface(ppOutBlob));
- return S_OK;
- }
- struct PDBReader {
- IStream *m_pStream = nullptr;
- IMalloc *m_pMalloc = nullptr;
- UINT32 m_uOriginalOffset = 0;
- MSF_SuperBlock m_SB = {};
- HRESULT m_Status = S_OK;
- HRESULT SetPosition(INT32 sOffset) {
- LARGE_INTEGER Distance = {};
- Distance.QuadPart = m_uOriginalOffset + sOffset;
- ULARGE_INTEGER NewLocation = {};
- return m_pStream->Seek(Distance, STREAM_SEEK_SET, &NewLocation);
- }
- PDBReader(IMalloc *pMalloc, IStream *pStream) : m_pStream(pStream), m_pMalloc(pMalloc) {
- m_Status = ReadSuperblock(&m_SB);
- }
- // Reset the stream back to its original position, regardless of
- // we succeeded or failed.
- ~PDBReader() {
- SetPosition(0);
- }
- HRESULT GetStatus() { return m_Status; }
- HRESULT ReadSuperblock(MSF_SuperBlock *pSB) {
- IFR(ReadAllBytes(m_pStream, pSB, sizeof(*pSB)));
- if (memcmp(pSB->MagicBytes, kMsfMagic, sizeof(kMsfMagic)) != 0)
- return E_FAIL;
- return S_OK;
- }
- HRESULT ReadU32(UINT32 *pValue) {
- support::ulittle32_t ValueLE;
- IFR(ReadAllBytes(m_pStream, &ValueLE, sizeof(ValueLE)));
- *pValue = ValueLE;
- return S_OK;
- }
- HRESULT GoToBeginningOfBlock(UINT32 uBlock) {
- return SetPosition(uBlock * m_SB.BlockSize);
- }
- HRESULT OffsetByU32(int sCount) {
- LARGE_INTEGER Offset = {};
- ULARGE_INTEGER BytesMoved = {};
- Offset.QuadPart = sCount * sizeof(UINT32);
- return m_pStream->Seek(Offset, STREAM_SEEK_CUR, &BytesMoved);
- }
- HRESULT ReadWholeStream(uint32_t StreamIndex, IDxcBlob **ppData) {
- if (FAILED(m_Status)) return m_Status;
- UINT32 uNumDirectoryBlocks =
- CalculateNumBlocks(m_SB.BlockSize, m_SB.NumDirectoryBytes);
- // Load in the directory blocks
- llvm::SmallVector<uint32_t, 32> DirectoryBlocks;
- IFR(GoToBeginningOfBlock(m_SB.BlockMapAddr))
- for (unsigned i = 0; i < uNumDirectoryBlocks; i++) {
- UINT32 uBlock = 0;
- IFR(ReadU32(&uBlock));
- DirectoryBlocks.push_back(uBlock);
- }
- // Load Num streams
- UINT32 uNumStreams = 0;
- IFR(GoToBeginningOfBlock(DirectoryBlocks[0]));
- IFR(ReadU32(&uNumStreams));
- // If we don't have enough streams, then give up.
- if (uNumStreams <= StreamIndex)
- return E_FAIL;
- llvm::SmallVector<uint32_t, 6> StreamSizes;
- IFR(ReadU32ListFromBlocks(DirectoryBlocks, 1, uNumStreams, StreamSizes));
- UINT32 uOffsets = 0;
- for (unsigned i = 0; i < StreamIndex; i++) {
- UINT32 uNumBlocks = CalculateNumBlocks(m_SB.BlockSize, StreamSizes[i]);
- uOffsets += uNumBlocks;
- }
- llvm::SmallVector<uint32_t, 12> DataBlocks;
- IFR(ReadU32ListFromBlocks(DirectoryBlocks, 1 + uNumStreams + uOffsets,
- CalculateNumBlocks(m_SB.BlockSize, StreamSizes[StreamIndex]), DataBlocks));
- if (DataBlocks.size() == 0)
- return E_FAIL;
- IFR(GoToBeginningOfBlock(DataBlocks[0]));
- CComPtr<hlsl::AbstractMemoryStream> pResult;
- IFR(CreateMemoryStream(m_pMalloc, &pResult));
- std::vector<char> CopyBuffer;
- CopyBuffer.resize(m_SB.BlockSize);
- for (unsigned i = 0; i < DataBlocks.size(); i++) {
- IFR(GoToBeginningOfBlock(DataBlocks[i]));
- IFR(ReadAllBytes(m_pStream, CopyBuffer.data(), m_SB.BlockSize));
- ULONG uSizeWritten = 0;
- IFR(pResult->Write(CopyBuffer.data(), m_SB.BlockSize, &uSizeWritten));
- if (uSizeWritten != m_SB.BlockSize)
- return E_FAIL;
- }
- IFR(pResult.QueryInterface(ppData));
- return S_OK;
- }
- HRESULT ReadU32ListFromBlocks(ArrayRef<uint32_t> Blocks, UINT32 uOffsetByU32, UINT32 uNumU32, SmallVectorImpl<uint32_t> &Output) {
- if (Blocks.size() == 0) return E_FAIL;
- Output.clear();
- for (unsigned i = 0; i < uNumU32; i++) {
- UINT32 uOffsetInBytes = (uOffsetByU32+i) * sizeof(UINT32);
- UINT32 BlockIndex = uOffsetInBytes / m_SB.BlockSize;
- UINT32 ByteOffset = uOffsetInBytes % m_SB.BlockSize;
- UINT32 uBlock = Blocks[BlockIndex];
- IFR(GoToBeginningOfBlock(uBlock));
- IFR(OffsetByU32(ByteOffset / sizeof(UINT32)));
- UINT32 uData = 0;
- IFR(ReadU32(&uData));
- Output.push_back(uData);
- }
- return S_OK;
- }
- };
- HRESULT hlsl::pdb::LoadDataFromStream(IMalloc *pMalloc, IStream *pIStream, IDxcBlob **ppHash, IDxcBlob **ppContainer) {
- PDBReader Reader(pMalloc, pIStream);
- if (ppHash) {
- CComPtr<IDxcBlob> pPdbStream;
- IFR(Reader.ReadWholeStream(kPdbStreamIndex, &pPdbStream));
- if (pPdbStream->GetBufferSize() < sizeof(PdbStreamHeader))
- return E_FAIL;
- PdbStreamHeader PdbHeader = {};
- memcpy(&PdbHeader, pPdbStream->GetBufferPointer(), sizeof(PdbHeader));
- CComPtr<hlsl::AbstractMemoryStream> pHash;
- IFR(CreateMemoryStream(pMalloc, &pHash));
- ULONG uBytesWritten = 0;
- IFR(pHash->Write(PdbHeader.UniqueId, sizeof(PdbHeader.UniqueId), &uBytesWritten));
- if (uBytesWritten != sizeof(PdbHeader.UniqueId))
- return E_FAIL;
- IFR(pHash.QueryInterface(ppHash));
- }
- CComPtr<IDxcBlob> pContainer;
- IFR(Reader.ReadWholeStream(kDataStreamIndex, &pContainer));
- if (!hlsl::IsValidDxilContainer((hlsl::DxilContainerHeader *)pContainer->GetBufferPointer(), pContainer->GetBufferSize()))
- return E_FAIL;
- *ppContainer = pContainer.Detach();
- return S_OK;
- }
- HRESULT hlsl::pdb::LoadDataFromStream(IMalloc *pMalloc, IStream *pIStream, IDxcBlob **ppContainer) {
- return LoadDataFromStream(pMalloc, pIStream, nullptr, ppContainer);
- }
|