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