MSFBuilder.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. //===- MSFBuilder.cpp -----------------------------------------------------===//
  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/DebugInfo/MSF/MSFBuilder.h"
  9. #include "llvm/ADT/ArrayRef.h"
  10. #include "llvm/DebugInfo/MSF/MSFError.h"
  11. #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
  12. #include "llvm/Support/BinaryByteStream.h"
  13. #include "llvm/Support/BinaryStreamWriter.h"
  14. #include "llvm/Support/Endian.h"
  15. #include "llvm/Support/Error.h"
  16. #include "llvm/Support/FileOutputBuffer.h"
  17. #include "llvm/Support/FormatVariadic.h"
  18. #include <algorithm>
  19. #include <cassert>
  20. #include <cstdint>
  21. #include <cstring>
  22. #include <memory>
  23. #include <utility>
  24. #include <vector>
  25. using namespace llvm;
  26. using namespace llvm::msf;
  27. using namespace llvm::support;
  28. static const uint32_t kSuperBlockBlock = 0;
  29. static const uint32_t kFreePageMap0Block = 1;
  30. static const uint32_t kFreePageMap1Block = 2;
  31. static const uint32_t kNumReservedPages = 3;
  32. static const uint32_t kDefaultFreePageMap = kFreePageMap1Block;
  33. static const uint32_t kDefaultBlockMapAddr = kNumReservedPages;
  34. MSFBuilder::MSFBuilder(uint32_t BlockSize, uint32_t MinBlockCount, bool CanGrow,
  35. BumpPtrAllocator &Allocator)
  36. : Allocator(Allocator), IsGrowable(CanGrow),
  37. FreePageMap(kDefaultFreePageMap), BlockSize(BlockSize),
  38. BlockMapAddr(kDefaultBlockMapAddr), FreeBlocks(MinBlockCount, true) {
  39. FreeBlocks[kSuperBlockBlock] = false;
  40. FreeBlocks[kFreePageMap0Block] = false;
  41. FreeBlocks[kFreePageMap1Block] = false;
  42. FreeBlocks[BlockMapAddr] = false;
  43. }
  44. Expected<MSFBuilder> MSFBuilder::create(BumpPtrAllocator &Allocator,
  45. uint32_t BlockSize,
  46. uint32_t MinBlockCount, bool CanGrow) {
  47. if (!isValidBlockSize(BlockSize))
  48. return make_error<MSFError>(msf_error_code::invalid_format,
  49. "The requested block size is unsupported");
  50. return MSFBuilder(BlockSize,
  51. std::max(MinBlockCount, msf::getMinimumBlockCount()),
  52. CanGrow, Allocator);
  53. }
  54. Error MSFBuilder::setBlockMapAddr(uint32_t Addr) {
  55. if (Addr == BlockMapAddr)
  56. return Error::success();
  57. if (Addr >= FreeBlocks.size()) {
  58. if (!IsGrowable)
  59. return make_error<MSFError>(msf_error_code::insufficient_buffer,
  60. "Cannot grow the number of blocks");
  61. FreeBlocks.resize(Addr + 1, true);
  62. }
  63. if (!isBlockFree(Addr))
  64. return make_error<MSFError>(
  65. msf_error_code::block_in_use,
  66. "Requested block map address is already in use");
  67. FreeBlocks[BlockMapAddr] = true;
  68. FreeBlocks[Addr] = false;
  69. BlockMapAddr = Addr;
  70. return Error::success();
  71. }
  72. void MSFBuilder::setFreePageMap(uint32_t Fpm) { FreePageMap = Fpm; }
  73. void MSFBuilder::setUnknown1(uint32_t Unk1) { Unknown1 = Unk1; }
  74. Error MSFBuilder::setDirectoryBlocksHint(ArrayRef<uint32_t> DirBlocks) {
  75. for (auto B : DirectoryBlocks)
  76. FreeBlocks[B] = true;
  77. for (auto B : DirBlocks) {
  78. if (!isBlockFree(B)) {
  79. return make_error<MSFError>(msf_error_code::unspecified,
  80. "Attempt to reuse an allocated block");
  81. }
  82. FreeBlocks[B] = false;
  83. }
  84. DirectoryBlocks = DirBlocks;
  85. return Error::success();
  86. }
  87. Error MSFBuilder::allocateBlocks(uint32_t NumBlocks,
  88. MutableArrayRef<uint32_t> Blocks) {
  89. if (NumBlocks == 0)
  90. return Error::success();
  91. uint32_t NumFreeBlocks = FreeBlocks.count();
  92. if (NumFreeBlocks < NumBlocks) {
  93. if (!IsGrowable)
  94. return make_error<MSFError>(msf_error_code::insufficient_buffer,
  95. "There are no free Blocks in the file");
  96. uint32_t AllocBlocks = NumBlocks - NumFreeBlocks;
  97. uint32_t OldBlockCount = FreeBlocks.size();
  98. uint32_t NewBlockCount = AllocBlocks + OldBlockCount;
  99. uint32_t NextFpmBlock = alignTo(OldBlockCount, BlockSize) + 1;
  100. FreeBlocks.resize(NewBlockCount, true);
  101. // If we crossed over an fpm page, we actually need to allocate 2 extra
  102. // blocks for each FPM group crossed and mark both blocks from the group as
  103. // used. FPM blocks are marked as allocated regardless of whether or not
  104. // they ultimately describe the status of blocks in the file. This means
  105. // that not only are extraneous blocks at the end of the main FPM marked as
  106. // allocated, but also blocks from the alternate FPM are always marked as
  107. // allocated.
  108. while (NextFpmBlock < NewBlockCount) {
  109. NewBlockCount += 2;
  110. FreeBlocks.resize(NewBlockCount, true);
  111. FreeBlocks.reset(NextFpmBlock, NextFpmBlock + 2);
  112. NextFpmBlock += BlockSize;
  113. }
  114. }
  115. int I = 0;
  116. int Block = FreeBlocks.find_first();
  117. do {
  118. assert(Block != -1 && "We ran out of Blocks!");
  119. uint32_t NextBlock = static_cast<uint32_t>(Block);
  120. Blocks[I++] = NextBlock;
  121. FreeBlocks.reset(NextBlock);
  122. Block = FreeBlocks.find_next(Block);
  123. } while (--NumBlocks > 0);
  124. return Error::success();
  125. }
  126. uint32_t MSFBuilder::getNumUsedBlocks() const {
  127. return getTotalBlockCount() - getNumFreeBlocks();
  128. }
  129. uint32_t MSFBuilder::getNumFreeBlocks() const { return FreeBlocks.count(); }
  130. uint32_t MSFBuilder::getTotalBlockCount() const { return FreeBlocks.size(); }
  131. bool MSFBuilder::isBlockFree(uint32_t Idx) const { return FreeBlocks[Idx]; }
  132. Expected<uint32_t> MSFBuilder::addStream(uint32_t Size,
  133. ArrayRef<uint32_t> Blocks) {
  134. // Add a new stream mapped to the specified blocks. Verify that the specified
  135. // blocks are both necessary and sufficient for holding the requested number
  136. // of bytes, and verify that all requested blocks are free.
  137. uint32_t ReqBlocks = bytesToBlocks(Size, BlockSize);
  138. if (ReqBlocks != Blocks.size())
  139. return make_error<MSFError>(
  140. msf_error_code::invalid_format,
  141. "Incorrect number of blocks for requested stream size");
  142. for (auto Block : Blocks) {
  143. if (Block >= FreeBlocks.size())
  144. FreeBlocks.resize(Block + 1, true);
  145. if (!FreeBlocks.test(Block))
  146. return make_error<MSFError>(
  147. msf_error_code::unspecified,
  148. "Attempt to re-use an already allocated block");
  149. }
  150. // Mark all the blocks occupied by the new stream as not free.
  151. for (auto Block : Blocks) {
  152. FreeBlocks.reset(Block);
  153. }
  154. StreamData.push_back(std::make_pair(Size, Blocks));
  155. return StreamData.size() - 1;
  156. }
  157. Expected<uint32_t> MSFBuilder::addStream(uint32_t Size) {
  158. uint32_t ReqBlocks = bytesToBlocks(Size, BlockSize);
  159. std::vector<uint32_t> NewBlocks;
  160. NewBlocks.resize(ReqBlocks);
  161. if (auto EC = allocateBlocks(ReqBlocks, NewBlocks))
  162. return std::move(EC);
  163. StreamData.push_back(std::make_pair(Size, NewBlocks));
  164. return StreamData.size() - 1;
  165. }
  166. Error MSFBuilder::setStreamSize(uint32_t Idx, uint32_t Size) {
  167. uint32_t OldSize = getStreamSize(Idx);
  168. if (OldSize == Size)
  169. return Error::success();
  170. uint32_t NewBlocks = bytesToBlocks(Size, BlockSize);
  171. uint32_t OldBlocks = bytesToBlocks(OldSize, BlockSize);
  172. if (NewBlocks > OldBlocks) {
  173. uint32_t AddedBlocks = NewBlocks - OldBlocks;
  174. // If we're growing, we have to allocate new Blocks.
  175. std::vector<uint32_t> AddedBlockList;
  176. AddedBlockList.resize(AddedBlocks);
  177. if (auto EC = allocateBlocks(AddedBlocks, AddedBlockList))
  178. return EC;
  179. auto &CurrentBlocks = StreamData[Idx].second;
  180. llvm::append_range(CurrentBlocks, AddedBlockList);
  181. } else if (OldBlocks > NewBlocks) {
  182. // For shrinking, free all the Blocks in the Block map, update the stream
  183. // data, then shrink the directory.
  184. uint32_t RemovedBlocks = OldBlocks - NewBlocks;
  185. auto CurrentBlocks = ArrayRef<uint32_t>(StreamData[Idx].second);
  186. auto RemovedBlockList = CurrentBlocks.drop_front(NewBlocks);
  187. for (auto P : RemovedBlockList)
  188. FreeBlocks[P] = true;
  189. StreamData[Idx].second = CurrentBlocks.drop_back(RemovedBlocks);
  190. }
  191. StreamData[Idx].first = Size;
  192. return Error::success();
  193. }
  194. uint32_t MSFBuilder::getNumStreams() const { return StreamData.size(); }
  195. uint32_t MSFBuilder::getStreamSize(uint32_t StreamIdx) const {
  196. return StreamData[StreamIdx].first;
  197. }
  198. ArrayRef<uint32_t> MSFBuilder::getStreamBlocks(uint32_t StreamIdx) const {
  199. return StreamData[StreamIdx].second;
  200. }
  201. uint32_t MSFBuilder::computeDirectoryByteSize() const {
  202. // The directory has the following layout, where each item is a ulittle32_t:
  203. // NumStreams
  204. // StreamSizes[NumStreams]
  205. // StreamBlocks[NumStreams][]
  206. uint32_t Size = sizeof(ulittle32_t); // NumStreams
  207. Size += StreamData.size() * sizeof(ulittle32_t); // StreamSizes
  208. for (const auto &D : StreamData) {
  209. uint32_t ExpectedNumBlocks = bytesToBlocks(D.first, BlockSize);
  210. assert(ExpectedNumBlocks == D.second.size() &&
  211. "Unexpected number of blocks");
  212. Size += ExpectedNumBlocks * sizeof(ulittle32_t);
  213. }
  214. return Size;
  215. }
  216. Expected<MSFLayout> MSFBuilder::generateLayout() {
  217. SuperBlock *SB = Allocator.Allocate<SuperBlock>();
  218. MSFLayout L;
  219. L.SB = SB;
  220. std::memcpy(SB->MagicBytes, Magic, sizeof(Magic));
  221. SB->BlockMapAddr = BlockMapAddr;
  222. SB->BlockSize = BlockSize;
  223. SB->NumDirectoryBytes = computeDirectoryByteSize();
  224. SB->FreeBlockMapBlock = FreePageMap;
  225. SB->Unknown1 = Unknown1;
  226. uint32_t NumDirectoryBlocks = bytesToBlocks(SB->NumDirectoryBytes, BlockSize);
  227. if (NumDirectoryBlocks > DirectoryBlocks.size()) {
  228. // Our hint wasn't enough to satisfy the entire directory. Allocate
  229. // remaining pages.
  230. std::vector<uint32_t> ExtraBlocks;
  231. uint32_t NumExtraBlocks = NumDirectoryBlocks - DirectoryBlocks.size();
  232. ExtraBlocks.resize(NumExtraBlocks);
  233. if (auto EC = allocateBlocks(NumExtraBlocks, ExtraBlocks))
  234. return std::move(EC);
  235. llvm::append_range(DirectoryBlocks, ExtraBlocks);
  236. } else if (NumDirectoryBlocks < DirectoryBlocks.size()) {
  237. uint32_t NumUnnecessaryBlocks = DirectoryBlocks.size() - NumDirectoryBlocks;
  238. for (auto B :
  239. ArrayRef<uint32_t>(DirectoryBlocks).drop_back(NumUnnecessaryBlocks))
  240. FreeBlocks[B] = true;
  241. DirectoryBlocks.resize(NumDirectoryBlocks);
  242. }
  243. // Don't set the number of blocks in the file until after allocating Blocks
  244. // for the directory, since the allocation might cause the file to need to
  245. // grow.
  246. SB->NumBlocks = FreeBlocks.size();
  247. ulittle32_t *DirBlocks = Allocator.Allocate<ulittle32_t>(NumDirectoryBlocks);
  248. std::uninitialized_copy_n(DirectoryBlocks.begin(), NumDirectoryBlocks,
  249. DirBlocks);
  250. L.DirectoryBlocks = ArrayRef<ulittle32_t>(DirBlocks, NumDirectoryBlocks);
  251. // The stream sizes should be re-allocated as a stable pointer and the stream
  252. // map should have each of its entries allocated as a separate stable pointer.
  253. if (!StreamData.empty()) {
  254. ulittle32_t *Sizes = Allocator.Allocate<ulittle32_t>(StreamData.size());
  255. L.StreamSizes = ArrayRef<ulittle32_t>(Sizes, StreamData.size());
  256. L.StreamMap.resize(StreamData.size());
  257. for (uint32_t I = 0; I < StreamData.size(); ++I) {
  258. Sizes[I] = StreamData[I].first;
  259. ulittle32_t *BlockList =
  260. Allocator.Allocate<ulittle32_t>(StreamData[I].second.size());
  261. std::uninitialized_copy_n(StreamData[I].second.begin(),
  262. StreamData[I].second.size(), BlockList);
  263. L.StreamMap[I] =
  264. ArrayRef<ulittle32_t>(BlockList, StreamData[I].second.size());
  265. }
  266. }
  267. L.FreePageMap = FreeBlocks;
  268. return L;
  269. }
  270. static void commitFpm(WritableBinaryStream &MsfBuffer, const MSFLayout &Layout,
  271. BumpPtrAllocator &Allocator) {
  272. auto FpmStream =
  273. WritableMappedBlockStream::createFpmStream(Layout, MsfBuffer, Allocator);
  274. // We only need to create the alt fpm stream so that it gets initialized.
  275. WritableMappedBlockStream::createFpmStream(Layout, MsfBuffer, Allocator,
  276. true);
  277. uint32_t BI = 0;
  278. BinaryStreamWriter FpmWriter(*FpmStream);
  279. while (BI < Layout.SB->NumBlocks) {
  280. uint8_t ThisByte = 0;
  281. for (uint32_t I = 0; I < 8; ++I) {
  282. bool IsFree =
  283. (BI < Layout.SB->NumBlocks) ? Layout.FreePageMap.test(BI) : true;
  284. uint8_t Mask = uint8_t(IsFree) << I;
  285. ThisByte |= Mask;
  286. ++BI;
  287. }
  288. cantFail(FpmWriter.writeObject(ThisByte));
  289. }
  290. assert(FpmWriter.bytesRemaining() == 0);
  291. }
  292. Expected<FileBufferByteStream> MSFBuilder::commit(StringRef Path,
  293. MSFLayout &Layout) {
  294. Expected<MSFLayout> L = generateLayout();
  295. if (!L)
  296. return L.takeError();
  297. Layout = std::move(*L);
  298. uint64_t FileSize = uint64_t(Layout.SB->BlockSize) * Layout.SB->NumBlocks;
  299. // Ensure that the file size is under the limit for the specified block size.
  300. if (FileSize > getMaxFileSizeFromBlockSize(Layout.SB->BlockSize)) {
  301. msf_error_code error_code = [](uint32_t BlockSize) {
  302. switch (BlockSize) {
  303. case 8192:
  304. return msf_error_code::size_overflow_8192;
  305. case 16384:
  306. return msf_error_code::size_overflow_16384;
  307. case 32768:
  308. return msf_error_code::size_overflow_32768;
  309. default:
  310. return msf_error_code::size_overflow_4096;
  311. }
  312. }(Layout.SB->BlockSize);
  313. return make_error<MSFError>(
  314. error_code,
  315. formatv("File size {0,1:N} too large for current PDB page size {1}",
  316. FileSize, Layout.SB->BlockSize));
  317. }
  318. auto OutFileOrError = FileOutputBuffer::create(Path, FileSize);
  319. if (auto EC = OutFileOrError.takeError())
  320. return std::move(EC);
  321. FileBufferByteStream Buffer(std::move(*OutFileOrError),
  322. llvm::support::little);
  323. BinaryStreamWriter Writer(Buffer);
  324. if (auto EC = Writer.writeObject(*Layout.SB))
  325. return std::move(EC);
  326. commitFpm(Buffer, Layout, Allocator);
  327. uint32_t BlockMapOffset =
  328. msf::blockToOffset(Layout.SB->BlockMapAddr, Layout.SB->BlockSize);
  329. Writer.setOffset(BlockMapOffset);
  330. if (auto EC = Writer.writeArray(Layout.DirectoryBlocks))
  331. return std::move(EC);
  332. auto DirStream = WritableMappedBlockStream::createDirectoryStream(
  333. Layout, Buffer, Allocator);
  334. BinaryStreamWriter DW(*DirStream);
  335. if (auto EC = DW.writeInteger<uint32_t>(Layout.StreamSizes.size()))
  336. return std::move(EC);
  337. if (auto EC = DW.writeArray(Layout.StreamSizes))
  338. return std::move(EC);
  339. for (const auto &Blocks : Layout.StreamMap) {
  340. if (auto EC = DW.writeArray(Blocks))
  341. return std::move(EC);
  342. }
  343. return std::move(Buffer);
  344. }