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 assert(((Record.size() & 3) == 0) && 48 "The type record's size is not a multiple of 4 bytes which will " 49 "cause misalignment in the output TPI stream!"); 50 size_t NewSize = TypeRecordBytes + Record.size(); 51 constexpr size_t EightKB = 8 * 1024; 52 if (NewSize / EightKB > TypeRecordBytes / EightKB || TypeRecords.empty()) { 53 TypeIndexOffsets.push_back( 54 {codeview::TypeIndex(codeview::TypeIndex::FirstNonSimpleIndex + 55 TypeRecords.size()), 56 ulittle32_t(TypeRecordBytes)}); 57 } 58 TypeRecordBytes = NewSize; 59 60 TypeRecords.push_back(Record); 61 if (Hash) 62 TypeHashes.push_back(*Hash); 63 } 64 65 Error TpiStreamBuilder::finalize() { 66 if (Header) 67 return Error::success(); 68 69 TpiStreamHeader *H = Allocator.Allocate<TpiStreamHeader>(); 70 71 uint32_t Count = TypeRecords.size(); 72 73 H->Version = VerHeader; 74 H->HeaderSize = sizeof(TpiStreamHeader); 75 H->TypeIndexBegin = codeview::TypeIndex::FirstNonSimpleIndex; 76 H->TypeIndexEnd = H->TypeIndexBegin + Count; 77 H->TypeRecordBytes = TypeRecordBytes; 78 79 H->HashStreamIndex = HashStreamIndex; 80 H->HashAuxStreamIndex = kInvalidStreamIndex; 81 H->HashKeySize = sizeof(ulittle32_t); 82 H->NumHashBuckets = MaxTpiHashBuckets - 1; 83 84 // Recall that hash values go into a completely different stream identified by 85 // the `HashStreamIndex` field of the `TpiStreamHeader`. Therefore, the data 86 // begins at offset 0 of this independent stream. 87 H->HashValueBuffer.Off = 0; 88 H->HashValueBuffer.Length = calculateHashBufferSize(); 89 90 // We never write any adjustments into our PDBs, so this is usually some 91 // offset with zero length. 92 H->HashAdjBuffer.Off = H->HashValueBuffer.Off + H->HashValueBuffer.Length; 93 H->HashAdjBuffer.Length = 0; 94 95 H->IndexOffsetBuffer.Off = H->HashAdjBuffer.Off + H->HashAdjBuffer.Length; 96 H->IndexOffsetBuffer.Length = calculateIndexOffsetSize(); 97 98 Header = H; 99 return Error::success(); 100 } 101 102 uint32_t TpiStreamBuilder::calculateSerializedLength() { 103 return sizeof(TpiStreamHeader) + TypeRecordBytes; 104 } 105 106 uint32_t TpiStreamBuilder::calculateHashBufferSize() const { 107 assert((TypeRecords.size() == TypeHashes.size() || TypeHashes.empty()) && 108 "either all or no type records should have hashes"); 109 return TypeHashes.size() * sizeof(ulittle32_t); 110 } 111 112 uint32_t TpiStreamBuilder::calculateIndexOffsetSize() const { 113 return TypeIndexOffsets.size() * sizeof(codeview::TypeIndexOffset); 114 } 115 116 Error TpiStreamBuilder::finalizeMsfLayout() { 117 uint32_t Length = calculateSerializedLength(); 118 if (auto EC = Msf.setStreamSize(Idx, Length)) 119 return EC; 120 121 uint32_t HashStreamSize = 122 calculateHashBufferSize() + calculateIndexOffsetSize(); 123 124 if (HashStreamSize == 0) 125 return Error::success(); 126 127 auto ExpectedIndex = Msf.addStream(HashStreamSize); 128 if (!ExpectedIndex) 129 return ExpectedIndex.takeError(); 130 HashStreamIndex = *ExpectedIndex; 131 if (!TypeHashes.empty()) { 132 ulittle32_t *H = Allocator.Allocate<ulittle32_t>(TypeHashes.size()); 133 MutableArrayRef<ulittle32_t> HashBuffer(H, TypeHashes.size()); 134 for (uint32_t I = 0; I < TypeHashes.size(); ++I) { 135 HashBuffer[I] = TypeHashes[I] % (MaxTpiHashBuckets - 1); 136 } 137 ArrayRef<uint8_t> Bytes( 138 reinterpret_cast<const uint8_t *>(HashBuffer.data()), 139 calculateHashBufferSize()); 140 HashValueStream = 141 std::make_unique<BinaryByteStream>(Bytes, llvm::support::little); 142 } 143 return Error::success(); 144 } 145 146 Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout, 147 WritableBinaryStreamRef Buffer) { 148 if (auto EC = finalize()) 149 return EC; 150 151 auto InfoS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer, 152 Idx, Allocator); 153 154 BinaryStreamWriter Writer(*InfoS); 155 if (auto EC = Writer.writeObject(*Header)) 156 return EC; 157 158 for (auto Rec : TypeRecords) { 159 assert(!Rec.empty() && "Attempting to write an empty type record shifts " 160 "all offsets in the TPI stream!"); 161 assert(((Rec.size() & 3) == 0) && 162 "The type record's size is not a multiple of 4 bytes which will " 163 "cause misalignment in the output TPI stream!"); 164 if (auto EC = Writer.writeBytes(Rec)) 165 return EC; 166 } 167 168 if (HashStreamIndex != kInvalidStreamIndex) { 169 auto HVS = WritableMappedBlockStream::createIndexedStream( 170 Layout, Buffer, HashStreamIndex, Allocator); 171 BinaryStreamWriter HW(*HVS); 172 if (HashValueStream) { 173 if (auto EC = HW.writeStreamRef(*HashValueStream)) 174 return EC; 175 } 176 177 for (auto &IndexOffset : TypeIndexOffsets) { 178 if (auto EC = HW.writeObject(IndexOffset)) 179 return EC; 180 } 181 } 182 183 return Error::success(); 184 } 185