1 //===- TpiStreamBuilder.cpp - -------------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" 11 #include "llvm/ADT/ArrayRef.h" 12 #include "llvm/ADT/STLExtras.h" 13 #include "llvm/DebugInfo/CodeView/TypeIndex.h" 14 #include "llvm/DebugInfo/CodeView/TypeRecord.h" 15 #include "llvm/DebugInfo/MSF/MSFBuilder.h" 16 #include "llvm/DebugInfo/MSF/MappedBlockStream.h" 17 #include "llvm/DebugInfo/PDB/Native/PDBFile.h" 18 #include "llvm/DebugInfo/PDB/Native/RawError.h" 19 #include "llvm/DebugInfo/PDB/Native/RawTypes.h" 20 #include "llvm/Support/Allocator.h" 21 #include "llvm/Support/BinaryByteStream.h" 22 #include "llvm/Support/BinaryStreamArray.h" 23 #include "llvm/Support/BinaryStreamReader.h" 24 #include "llvm/Support/BinaryStreamWriter.h" 25 #include "llvm/Support/Endian.h" 26 #include "llvm/Support/Error.h" 27 #include <algorithm> 28 #include <cstdint> 29 30 using namespace llvm; 31 using namespace llvm::msf; 32 using namespace llvm::pdb; 33 using namespace llvm::support; 34 35 TpiStreamBuilder::TpiStreamBuilder(MSFBuilder &Msf, uint32_t StreamIdx) 36 : Msf(Msf), Allocator(Msf.getAllocator()), Header(nullptr), Idx(StreamIdx) { 37 } 38 39 TpiStreamBuilder::~TpiStreamBuilder() = default; 40 41 void TpiStreamBuilder::setVersionHeader(PdbRaw_TpiVer Version) { 42 VerHeader = Version; 43 } 44 45 void TpiStreamBuilder::addTypeRecord(ArrayRef<uint8_t> Record, 46 Optional<uint32_t> Hash) { 47 // If we just crossed an 8KB threshold, add a type index offset. 48 size_t NewSize = TypeRecordBytes + Record.size(); 49 constexpr size_t EightKB = 8 * 1024; 50 if (NewSize / EightKB > TypeRecordBytes / EightKB || TypeRecords.empty()) { 51 TypeIndexOffsets.push_back( 52 {codeview::TypeIndex(codeview::TypeIndex::FirstNonSimpleIndex + 53 TypeRecords.size()), 54 ulittle32_t(TypeRecordBytes)}); 55 } 56 TypeRecordBytes = NewSize; 57 58 TypeRecords.push_back(Record); 59 if (Hash) 60 TypeHashes.push_back(*Hash); 61 } 62 63 Error TpiStreamBuilder::finalize() { 64 if (Header) 65 return Error::success(); 66 67 TpiStreamHeader *H = Allocator.Allocate<TpiStreamHeader>(); 68 69 uint32_t Count = TypeRecords.size(); 70 71 H->Version = VerHeader; 72 H->HeaderSize = sizeof(TpiStreamHeader); 73 H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex; 74 H->TypeIndexEnd = H->TypeIndexBegin + Count; 75 H->TypeRecordBytes = TypeRecordBytes; 76 77 H->HashStreamIndex = HashStreamIndex; 78 H->HashAuxStreamIndex = kInvalidStreamIndex; 79 H->HashKeySize = sizeof(ulittle32_t); 80 H->NumHashBuckets = MinTpiHashBuckets; 81 82 // Recall that hash values go into a completely different stream identified by 83 // the `HashStreamIndex` field of the `TpiStreamHeader`. Therefore, the data 84 // begins at offset 0 of this independent stream. 85 H->HashValueBuffer.Off = 0; 86 H->HashValueBuffer.Length = calculateHashBufferSize(); 87 88 // We never write any adjustments into our PDBs, so this is usually some 89 // offset with zero length. 90 H->HashAdjBuffer.Off = H->HashValueBuffer.Off + H->HashValueBuffer.Length; 91 H->HashAdjBuffer.Length = 0; 92 93 H->IndexOffsetBuffer.Off = H->HashAdjBuffer.Off + H->HashAdjBuffer.Length; 94 H->IndexOffsetBuffer.Length = calculateIndexOffsetSize(); 95 96 Header = H; 97 return Error::success(); 98 } 99 100 uint32_t TpiStreamBuilder::calculateSerializedLength() { 101 return sizeof(TpiStreamHeader) + TypeRecordBytes; 102 } 103 104 uint32_t TpiStreamBuilder::calculateHashBufferSize() const { 105 assert((TypeRecords.size() == TypeHashes.size() || TypeHashes.empty()) && 106 "either all or no type records should have hashes"); 107 return TypeHashes.size() * sizeof(ulittle32_t); 108 } 109 110 uint32_t TpiStreamBuilder::calculateIndexOffsetSize() const { 111 return TypeIndexOffsets.size() * sizeof(codeview::TypeIndexOffset); 112 } 113 114 Error TpiStreamBuilder::finalizeMsfLayout() { 115 uint32_t Length = calculateSerializedLength(); 116 if (auto EC = Msf.setStreamSize(Idx, Length)) 117 return EC; 118 119 uint32_t HashStreamSize = 120 calculateHashBufferSize() + calculateIndexOffsetSize(); 121 122 if (HashStreamSize == 0) 123 return Error::success(); 124 125 auto ExpectedIndex = Msf.addStream(HashStreamSize); 126 if (!ExpectedIndex) 127 return ExpectedIndex.takeError(); 128 HashStreamIndex = *ExpectedIndex; 129 if (!TypeHashes.empty()) { 130 ulittle32_t *H = Allocator.Allocate<ulittle32_t>(TypeHashes.size()); 131 MutableArrayRef<ulittle32_t> HashBuffer(H, TypeHashes.size()); 132 for (uint32_t I = 0; I < TypeHashes.size(); ++I) { 133 HashBuffer[I] = TypeHashes[I] % MinTpiHashBuckets; 134 } 135 ArrayRef<uint8_t> Bytes( 136 reinterpret_cast<const uint8_t *>(HashBuffer.data()), 137 calculateHashBufferSize()); 138 HashValueStream = 139 llvm::make_unique<BinaryByteStream>(Bytes, llvm::support::little); 140 } 141 return Error::success(); 142 } 143 144 Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout, 145 WritableBinaryStreamRef Buffer) { 146 if (auto EC = finalize()) 147 return EC; 148 149 auto InfoS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer, 150 Idx, Allocator); 151 152 BinaryStreamWriter Writer(*InfoS); 153 if (auto EC = Writer.writeObject(*Header)) 154 return EC; 155 156 for (auto Rec : TypeRecords) 157 if (auto EC = Writer.writeBytes(Rec)) 158 return EC; 159 160 if (HashStreamIndex != kInvalidStreamIndex) { 161 auto HVS = WritableMappedBlockStream::createIndexedStream( 162 Layout, Buffer, HashStreamIndex, Allocator); 163 BinaryStreamWriter HW(*HVS); 164 if (HashValueStream) { 165 if (auto EC = HW.writeStreamRef(*HashValueStream)) 166 return EC; 167 } 168 169 for (auto &IndexOffset : TypeIndexOffsets) { 170 if (auto EC = HW.writeObject(IndexOffset)) 171 return EC; 172 } 173 } 174 175 return Error::success(); 176 } 177