DxilPDB.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // DxilPDB.cpp //
  4. // Copyright (C) Microsoft Corporation. All rights reserved. //
  5. // This file is distributed under the University of Illinois Open Source //
  6. // License. See LICENSE.TXT for details. //
  7. // //
  8. // Helpers to wrap debug information in a PDB container. //
  9. // //
  10. ///////////////////////////////////////////////////////////////////////////////
  11. //
  12. // This file contains code that helps creating our special PDB format. PDB
  13. // format contains streams at fixed locations. Outside of those fixed
  14. // locations, unless they are listed in the stream hash table, there is be no
  15. // way to know what the stream is. As far as normal PDB's are concerned, they
  16. // dont' really exist.
  17. //
  18. // For our purposes, we always put our data in one stream at a fixed index
  19. // defined below. The data is an ordinary DXIL container format, with parts
  20. // that are relevant for debugging.
  21. //
  22. #include "llvm/Support/raw_ostream.h"
  23. #include "llvm/ADT/ArrayRef.h"
  24. #include "llvm/ADT/SmallString.h"
  25. #include "llvm/Support/Endian.h"
  26. #include "dxc/DXIL/DxilPDB.h"
  27. #include "dxc/Support/WinIncludes.h"
  28. #include "dxc/Support/Global.h"
  29. #include "dxc/Support/FileIOHelper.h"
  30. #include "dxc/DxilContainer/DxilContainer.h"
  31. #include "dxc/dxcapi.h"
  32. #include "dxc/Support/dxcapi.impl.h"
  33. using namespace llvm;
  34. // MSF header
  35. static const char kMsfMagic[] = {'M', 'i', 'c', 'r', 'o', 's', 'o', 'f',
  36. 't', ' ', 'C', '/', 'C', '+', '+', ' ',
  37. 'M', 'S', 'F', ' ', '7', '.', '0', '0',
  38. '\r', '\n', '\x1a', 'D', 'S', '\0', '\0', '\0'};
  39. static const uint32_t kPdbStreamIndex = 1; // This is the fixed stream index where the PDB stream header is
  40. static const uint32_t kDataStreamIndex = 5; // This is the fixed stream index where we will store our custom data.
  41. static const uint32_t kMsfBlockSize = 512;
  42. // The superblock is overlaid at the beginning of the file (offset 0).
  43. // It starts with a magic header and is followed by information which
  44. // describes the layout of the file system.
  45. struct MSF_SuperBlock {
  46. char MagicBytes[sizeof(kMsfMagic)];
  47. // The file system is split into a variable number of fixed size elements.
  48. // These elements are referred to as blocks. The size of a block may vary
  49. // from system to system.
  50. support::ulittle32_t BlockSize;
  51. // The index of the free block map.
  52. support::ulittle32_t FreeBlockMapBlock;
  53. // This contains the number of blocks resident in the file system. In
  54. // practice, NumBlocks * BlockSize is equivalent to the size of the MSF
  55. // file.
  56. support::ulittle32_t NumBlocks;
  57. // This contains the number of bytes which make up the directory.
  58. support::ulittle32_t NumDirectoryBytes;
  59. // This field's purpose is not yet known.
  60. support::ulittle32_t Unknown1;
  61. // This contains the block # of the block map.
  62. support::ulittle32_t BlockMapAddr;
  63. };
  64. static_assert(sizeof(MSF_SuperBlock) <= kMsfBlockSize, "MSF Block too small.");
  65. // Calculate how many blocks are needed
  66. static uint32_t CalculateNumBlocks(uint32_t BlockSize, uint32_t Size) {
  67. return (Size / BlockSize) +
  68. ((Size % BlockSize) ? 1 : 0);
  69. }
  70. static HRESULT ReadAllBytes(IStream *pStream, void *pDst, size_t uSize) {
  71. ULONG uBytesRead = 0;
  72. IFR(pStream->Read(pDst, uSize, &uBytesRead));
  73. if (uBytesRead != uSize)
  74. return E_FAIL;
  75. return S_OK;
  76. }
  77. struct MSFWriter {
  78. struct Stream {
  79. ArrayRef<char> Data;
  80. unsigned NumBlocks = 0;
  81. };
  82. struct StreamLayout {
  83. MSF_SuperBlock SB;
  84. };
  85. int m_NumBlocks = 0;
  86. SmallVector<Stream, 8> m_Streams;
  87. static uint32_t GetNumBlocks(uint32_t Size) {
  88. return CalculateNumBlocks(kMsfBlockSize, Size);
  89. }
  90. uint32_t AddStream(ArrayRef<char> Data) {
  91. uint32_t ID = m_Streams.size();
  92. Stream S;
  93. S.Data = Data;
  94. S.NumBlocks = GetNumBlocks(Data.size());
  95. m_NumBlocks += S.NumBlocks;
  96. m_Streams.push_back(S);
  97. return ID;
  98. }
  99. uint32_t AddEmptyStream() {
  100. return AddStream({});
  101. }
  102. uint32_t CalculateDirectorySize() {
  103. uint32_t DirectorySizeInBytes = 0;
  104. DirectorySizeInBytes += sizeof(uint32_t);
  105. DirectorySizeInBytes += m_Streams.size() * 4;
  106. for (unsigned i = 0; i < m_Streams.size(); i++) {
  107. DirectorySizeInBytes += m_Streams[i].NumBlocks * 4;
  108. }
  109. return DirectorySizeInBytes;
  110. }
  111. MSF_SuperBlock CalculateSuperblock() {
  112. MSF_SuperBlock SB = {};
  113. memcpy(SB.MagicBytes, kMsfMagic, sizeof(kMsfMagic));
  114. SB.BlockSize = kMsfBlockSize;
  115. SB.NumDirectoryBytes = CalculateDirectorySize();
  116. SB.NumBlocks = 3 + m_NumBlocks + GetNumBlocks(SB.NumDirectoryBytes);
  117. SB.FreeBlockMapBlock = 1;
  118. SB.BlockMapAddr = 3;
  119. return SB;
  120. }
  121. struct BlockWriter {
  122. uint32_t BlocksWritten = 0;
  123. raw_ostream &OS;
  124. BlockWriter(raw_ostream &OS) : OS(OS) {}
  125. void WriteZeroPads(uint32_t Count) {
  126. for (unsigned i = 0; i < Count; i++)
  127. OS.write(0);
  128. }
  129. void WriteEmptyBlock() {
  130. BlocksWritten++;
  131. WriteZeroPads(kMsfBlockSize);
  132. }
  133. void WriteBlocks(uint32_t NumBlocks, const void *Data, uint32_t Size) {
  134. assert(NumBlocks >= GetNumBlocks(Size) && "Cannot fit data into the requested number of blocks!");
  135. uint32_t TotalSize = NumBlocks * kMsfBlockSize;
  136. OS.write(static_cast<char*>(const_cast<void *>(Data)), Size);
  137. WriteZeroPads(TotalSize - Size);
  138. BlocksWritten += NumBlocks;
  139. }
  140. void WriteUint32(uint32_t Value) {
  141. support::ulittle32_t ValueLE;
  142. ValueLE = Value;
  143. OS.write((char *)&ValueLE, sizeof(ValueLE));
  144. }
  145. };
  146. void WriteBlocks(raw_ostream &OS, ArrayRef<char> Data, uint32_t NumBlocks) {
  147. assert(NumBlocks >= GetNumBlocks(Data.size()) && "Cannot fit data into the requested number of blocks!");
  148. uint32_t TotalSize = NumBlocks * kMsfBlockSize;
  149. OS.write(Data.data(), Data.size());
  150. WriteZeroPadding(OS, TotalSize - Data.size());
  151. }
  152. void WriteZeroPadding(raw_ostream &OS, int Count) {
  153. for (int i = 0; i < Count; i++)
  154. OS.write(0);
  155. }
  156. static support::ulittle32_t MakeUint32LE(uint32_t Value) {
  157. support::ulittle32_t ValueLE;
  158. ValueLE = Value;
  159. return ValueLE;
  160. }
  161. void WriteToStream(raw_ostream &OS) {
  162. MSF_SuperBlock SB = CalculateSuperblock();
  163. const uint32_t NumDirectoryBlocks = GetNumBlocks(SB.NumDirectoryBytes);
  164. const uint32_t StreamDirectoryAddr = SB.BlockMapAddr;
  165. const uint32_t BlockAddrSize = NumDirectoryBlocks * sizeof(support::ulittle32_t);
  166. const uint32_t NumBlockAddrBlocks = GetNumBlocks(BlockAddrSize);
  167. const uint32_t StreamDirectoryStart = StreamDirectoryAddr + NumBlockAddrBlocks;
  168. const uint32_t StreamStart = StreamDirectoryStart + NumDirectoryBlocks;
  169. BlockWriter Writer(OS);
  170. Writer.WriteBlocks(1, &SB, sizeof(SB)); // Super Block
  171. Writer.WriteEmptyBlock(); // FPM 1
  172. Writer.WriteEmptyBlock(); // FPM 2
  173. // BlockAddr
  174. // This block contains a list of uint32's that point to the blocks that
  175. // make up the stream directory.
  176. {
  177. SmallVector<support::ulittle32_t, 4> BlockAddr;
  178. uint32_t Start = StreamDirectoryStart;
  179. for (unsigned i = 0; i < NumDirectoryBlocks; i++) {
  180. support::ulittle32_t V;
  181. V = Start++;
  182. BlockAddr.push_back(V);
  183. }
  184. assert(BlockAddrSize == sizeof(BlockAddr[0])*BlockAddr.size());
  185. Writer.WriteBlocks(NumBlockAddrBlocks, BlockAddr.data(), BlockAddrSize);
  186. }
  187. // Stream Directory. Describes where all the streams are
  188. // Looks like this:
  189. //
  190. {
  191. SmallVector<support::ulittle32_t, 32> StreamDirectoryData;
  192. StreamDirectoryData.push_back(MakeUint32LE(m_Streams.size()));
  193. for (unsigned i = 0; i < m_Streams.size(); i++) {
  194. StreamDirectoryData.push_back(MakeUint32LE(m_Streams[i].Data.size()));
  195. }
  196. uint32_t Start = StreamStart;
  197. for (unsigned i = 0; i < m_Streams.size(); i++) {
  198. auto &Stream = m_Streams[i];
  199. for (unsigned j = 0; j < Stream.NumBlocks; j++) {
  200. StreamDirectoryData.push_back(MakeUint32LE(Start++));
  201. }
  202. }
  203. Writer.WriteBlocks(NumDirectoryBlocks, StreamDirectoryData.data(), StreamDirectoryData.size()*sizeof(StreamDirectoryData[0]));
  204. }
  205. // Write the streams.
  206. {
  207. for (unsigned i = 0; i < m_Streams.size(); i++) {
  208. auto &Stream = m_Streams[i];
  209. Writer.WriteBlocks(Stream.NumBlocks, Stream.Data.data(), Stream.Data.size());
  210. }
  211. }
  212. }
  213. };
  214. enum class PdbStreamVersion : uint32_t {
  215. VC2 = 19941610,
  216. VC4 = 19950623,
  217. VC41 = 19950814,
  218. VC50 = 19960307,
  219. VC98 = 19970604,
  220. VC70Dep = 19990604,
  221. VC70 = 20000404,
  222. VC80 = 20030901,
  223. VC110 = 20091201,
  224. VC140 = 20140508,
  225. };
  226. struct PdbStreamHeader {
  227. support::ulittle32_t Version;
  228. support::ulittle32_t Signature;
  229. support::ulittle32_t Age;
  230. uint8_t UniqueId[16];
  231. };
  232. static_assert(sizeof(PdbStreamHeader) == 28, "PDB Header incorrect.");
  233. static
  234. SmallVector<char, 0> WritePdbStream(ArrayRef<BYTE> Hash) {
  235. PdbStreamHeader Header = {};
  236. Header.Version = (uint32_t)PdbStreamVersion::VC70;
  237. Header.Age = 1;
  238. Header.Signature = 0;
  239. DXASSERT_NOMSG(Hash.size() == sizeof(Header.UniqueId));
  240. memcpy(Header.UniqueId, Hash.data(), std::min(Hash.size(), sizeof(Header.UniqueId)));
  241. SmallVector<char, 0> Result;
  242. raw_svector_ostream OS(Result);
  243. auto WriteU32 = [&](uint32_t val) {
  244. support::ulittle32_t valLE;
  245. valLE = val;
  246. OS.write((char *)&valLE, sizeof(valLE));
  247. };
  248. OS.write((char *)&Header, 28);
  249. WriteU32(0); // String buffer size
  250. WriteU32(0); // Size
  251. WriteU32(1); // Capacity // Capacity is required to be 1.
  252. WriteU32(0); // Present count
  253. WriteU32(0); // Deleted count
  254. WriteU32(0); // Key
  255. WriteU32(0); // Value
  256. OS.flush();
  257. return Result;
  258. }
  259. HRESULT hlsl::pdb::WriteDxilPDB(IMalloc *pMalloc, IDxcBlob *pContainer, ArrayRef<BYTE> HashData, IDxcBlob **ppOutBlob) {
  260. return hlsl::pdb::WriteDxilPDB(pMalloc,
  261. llvm::ArrayRef<BYTE>((const BYTE *)pContainer->GetBufferPointer(), pContainer->GetBufferSize()),
  262. HashData, ppOutBlob);
  263. }
  264. HRESULT hlsl::pdb::WriteDxilPDB(IMalloc *pMalloc, llvm::ArrayRef<BYTE> ContainerData, llvm::ArrayRef<BYTE> HashData, IDxcBlob **ppOutBlob) {
  265. if (!hlsl::IsValidDxilContainer((const hlsl::DxilContainerHeader *)ContainerData.data(), ContainerData.size()))
  266. return E_FAIL;
  267. SmallVector<char, 0> PdbStream = WritePdbStream(HashData);
  268. MSFWriter Writer;
  269. Writer.AddEmptyStream(); // Old Directory
  270. Writer.AddStream(PdbStream); // PDB Header
  271. // Fixed streams
  272. Writer.AddEmptyStream(); // TPI
  273. Writer.AddEmptyStream(); // DBI
  274. Writer.AddEmptyStream(); // IPI
  275. Writer.AddStream(llvm::ArrayRef<char>((const char *)ContainerData.data(), ContainerData.size() )); // Actual data block
  276. CComPtr<hlsl::AbstractMemoryStream> pStream;
  277. IFR(hlsl::CreateMemoryStream(pMalloc, &pStream));
  278. raw_stream_ostream OS(pStream);
  279. Writer.WriteToStream(OS);
  280. OS.flush();
  281. IFR(pStream.QueryInterface(ppOutBlob));
  282. return S_OK;
  283. }
  284. struct PDBReader {
  285. IStream *m_pStream = nullptr;
  286. IMalloc *m_pMalloc = nullptr;
  287. UINT32 m_uOriginalOffset = 0;
  288. MSF_SuperBlock m_SB = {};
  289. HRESULT m_Status = S_OK;
  290. HRESULT SetPosition(INT32 sOffset) {
  291. LARGE_INTEGER Distance = {};
  292. Distance.QuadPart = m_uOriginalOffset + sOffset;
  293. ULARGE_INTEGER NewLocation = {};
  294. return m_pStream->Seek(Distance, STREAM_SEEK_SET, &NewLocation);
  295. }
  296. PDBReader(IMalloc *pMalloc, IStream *pStream) : m_pStream(pStream), m_pMalloc(pMalloc) {
  297. m_Status = ReadSuperblock(&m_SB);
  298. }
  299. // Reset the stream back to its original position, regardless of
  300. // we succeeded or failed.
  301. ~PDBReader() {
  302. SetPosition(0);
  303. }
  304. HRESULT GetStatus() { return m_Status; }
  305. HRESULT ReadSuperblock(MSF_SuperBlock *pSB) {
  306. IFR(ReadAllBytes(m_pStream, pSB, sizeof(*pSB)));
  307. if (memcmp(pSB->MagicBytes, kMsfMagic, sizeof(kMsfMagic)) != 0)
  308. return E_FAIL;
  309. return S_OK;
  310. }
  311. HRESULT ReadU32(UINT32 *pValue) {
  312. support::ulittle32_t ValueLE;
  313. IFR(ReadAllBytes(m_pStream, &ValueLE, sizeof(ValueLE)));
  314. *pValue = ValueLE;
  315. return S_OK;
  316. }
  317. HRESULT GoToBeginningOfBlock(UINT32 uBlock) {
  318. return SetPosition(uBlock * m_SB.BlockSize);
  319. }
  320. HRESULT OffsetByU32(int sCount) {
  321. LARGE_INTEGER Offset = {};
  322. ULARGE_INTEGER BytesMoved = {};
  323. Offset.QuadPart = sCount * sizeof(UINT32);
  324. return m_pStream->Seek(Offset, STREAM_SEEK_CUR, &BytesMoved);
  325. }
  326. HRESULT ReadWholeStream(uint32_t StreamIndex, IDxcBlob **ppData) {
  327. if (FAILED(m_Status)) return m_Status;
  328. UINT32 uNumDirectoryBlocks =
  329. CalculateNumBlocks(m_SB.BlockSize, m_SB.NumDirectoryBytes);
  330. // Load in the directory blocks
  331. llvm::SmallVector<uint32_t, 32> DirectoryBlocks;
  332. IFR(GoToBeginningOfBlock(m_SB.BlockMapAddr))
  333. for (unsigned i = 0; i < uNumDirectoryBlocks; i++) {
  334. UINT32 uBlock = 0;
  335. IFR(ReadU32(&uBlock));
  336. DirectoryBlocks.push_back(uBlock);
  337. }
  338. // Load Num streams
  339. UINT32 uNumStreams = 0;
  340. IFR(GoToBeginningOfBlock(DirectoryBlocks[0]));
  341. IFR(ReadU32(&uNumStreams));
  342. // If we don't have enough streams, then give up.
  343. if (uNumStreams <= StreamIndex)
  344. return E_FAIL;
  345. llvm::SmallVector<uint32_t, 6> StreamSizes;
  346. IFR(ReadU32ListFromBlocks(DirectoryBlocks, 1, uNumStreams, StreamSizes));
  347. UINT32 uOffsets = 0;
  348. for (unsigned i = 0; i < StreamIndex; i++) {
  349. UINT32 uNumBlocks = CalculateNumBlocks(m_SB.BlockSize, StreamSizes[i]);
  350. uOffsets += uNumBlocks;
  351. }
  352. llvm::SmallVector<uint32_t, 12> DataBlocks;
  353. IFR(ReadU32ListFromBlocks(DirectoryBlocks, 1 + uNumStreams + uOffsets,
  354. CalculateNumBlocks(m_SB.BlockSize, StreamSizes[StreamIndex]), DataBlocks));
  355. if (DataBlocks.size() == 0)
  356. return E_FAIL;
  357. IFR(GoToBeginningOfBlock(DataBlocks[0]));
  358. CComPtr<hlsl::AbstractMemoryStream> pResult;
  359. IFR(CreateMemoryStream(m_pMalloc, &pResult));
  360. std::vector<char> CopyBuffer;
  361. CopyBuffer.resize(m_SB.BlockSize);
  362. for (unsigned i = 0; i < DataBlocks.size(); i++) {
  363. IFR(GoToBeginningOfBlock(DataBlocks[i]));
  364. IFR(ReadAllBytes(m_pStream, CopyBuffer.data(), m_SB.BlockSize));
  365. ULONG uSizeWritten = 0;
  366. IFR(pResult->Write(CopyBuffer.data(), m_SB.BlockSize, &uSizeWritten));
  367. if (uSizeWritten != m_SB.BlockSize)
  368. return E_FAIL;
  369. }
  370. IFR(pResult.QueryInterface(ppData));
  371. return S_OK;
  372. }
  373. HRESULT ReadU32ListFromBlocks(ArrayRef<uint32_t> Blocks, UINT32 uOffsetByU32, UINT32 uNumU32, SmallVectorImpl<uint32_t> &Output) {
  374. if (Blocks.size() == 0) return E_FAIL;
  375. Output.clear();
  376. for (unsigned i = 0; i < uNumU32; i++) {
  377. UINT32 uOffsetInBytes = (uOffsetByU32+i) * sizeof(UINT32);
  378. UINT32 BlockIndex = uOffsetInBytes / m_SB.BlockSize;
  379. UINT32 ByteOffset = uOffsetInBytes % m_SB.BlockSize;
  380. UINT32 uBlock = Blocks[BlockIndex];
  381. IFR(GoToBeginningOfBlock(uBlock));
  382. IFR(OffsetByU32(ByteOffset / sizeof(UINT32)));
  383. UINT32 uData = 0;
  384. IFR(ReadU32(&uData));
  385. Output.push_back(uData);
  386. }
  387. return S_OK;
  388. }
  389. };
  390. HRESULT hlsl::pdb::LoadDataFromStream(IMalloc *pMalloc, IStream *pIStream, IDxcBlob **ppHash, IDxcBlob **ppContainer) {
  391. PDBReader Reader(pMalloc, pIStream);
  392. if (ppHash) {
  393. CComPtr<IDxcBlob> pPdbStream;
  394. IFR(Reader.ReadWholeStream(kPdbStreamIndex, &pPdbStream));
  395. if (pPdbStream->GetBufferSize() < sizeof(PdbStreamHeader))
  396. return E_FAIL;
  397. PdbStreamHeader PdbHeader = {};
  398. memcpy(&PdbHeader, pPdbStream->GetBufferPointer(), sizeof(PdbHeader));
  399. CComPtr<hlsl::AbstractMemoryStream> pHash;
  400. IFR(CreateMemoryStream(pMalloc, &pHash));
  401. ULONG uBytesWritten = 0;
  402. IFR(pHash->Write(PdbHeader.UniqueId, sizeof(PdbHeader.UniqueId), &uBytesWritten));
  403. if (uBytesWritten != sizeof(PdbHeader.UniqueId))
  404. return E_FAIL;
  405. IFR(pHash.QueryInterface(ppHash));
  406. }
  407. CComPtr<IDxcBlob> pContainer;
  408. IFR(Reader.ReadWholeStream(kDataStreamIndex, &pContainer));
  409. if (!hlsl::IsValidDxilContainer((hlsl::DxilContainerHeader *)pContainer->GetBufferPointer(), pContainer->GetBufferSize()))
  410. return E_FAIL;
  411. *ppContainer = pContainer.Detach();
  412. return S_OK;
  413. }
  414. HRESULT hlsl::pdb::LoadDataFromStream(IMalloc *pMalloc, IStream *pIStream, IDxcBlob **ppContainer) {
  415. return LoadDataFromStream(pMalloc, pIStream, nullptr, ppContainer);
  416. }