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