1 //===- TpiStream.cpp - PDB Type Info (TPI) Stream 2 Access ----------------===// 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/TpiStream.h" 10 11 #include "llvm/ADT/iterator_range.h" 12 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" 13 #include "llvm/DebugInfo/CodeView/RecordName.h" 14 #include "llvm/DebugInfo/CodeView/TypeRecord.h" 15 #include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h" 16 #include "llvm/DebugInfo/MSF/MappedBlockStream.h" 17 #include "llvm/DebugInfo/PDB/Native/Hash.h" 18 #include "llvm/DebugInfo/PDB/Native/PDBFile.h" 19 #include "llvm/DebugInfo/PDB/Native/RawConstants.h" 20 #include "llvm/DebugInfo/PDB/Native/RawError.h" 21 #include "llvm/DebugInfo/PDB/Native/RawTypes.h" 22 #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" 23 #include "llvm/Support/BinaryStreamReader.h" 24 #include "llvm/Support/Endian.h" 25 #include "llvm/Support/Error.h" 26 #include <cstdint> 27 #include <vector> 28 29 using namespace llvm; 30 using namespace llvm::codeview; 31 using namespace llvm::support; 32 using namespace llvm::msf; 33 using namespace llvm::pdb; 34 35 TpiStream::TpiStream(PDBFile &File, std::unique_ptr<MappedBlockStream> Stream) 36 : Pdb(File), Stream(std::move(Stream)) {} 37 38 TpiStream::~TpiStream() = default; 39 40 Error TpiStream::reload() { 41 BinaryStreamReader Reader(*Stream); 42 43 if (Reader.bytesRemaining() < sizeof(TpiStreamHeader)) 44 return make_error<RawError>(raw_error_code::corrupt_file, 45 "TPI Stream does not contain a header."); 46 47 if (Reader.readObject(Header)) 48 return make_error<RawError>(raw_error_code::corrupt_file, 49 "TPI Stream does not contain a header."); 50 51 if (Header->Version != PdbTpiV80) 52 return make_error<RawError>(raw_error_code::corrupt_file, 53 "Unsupported TPI Version."); 54 55 if (Header->HeaderSize != sizeof(TpiStreamHeader)) 56 return make_error<RawError>(raw_error_code::corrupt_file, 57 "Corrupt TPI Header size."); 58 59 if (Header->HashKeySize != sizeof(ulittle32_t)) 60 return make_error<RawError>(raw_error_code::corrupt_file, 61 "TPI Stream expected 4 byte hash key size."); 62 63 if (Header->NumHashBuckets < MinTpiHashBuckets || 64 Header->NumHashBuckets > MaxTpiHashBuckets) 65 return make_error<RawError>(raw_error_code::corrupt_file, 66 "TPI Stream Invalid number of hash buckets."); 67 68 // The actual type records themselves come from this stream 69 if (auto EC = 70 Reader.readSubstream(TypeRecordsSubstream, Header->TypeRecordBytes)) 71 return EC; 72 73 BinaryStreamReader RecordReader(TypeRecordsSubstream.StreamData); 74 if (auto EC = 75 RecordReader.readArray(TypeRecords, TypeRecordsSubstream.size())) 76 return EC; 77 78 // Hash indices, hash values, etc come from the hash stream. 79 if (Header->HashStreamIndex != kInvalidStreamIndex) { 80 auto HS = Pdb.safelyCreateIndexedStream(Header->HashStreamIndex); 81 if (!HS) { 82 consumeError(HS.takeError()); 83 return make_error<RawError>(raw_error_code::corrupt_file, 84 "Invalid TPI hash stream index."); 85 } 86 BinaryStreamReader HSR(**HS); 87 88 // There should be a hash value for every type record, or no hashes at all. 89 uint32_t NumHashValues = 90 Header->HashValueBuffer.Length / sizeof(ulittle32_t); 91 if (NumHashValues != getNumTypeRecords() && NumHashValues != 0) 92 return make_error<RawError>( 93 raw_error_code::corrupt_file, 94 "TPI hash count does not match with the number of type records."); 95 HSR.setOffset(Header->HashValueBuffer.Off); 96 if (auto EC = HSR.readArray(HashValues, NumHashValues)) 97 return EC; 98 99 HSR.setOffset(Header->IndexOffsetBuffer.Off); 100 uint32_t NumTypeIndexOffsets = 101 Header->IndexOffsetBuffer.Length / sizeof(TypeIndexOffset); 102 if (auto EC = HSR.readArray(TypeIndexOffsets, NumTypeIndexOffsets)) 103 return EC; 104 105 if (Header->HashAdjBuffer.Length > 0) { 106 HSR.setOffset(Header->HashAdjBuffer.Off); 107 if (auto EC = HashAdjusters.load(HSR)) 108 return EC; 109 } 110 111 HashStream = std::move(*HS); 112 } 113 114 Types = std::make_unique<LazyRandomTypeCollection>( 115 TypeRecords, getNumTypeRecords(), getTypeIndexOffsets()); 116 return Error::success(); 117 } 118 119 PdbRaw_TpiVer TpiStream::getTpiVersion() const { 120 uint32_t Value = Header->Version; 121 return static_cast<PdbRaw_TpiVer>(Value); 122 } 123 124 uint32_t TpiStream::TypeIndexBegin() const { return Header->TypeIndexBegin; } 125 126 uint32_t TpiStream::TypeIndexEnd() const { return Header->TypeIndexEnd; } 127 128 uint32_t TpiStream::getNumTypeRecords() const { 129 return TypeIndexEnd() - TypeIndexBegin(); 130 } 131 132 uint16_t TpiStream::getTypeHashStreamIndex() const { 133 return Header->HashStreamIndex; 134 } 135 136 uint16_t TpiStream::getTypeHashStreamAuxIndex() const { 137 return Header->HashAuxStreamIndex; 138 } 139 140 uint32_t TpiStream::getNumHashBuckets() const { return Header->NumHashBuckets; } 141 uint32_t TpiStream::getHashKeySize() const { return Header->HashKeySize; } 142 143 void TpiStream::buildHashMap() { 144 if (!HashMap.empty()) 145 return; 146 if (HashValues.empty()) 147 return; 148 149 HashMap.resize(Header->NumHashBuckets); 150 151 TypeIndex TIB{Header->TypeIndexBegin}; 152 TypeIndex TIE{Header->TypeIndexEnd}; 153 while (TIB < TIE) { 154 uint32_t HV = HashValues[TIB.toArrayIndex()]; 155 HashMap[HV].push_back(TIB++); 156 } 157 } 158 159 std::vector<TypeIndex> TpiStream::findRecordsByName(StringRef Name) const { 160 if (!supportsTypeLookup()) 161 const_cast<TpiStream*>(this)->buildHashMap(); 162 163 uint32_t Bucket = hashStringV1(Name) % Header->NumHashBuckets; 164 if (Bucket > HashMap.size()) 165 return {}; 166 167 std::vector<TypeIndex> Result; 168 for (TypeIndex TI : HashMap[Bucket]) { 169 std::string ThisName = computeTypeName(*Types, TI); 170 if (ThisName == Name) 171 Result.push_back(TI); 172 } 173 return Result; 174 } 175 176 bool TpiStream::supportsTypeLookup() const { return !HashMap.empty(); } 177 178 Expected<TypeIndex> 179 TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const { 180 if (!supportsTypeLookup()) 181 const_cast<TpiStream*>(this)->buildHashMap(); 182 183 CVType F = Types->getType(ForwardRefTI); 184 if (!isUdtForwardRef(F)) 185 return ForwardRefTI; 186 187 Expected<TagRecordHash> ForwardTRH = hashTagRecord(F); 188 if (!ForwardTRH) 189 return ForwardTRH.takeError(); 190 191 uint32_t BucketIdx = ForwardTRH->FullRecordHash % Header->NumHashBuckets; 192 193 for (TypeIndex TI : HashMap[BucketIdx]) { 194 CVType CVT = Types->getType(TI); 195 if (CVT.kind() != F.kind()) 196 continue; 197 198 Expected<TagRecordHash> FullTRH = hashTagRecord(CVT); 199 if (!FullTRH) 200 return FullTRH.takeError(); 201 if (ForwardTRH->FullRecordHash != FullTRH->FullRecordHash) 202 continue; 203 TagRecord &ForwardTR = ForwardTRH->getRecord(); 204 TagRecord &FullTR = FullTRH->getRecord(); 205 206 if (!ForwardTR.hasUniqueName()) { 207 if (ForwardTR.getName() == FullTR.getName()) 208 return TI; 209 continue; 210 } 211 212 if (!FullTR.hasUniqueName()) 213 continue; 214 if (ForwardTR.getUniqueName() == FullTR.getUniqueName()) 215 return TI; 216 } 217 return ForwardRefTI; 218 } 219 220 codeview::CVType TpiStream::getType(codeview::TypeIndex Index) { 221 assert(!Index.isSimple()); 222 return Types->getType(Index); 223 } 224 225 BinarySubstreamRef TpiStream::getTypeRecordsSubstream() const { 226 return TypeRecordsSubstream; 227 } 228 229 FixedStreamArray<support::ulittle32_t> TpiStream::getHashValues() const { 230 return HashValues; 231 } 232 233 FixedStreamArray<TypeIndexOffset> TpiStream::getTypeIndexOffsets() const { 234 return TypeIndexOffsets; 235 } 236 237 HashTable<support::ulittle32_t> &TpiStream::getHashAdjusters() { 238 return HashAdjusters; 239 } 240 241 CVTypeRange TpiStream::types(bool *HadError) const { 242 return make_range(TypeRecords.begin(HadError), TypeRecords.end()); 243 } 244 245 Error TpiStream::commit() { return Error::success(); } 246