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