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/TypeDeserializer.h"
15 #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
16 #include "llvm/DebugInfo/CodeView/TypeRecord.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 
37 TpiStream::TpiStream(PDBFile &File, std::unique_ptr<MappedBlockStream> Stream)
38     : Pdb(File), Stream(std::move(Stream)) {}
39 
40 TpiStream::~TpiStream() = default;
41 
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 
122 PdbRaw_TpiVer TpiStream::getTpiVersion() const {
123   uint32_t Value = Header->Version;
124   return static_cast<PdbRaw_TpiVer>(Value);
125 }
126 
127 uint32_t TpiStream::TypeIndexBegin() const { return Header->TypeIndexBegin; }
128 
129 uint32_t TpiStream::TypeIndexEnd() const { return Header->TypeIndexEnd; }
130 
131 uint32_t TpiStream::getNumTypeRecords() const {
132   return TypeIndexEnd() - TypeIndexBegin();
133 }
134 
135 uint16_t TpiStream::getTypeHashStreamIndex() const {
136   return Header->HashStreamIndex;
137 }
138 
139 uint16_t TpiStream::getTypeHashStreamAuxIndex() const {
140   return Header->HashAuxStreamIndex;
141 }
142 
143 uint32_t TpiStream::getNumHashBuckets() const { return Header->NumHashBuckets; }
144 uint32_t TpiStream::getHashKeySize() const { return Header->HashKeySize; }
145 
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 
162 bool TpiStream::supportsTypeLookup() const { return !HashMap.empty(); }
163 
164 template <typename RecordT> static ClassOptions getUdtOptions(CVType CVT) {
165   RecordT Record;
166   if (auto EC = TypeDeserializer::deserializeAs<RecordT>(CVT, Record)) {
167     consumeError(std::move(EC));
168     return ClassOptions::None;
169   }
170   return Record.getOptions();
171 }
172 
173 static bool isUdtForwardRef(CVType CVT) {
174   ClassOptions UdtOptions = ClassOptions::None;
175   switch (CVT.kind()) {
176   case LF_STRUCTURE:
177   case LF_CLASS:
178   case LF_INTERFACE:
179     UdtOptions = getUdtOptions<ClassRecord>(std::move(CVT));
180     break;
181   case LF_ENUM:
182     UdtOptions = getUdtOptions<EnumRecord>(std::move(CVT));
183     break;
184   case LF_UNION:
185     UdtOptions = getUdtOptions<UnionRecord>(std::move(CVT));
186     break;
187   default:
188     return false;
189   }
190   return (UdtOptions & ClassOptions::ForwardReference) != ClassOptions::None;
191 }
192 
193 Expected<TypeIndex>
194 TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const {
195   CVType F = Types->getType(ForwardRefTI);
196   if (!isUdtForwardRef(F))
197     return ForwardRefTI;
198 
199   Expected<TagRecordHash> ForwardTRH = hashTagRecord(F);
200   if (!ForwardTRH)
201     return ForwardTRH.takeError();
202 
203   uint32_t BucketIdx = ForwardTRH->FullRecordHash % Header->NumHashBuckets;
204 
205   for (TypeIndex TI : HashMap[BucketIdx]) {
206     CVType CVT = Types->getType(TI);
207     if (CVT.kind() != F.kind())
208       continue;
209 
210     Expected<TagRecordHash> FullTRH = hashTagRecord(CVT);
211     if (!FullTRH)
212       return FullTRH.takeError();
213     if (ForwardTRH->FullRecordHash != FullTRH->FullRecordHash)
214       continue;
215     TagRecord &ForwardTR = ForwardTRH->getRecord();
216     TagRecord &FullTR = FullTRH->getRecord();
217 
218     if (!ForwardTR.hasUniqueName()) {
219       if (ForwardTR.getName() == FullTR.getName())
220         return TI;
221       continue;
222     }
223 
224     if (!FullTR.hasUniqueName())
225       continue;
226     if (ForwardTR.getUniqueName() == FullTR.getUniqueName())
227       return TI;
228   }
229   return ForwardRefTI;
230 }
231 
232 BinarySubstreamRef TpiStream::getTypeRecordsSubstream() const {
233   return TypeRecordsSubstream;
234 }
235 
236 FixedStreamArray<support::ulittle32_t> TpiStream::getHashValues() const {
237   return HashValues;
238 }
239 
240 FixedStreamArray<TypeIndexOffset> TpiStream::getTypeIndexOffsets() const {
241   return TypeIndexOffsets;
242 }
243 
244 HashTable<support::ulittle32_t> &TpiStream::getHashAdjusters() {
245   return HashAdjusters;
246 }
247 
248 CVTypeRange TpiStream::types(bool *HadError) const {
249   return make_range(TypeRecords.begin(HadError), TypeRecords.end());
250 }
251 
252 Error TpiStream::commit() { return Error::success(); }
253