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